quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

quickjs.c (1831380B)


      1 /*
      2  * QuickJS Javascript Engine
      3  *
      4  * Copyright (c) 2017-2021 Fabrice Bellard
      5  * Copyright (c) 2017-2021 Charlie Gordon
      6  *
      7  * Permission is hereby granted, free of charge, to any person obtaining a copy
      8  * of this software and associated documentation files (the "Software"), to deal
      9  * in the Software without restriction, including without limitation the rights
     10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     11  * copies of the Software, and to permit persons to whom the Software is
     12  * furnished to do so, subject to the following conditions:
     13  *
     14  * The above copyright notice and this permission notice shall be included in
     15  * all copies or substantial portions of the Software.
     16  *
     17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     23  * THE SOFTWARE.
     24  */
     25 #include <stdlib.h>
     26 #include <stdio.h>
     27 #include <stdarg.h>
     28 #include <inttypes.h>
     29 #include <string.h>
     30 #include <assert.h>
     31 #include <sys/time.h>
     32 #include <time.h>
     33 #include <fenv.h>
     34 #include <math.h>
     35 #if defined(__APPLE__)
     36 #include <malloc/malloc.h>
     37 #elif defined(__linux__) || defined(__GLIBC__)
     38 #include <malloc.h>
     39 #elif defined(__FreeBSD__)
     40 #include <malloc_np.h>
     41 #endif
     42 
     43 #include "cutils.h"
     44 #include "list.h"
     45 #include "quickjs.h"
     46 #include "libregexp.h"
     47 #include "libunicode.h"
     48 #include "libbf.h"
     49 
     50 #define OPTIMIZE         1
     51 #define SHORT_OPCODES    1
     52 #if defined(EMSCRIPTEN)
     53 #define DIRECT_DISPATCH  0
     54 #else
     55 #define DIRECT_DISPATCH  1
     56 #endif
     57 
     58 #if defined(__APPLE__)
     59 #define MALLOC_OVERHEAD  0
     60 #else
     61 #define MALLOC_OVERHEAD  8
     62 #endif
     63 
     64 #if !defined(_WIN32)
     65 /* define it if printf uses the RNDN rounding mode instead of RNDNA */
     66 #define CONFIG_PRINTF_RNDN
     67 #endif
     68 
     69 /* define to include Atomics.* operations which depend on the OS
     70    threads */
     71 #if !defined(EMSCRIPTEN)
     72 #define CONFIG_ATOMICS
     73 #endif
     74 
     75 #if !defined(EMSCRIPTEN) && !defined(__APPLE__)
     76 /* enable stack limitation */
     77 #define CONFIG_STACK_CHECK
     78 #endif
     79 
     80 
     81 /* dump object free */
     82 //#define DUMP_FREE
     83 //#define DUMP_CLOSURE
     84 /* dump the bytecode of the compiled functions: combination of bits
     85    1: dump pass 3 final byte code
     86    2: dump pass 2 code
     87    4: dump pass 1 code
     88    8: dump stdlib functions
     89   16: dump bytecode in hex
     90   32: dump line number table
     91   64: dump compute_stack_size
     92  */
     93 //#define DUMP_BYTECODE  (1)
     94 /* dump the occurence of the automatic GC */
     95 //#define DUMP_GC
     96 /* dump objects freed by the garbage collector */
     97 //#define DUMP_GC_FREE
     98 /* dump objects leaking when freeing the runtime */
     99 //#define DUMP_LEAKS  1
    100 /* dump memory usage before running the garbage collector */
    101 //#define DUMP_MEM
    102 //#define DUMP_OBJECTS    /* dump objects in JS_FreeContext */
    103 //#define DUMP_ATOMS      /* dump atoms in JS_FreeContext */
    104 //#define DUMP_SHAPES     /* dump shapes in JS_FreeContext */
    105 //#define DUMP_MODULE_RESOLVE
    106 //#define DUMP_PROMISE
    107 //#define DUMP_READ_OBJECT
    108 
    109 /* test the GC by forcing it before each object allocation */
    110 //#define FORCE_GC_AT_MALLOC
    111 
    112 /* use function call trampolines for better output in profiling tools */
    113 //#define PERF_TRAMPOLINE
    114 
    115 #ifdef CONFIG_ATOMICS
    116 #include <pthread.h>
    117 #include <stdatomic.h>
    118 #include <errno.h>
    119 #endif
    120 
    121 enum {
    122     /* classid tag        */    /* union usage   | properties */
    123     JS_CLASS_OBJECT = 1,        /* must be first */
    124     JS_CLASS_ARRAY,             /* u.array       | length */
    125     JS_CLASS_ERROR,
    126     JS_CLASS_NUMBER,            /* u.object_data */
    127     JS_CLASS_STRING,            /* u.object_data */
    128     JS_CLASS_BOOLEAN,           /* u.object_data */
    129     JS_CLASS_SYMBOL,            /* u.object_data */
    130     JS_CLASS_ARGUMENTS,         /* u.array       | length */
    131     JS_CLASS_MAPPED_ARGUMENTS,  /*               | length */
    132     JS_CLASS_DATE,              /* u.object_data */
    133     JS_CLASS_MODULE_NS,
    134     JS_CLASS_C_FUNCTION,        /* u.cfunc */
    135     JS_CLASS_BYTECODE_FUNCTION, /* u.func */
    136     JS_CLASS_BOUND_FUNCTION,    /* u.bound_function */
    137     JS_CLASS_C_FUNCTION_DATA,   /* u.c_function_data_record */
    138     JS_CLASS_GENERATOR_FUNCTION, /* u.func */
    139     JS_CLASS_FOR_IN_ITERATOR,   /* u.for_in_iterator */
    140     JS_CLASS_REGEXP,            /* u.regexp */
    141     JS_CLASS_ARRAY_BUFFER,      /* u.array_buffer */
    142     JS_CLASS_SHARED_ARRAY_BUFFER, /* u.array_buffer */
    143     JS_CLASS_UINT8C_ARRAY,      /* u.array (typed_array) */
    144     JS_CLASS_INT8_ARRAY,        /* u.array (typed_array) */
    145     JS_CLASS_UINT8_ARRAY,       /* u.array (typed_array) */
    146     JS_CLASS_INT16_ARRAY,       /* u.array (typed_array) */
    147     JS_CLASS_UINT16_ARRAY,      /* u.array (typed_array) */
    148     JS_CLASS_INT32_ARRAY,       /* u.array (typed_array) */
    149     JS_CLASS_UINT32_ARRAY,      /* u.array (typed_array) */
    150     JS_CLASS_BIG_INT64_ARRAY,   /* u.array (typed_array) */
    151     JS_CLASS_BIG_UINT64_ARRAY,  /* u.array (typed_array) */
    152     JS_CLASS_FLOAT32_ARRAY,     /* u.array (typed_array) */
    153     JS_CLASS_FLOAT64_ARRAY,     /* u.array (typed_array) */
    154     JS_CLASS_DATAVIEW,          /* u.typed_array */
    155     JS_CLASS_BIG_INT,           /* u.object_data */
    156 #ifdef CONFIG_BIGNUM
    157     JS_CLASS_BIG_FLOAT,         /* u.object_data */
    158     JS_CLASS_FLOAT_ENV,         /* u.float_env */
    159     JS_CLASS_BIG_DECIMAL,       /* u.object_data */
    160     JS_CLASS_OPERATOR_SET,      /* u.operator_set */
    161 #endif
    162     JS_CLASS_MAP,               /* u.map_state */
    163     JS_CLASS_SET,               /* u.map_state */
    164     JS_CLASS_WEAKMAP,           /* u.map_state */
    165     JS_CLASS_WEAKSET,           /* u.map_state */
    166     JS_CLASS_MAP_ITERATOR,      /* u.map_iterator_data */
    167     JS_CLASS_SET_ITERATOR,      /* u.map_iterator_data */
    168     JS_CLASS_ARRAY_ITERATOR,    /* u.array_iterator_data */
    169     JS_CLASS_STRING_ITERATOR,   /* u.array_iterator_data */
    170     JS_CLASS_REGEXP_STRING_ITERATOR,   /* u.regexp_string_iterator_data */
    171     JS_CLASS_GENERATOR,         /* u.generator_data */
    172     JS_CLASS_PROXY,             /* u.proxy_data */
    173     JS_CLASS_PROMISE,           /* u.promise_data */
    174     JS_CLASS_PROMISE_RESOLVE_FUNCTION,  /* u.promise_function_data */
    175     JS_CLASS_PROMISE_REJECT_FUNCTION,   /* u.promise_function_data */
    176     JS_CLASS_ASYNC_FUNCTION,            /* u.func */
    177     JS_CLASS_ASYNC_FUNCTION_RESOLVE,    /* u.async_function_data */
    178     JS_CLASS_ASYNC_FUNCTION_REJECT,     /* u.async_function_data */
    179     JS_CLASS_ASYNC_FROM_SYNC_ITERATOR,  /* u.async_from_sync_iterator_data */
    180     JS_CLASS_ASYNC_GENERATOR_FUNCTION,  /* u.func */
    181     JS_CLASS_ASYNC_GENERATOR,   /* u.async_generator_data */
    182 
    183     JS_CLASS_INIT_COUNT, /* last entry for predefined classes */
    184 };
    185 
    186 /* number of typed array types */
    187 #define JS_TYPED_ARRAY_COUNT  (JS_CLASS_FLOAT64_ARRAY - JS_CLASS_UINT8C_ARRAY + 1)
    188 static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT];
    189 #define typed_array_size_log2(classid)  (typed_array_size_log2[(classid)- JS_CLASS_UINT8C_ARRAY])
    190 
    191 typedef enum JSErrorEnum {
    192     JS_EVAL_ERROR,
    193     JS_RANGE_ERROR,
    194     JS_REFERENCE_ERROR,
    195     JS_SYNTAX_ERROR,
    196     JS_TYPE_ERROR,
    197     JS_URI_ERROR,
    198     JS_INTERNAL_ERROR,
    199     JS_AGGREGATE_ERROR,
    200 
    201     JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */
    202 } JSErrorEnum;
    203 
    204 #define JS_MAX_LOCAL_VARS 65535
    205 #define JS_STACK_SIZE_MAX 65534
    206 #define JS_STRING_LEN_MAX ((1 << 30) - 1)
    207 
    208 #define __exception __attribute__((warn_unused_result))
    209 
    210 typedef struct JSShape JSShape;
    211 typedef struct JSString JSString;
    212 typedef struct JSString JSAtomStruct;
    213 
    214 typedef enum {
    215     JS_GC_PHASE_NONE,
    216     JS_GC_PHASE_DECREF,
    217     JS_GC_PHASE_REMOVE_CYCLES,
    218 } JSGCPhaseEnum;
    219 
    220 typedef enum OPCodeEnum OPCodeEnum;
    221 
    222 /* function pointers are used for numeric operations so that it is
    223    possible to remove some numeric types */
    224 typedef struct {
    225     JSValue (*to_string)(JSContext *ctx, JSValueConst val);
    226     JSValue (*from_string)(JSContext *ctx, const char *buf,
    227                            int radix, int flags, slimb_t *pexponent);
    228     int (*unary_arith)(JSContext *ctx,
    229                        JSValue *pres, OPCodeEnum op, JSValue op1);
    230     int (*binary_arith)(JSContext *ctx, OPCodeEnum op,
    231                         JSValue *pres, JSValue op1, JSValue op2);
    232     int (*compare)(JSContext *ctx, OPCodeEnum op,
    233                    JSValue op1, JSValue op2);
    234     /* only for bigfloat: */
    235     JSValue (*mul_pow10_to_float64)(JSContext *ctx, const bf_t *a,
    236                                     int64_t exponent);
    237     int (*mul_pow10)(JSContext *ctx, JSValue *sp);
    238 } JSNumericOperations;
    239 
    240 struct JSRuntime {
    241     JSMallocFunctions mf;
    242     JSMallocState malloc_state;
    243     const char *rt_info;
    244 
    245     int atom_hash_size; /* power of two */
    246     int atom_count;
    247     int atom_size;
    248     int atom_count_resize; /* resize hash table at this count */
    249     uint32_t *atom_hash;
    250     JSAtomStruct **atom_array;
    251     int atom_free_index; /* 0 = none */
    252 
    253     int class_count;    /* size of class_array */
    254     JSClass *class_array;
    255 
    256     struct list_head context_list; /* list of JSContext.link */
    257     /* list of JSGCObjectHeader.link. List of allocated GC objects (used
    258        by the garbage collector) */
    259     struct list_head gc_obj_list;
    260     /* list of JSGCObjectHeader.link. Used during JS_FreeValueRT() */
    261     struct list_head gc_zero_ref_count_list;
    262     struct list_head tmp_obj_list; /* used during GC */
    263     JSGCPhaseEnum gc_phase : 8;
    264     size_t malloc_gc_threshold;
    265 #ifdef DUMP_LEAKS
    266     struct list_head string_list; /* list of JSString.link */
    267 #endif
    268     /* stack limitation */
    269     uintptr_t stack_size; /* in bytes, 0 if no limit */
    270     uintptr_t stack_top;
    271     uintptr_t stack_limit; /* lower stack limit */
    272 
    273     JSValue current_exception;
    274     /* true if inside an out of memory error, to avoid recursing */
    275     BOOL in_out_of_memory : 8;
    276 
    277     struct JSStackFrame *current_stack_frame;
    278 
    279     JSInterruptHandler *interrupt_handler;
    280     void *interrupt_opaque;
    281 
    282     JSHostPromiseRejectionTracker *host_promise_rejection_tracker;
    283     void *host_promise_rejection_tracker_opaque;
    284 
    285     struct list_head job_list; /* list of JSJobEntry.link */
    286 
    287     JSModuleNormalizeFunc *module_normalize_func;
    288     JSModuleLoaderFunc *module_loader_func;
    289     void *module_loader_opaque;
    290     /* timestamp for internal use in module evaluation */
    291     int64_t module_async_evaluation_next_timestamp;
    292 
    293     BOOL can_block : 8; /* TRUE if Atomics.wait can block */
    294     /* used to allocate, free and clone SharedArrayBuffers */
    295     JSSharedArrayBufferFunctions sab_funcs;
    296 
    297     /* Shape hash table */
    298     int shape_hash_bits;
    299     int shape_hash_size;
    300     int shape_hash_count; /* number of hashed shapes */
    301     JSShape **shape_hash;
    302     bf_context_t bf_ctx;
    303     JSNumericOperations bigint_ops;
    304 #ifdef CONFIG_BIGNUM
    305     JSNumericOperations bigfloat_ops;
    306     JSNumericOperations bigdecimal_ops;
    307     uint32_t operator_count;
    308 #endif
    309     void *user_opaque;
    310 };
    311 
    312 struct JSClass {
    313     uint32_t class_id; /* 0 means free entry */
    314     JSAtom class_name;
    315     JSClassFinalizer *finalizer;
    316     JSClassGCMark *gc_mark;
    317     JSClassCall *call;
    318     /* pointers for exotic behavior, can be NULL if none are present */
    319     const JSClassExoticMethods *exotic;
    320 };
    321 
    322 #define JS_MODE_STRICT (1 << 0)
    323 #define JS_MODE_STRIP  (1 << 1)
    324 #define JS_MODE_MATH   (1 << 2)
    325 #define JS_MODE_ASYNC  (1 << 3) /* async function */
    326 
    327 typedef struct JSStackFrame {
    328     struct JSStackFrame *prev_frame; /* NULL if first stack frame */
    329     JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */
    330     JSValue *arg_buf; /* arguments */
    331     JSValue *var_buf; /* variables */
    332     struct list_head var_ref_list; /* list of JSVarRef.var_ref_link */
    333     const uint8_t *cur_pc; /* only used in bytecode functions : PC of the
    334                         instruction after the call */
    335     int arg_count;
    336     int js_mode; /* for C functions, only JS_MODE_MATH may be set */
    337     /* only used in generators. Current stack pointer value. NULL if
    338        the function is running. */
    339     JSValue *cur_sp;
    340 } JSStackFrame;
    341 
    342 typedef enum {
    343     JS_GC_OBJ_TYPE_JS_OBJECT,
    344     JS_GC_OBJ_TYPE_FUNCTION_BYTECODE,
    345     JS_GC_OBJ_TYPE_SHAPE,
    346     JS_GC_OBJ_TYPE_VAR_REF,
    347     JS_GC_OBJ_TYPE_ASYNC_FUNCTION,
    348     JS_GC_OBJ_TYPE_JS_CONTEXT,
    349 } JSGCObjectTypeEnum;
    350 
    351 /* header for GC objects. GC objects are C data structures with a
    352    reference count that can reference other GC objects. JS Objects are
    353    a particular type of GC object. */
    354 struct JSGCObjectHeader {
    355     int ref_count; /* must come first, 32-bit */
    356     JSGCObjectTypeEnum gc_obj_type : 4;
    357     uint8_t mark : 4; /* used by the GC */
    358     uint8_t dummy1; /* not used by the GC */
    359     uint16_t dummy2; /* not used by the GC */
    360     struct list_head link;
    361 };
    362 
    363 typedef struct JSVarRef {
    364     union {
    365         JSGCObjectHeader header; /* must come first */
    366         struct {
    367             int __gc_ref_count; /* corresponds to header.ref_count */
    368             uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */
    369             uint8_t is_detached : 1;
    370             uint8_t is_arg : 1;
    371             uint16_t var_idx; /* index of the corresponding function variable on
    372                                  the stack */
    373         };
    374     };
    375     JSValue *pvalue; /* pointer to the value, either on the stack or
    376                         to 'value' */
    377     union {
    378         JSValue value; /* used when is_detached = TRUE */
    379         struct {
    380             struct list_head var_ref_link; /* JSStackFrame.var_ref_list list */
    381             struct JSAsyncFunctionState *async_func; /* != NULL if async stack frame */
    382         }; /* used when is_detached = FALSE */
    383     };
    384 } JSVarRef;
    385 
    386 /* the same structure is used for big integers and big floats. Big
    387    integers are never infinite or NaNs */
    388 typedef struct JSBigFloat {
    389     JSRefCountHeader header; /* must come first, 32-bit */
    390     bf_t num;
    391 } JSBigFloat;
    392 
    393 #ifdef CONFIG_BIGNUM
    394 typedef struct JSFloatEnv {
    395     limb_t prec;
    396     bf_flags_t flags;
    397     unsigned int status;
    398 } JSFloatEnv;
    399 
    400 typedef struct JSBigDecimal {
    401     JSRefCountHeader header; /* must come first, 32-bit */
    402     bfdec_t num;
    403 } JSBigDecimal;
    404 #endif
    405 
    406 typedef enum {
    407     JS_AUTOINIT_ID_PROTOTYPE,
    408     JS_AUTOINIT_ID_MODULE_NS,
    409     JS_AUTOINIT_ID_PROP,
    410 } JSAutoInitIDEnum;
    411 
    412 /* must be large enough to have a negligible runtime cost and small
    413    enough to call the interrupt callback often. */
    414 #define JS_INTERRUPT_COUNTER_INIT 10000
    415 
    416 struct JSContext {
    417     JSGCObjectHeader header; /* must come first */
    418     JSRuntime *rt;
    419     struct list_head link;
    420 
    421     uint16_t binary_object_count;
    422     int binary_object_size;
    423 
    424     JSShape *array_shape;   /* initial shape for Array objects */
    425 
    426     JSValue *class_proto;
    427     JSValue function_proto;
    428     JSValue function_ctor;
    429     JSValue array_ctor;
    430     JSValue regexp_ctor;
    431     JSValue promise_ctor;
    432     JSValue native_error_proto[JS_NATIVE_ERROR_COUNT];
    433     JSValue iterator_proto;
    434     JSValue async_iterator_proto;
    435     JSValue array_proto_values;
    436     JSValue throw_type_error;
    437     JSValue eval_obj;
    438 
    439     JSValue global_obj; /* global object */
    440     JSValue global_var_obj; /* contains the global let/const definitions */
    441 
    442     uint64_t random_state;
    443     bf_context_t *bf_ctx;   /* points to rt->bf_ctx, shared by all contexts */
    444 #ifdef CONFIG_BIGNUM
    445     JSFloatEnv fp_env; /* global FP environment */
    446     BOOL bignum_ext : 8; /* enable math mode */
    447     BOOL allow_operator_overloading : 8;
    448 #endif
    449     /* when the counter reaches zero, JSRutime.interrupt_handler is called */
    450     int interrupt_counter;
    451 
    452     struct list_head loaded_modules; /* list of JSModuleDef.link */
    453 
    454     /* if NULL, RegExp compilation is not supported */
    455     JSValue (*compile_regexp)(JSContext *ctx, JSValueConst pattern,
    456                               JSValueConst flags);
    457     /* if NULL, eval is not supported */
    458     JSValue (*eval_internal)(JSContext *ctx, JSValueConst this_obj,
    459                              const char *input, size_t input_len,
    460                              const char *filename, int flags, int scope_idx);
    461     void *user_opaque;
    462 };
    463 
    464 typedef union JSFloat64Union {
    465     double d;
    466     uint64_t u64;
    467     uint32_t u32[2];
    468 } JSFloat64Union;
    469 
    470 enum {
    471     JS_ATOM_TYPE_STRING = 1,
    472     JS_ATOM_TYPE_GLOBAL_SYMBOL,
    473     JS_ATOM_TYPE_SYMBOL,
    474     JS_ATOM_TYPE_PRIVATE,
    475 };
    476 
    477 enum {
    478     JS_ATOM_HASH_SYMBOL,
    479     JS_ATOM_HASH_PRIVATE,
    480 };
    481 
    482 typedef enum {
    483     JS_ATOM_KIND_STRING,
    484     JS_ATOM_KIND_SYMBOL,
    485     JS_ATOM_KIND_PRIVATE,
    486 } JSAtomKindEnum;
    487 
    488 #define JS_ATOM_HASH_MASK  ((1 << 30) - 1)
    489 
    490 struct JSString {
    491     JSRefCountHeader header; /* must come first, 32-bit */
    492     uint32_t len : 31;
    493     uint8_t is_wide_char : 1; /* 0 = 8 bits, 1 = 16 bits characters */
    494     /* for JS_ATOM_TYPE_SYMBOL: hash = 0, atom_type = 3,
    495        for JS_ATOM_TYPE_PRIVATE: hash = 1, atom_type = 3
    496        XXX: could change encoding to have one more bit in hash */
    497     uint32_t hash : 30;
    498     uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */
    499     uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */
    500 #ifdef DUMP_LEAKS
    501     struct list_head link; /* string list */
    502 #endif
    503     union {
    504         uint8_t str8[0]; /* 8 bit strings will get an extra null terminator */
    505         uint16_t str16[0];
    506     } u;
    507 };
    508 
    509 typedef struct JSClosureVar {
    510     uint8_t is_local : 1;
    511     uint8_t is_arg : 1;
    512     uint8_t is_const : 1;
    513     uint8_t is_lexical : 1;
    514     uint8_t var_kind : 4; /* see JSVarKindEnum */
    515     /* 8 bits available */
    516     uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the
    517                     parent function. otherwise: index to a closure
    518                     variable of the parent function */
    519     JSAtom var_name;
    520 } JSClosureVar;
    521 
    522 #define ARG_SCOPE_INDEX 1
    523 #define ARG_SCOPE_END (-2)
    524 
    525 typedef struct JSVarScope {
    526     int parent;  /* index into fd->scopes of the enclosing scope */
    527     int first;   /* index into fd->vars of the last variable in this scope */
    528 } JSVarScope;
    529 
    530 typedef enum {
    531     /* XXX: add more variable kinds here instead of using bit fields */
    532     JS_VAR_NORMAL,
    533     JS_VAR_FUNCTION_DECL, /* lexical var with function declaration */
    534     JS_VAR_NEW_FUNCTION_DECL, /* lexical var with async/generator
    535                                  function declaration */
    536     JS_VAR_CATCH,
    537     JS_VAR_FUNCTION_NAME, /* function expression name */
    538     JS_VAR_PRIVATE_FIELD,
    539     JS_VAR_PRIVATE_METHOD,
    540     JS_VAR_PRIVATE_GETTER,
    541     JS_VAR_PRIVATE_SETTER, /* must come after JS_VAR_PRIVATE_GETTER */
    542     JS_VAR_PRIVATE_GETTER_SETTER, /* must come after JS_VAR_PRIVATE_SETTER */
    543 } JSVarKindEnum;
    544 
    545 /* XXX: could use a different structure in bytecode functions to save
    546    memory */
    547 typedef struct JSVarDef {
    548     JSAtom var_name;
    549     /* index into fd->scopes of this variable lexical scope */
    550     int scope_level;
    551     /* during compilation:
    552         - if scope_level = 0: scope in which the variable is defined
    553         - if scope_level != 0: index into fd->vars of the next
    554           variable in the same or enclosing lexical scope
    555        in a bytecode function:
    556        index into fd->vars of the next
    557        variable in the same or enclosing lexical scope
    558     */
    559     int scope_next;
    560     uint8_t is_const : 1;
    561     uint8_t is_lexical : 1;
    562     uint8_t is_captured : 1;
    563     uint8_t is_static_private : 1; /* only used during private class field parsing */
    564     uint8_t var_kind : 4; /* see JSVarKindEnum */
    565     /* only used during compilation: function pool index for lexical
    566        variables with var_kind =
    567        JS_VAR_FUNCTION_DECL/JS_VAR_NEW_FUNCTION_DECL or scope level of
    568        the definition of the 'var' variables (they have scope_level =
    569        0) */
    570     int func_pool_idx : 24; /* only used during compilation : index in
    571                                the constant pool for hoisted function
    572                                definition */
    573 } JSVarDef;
    574 
    575 /* for the encoding of the pc2line table */
    576 #define PC2LINE_BASE     (-1)
    577 #define PC2LINE_RANGE    5
    578 #define PC2LINE_OP_FIRST 1
    579 #define PC2LINE_DIFF_PC_MAX ((255 - PC2LINE_OP_FIRST) / PC2LINE_RANGE)
    580 
    581 typedef enum JSFunctionKindEnum {
    582     JS_FUNC_NORMAL = 0,
    583     JS_FUNC_GENERATOR = (1 << 0),
    584     JS_FUNC_ASYNC = (1 << 1),
    585     JS_FUNC_ASYNC_GENERATOR = (JS_FUNC_GENERATOR | JS_FUNC_ASYNC),
    586 } JSFunctionKindEnum;
    587 
    588 typedef struct JSFunctionBytecode {
    589     JSGCObjectHeader header; /* must come first */
    590     uint8_t js_mode;
    591     uint8_t has_prototype : 1; /* true if a prototype field is necessary */
    592     uint8_t has_simple_parameter_list : 1;
    593     uint8_t is_derived_class_constructor : 1;
    594     /* true if home_object needs to be initialized */
    595     uint8_t need_home_object : 1;
    596     uint8_t func_kind : 2;
    597     uint8_t new_target_allowed : 1;
    598     uint8_t super_call_allowed : 1;
    599     uint8_t super_allowed : 1;
    600     uint8_t arguments_allowed : 1;
    601     uint8_t has_debug : 1;
    602     uint8_t backtrace_barrier : 1; /* stop backtrace on this function */
    603     uint8_t read_only_bytecode : 1;
    604     uint8_t is_direct_or_indirect_eval : 1; /* used by JS_GetScriptOrModuleName() */
    605     /* XXX: 10 bits available */
    606     uint8_t *byte_code_buf; /* (self pointer) */
    607     int byte_code_len;
    608     JSAtom func_name;
    609     JSVarDef *vardefs; /* arguments + local variables (arg_count + var_count) (self pointer) */
    610     JSClosureVar *closure_var; /* list of variables in the closure (self pointer) */
    611     uint16_t arg_count;
    612     uint16_t var_count;
    613     uint16_t defined_arg_count; /* for length function property */
    614     uint16_t stack_size; /* maximum stack size */
    615     JSContext *realm; /* function realm */
    616     JSValue *cpool; /* constant pool (self pointer) */
    617     int cpool_count;
    618     int closure_var_count;
    619     void *perf_trampoline;
    620     struct {
    621         /* debug info, move to separate structure to save memory? */
    622         JSAtom filename;
    623         int line_num;
    624         int source_len;
    625         int pc2line_len;
    626         uint8_t *pc2line_buf;
    627         char *source;
    628     } debug;
    629 } JSFunctionBytecode;
    630 
    631 typedef struct JSBoundFunction {
    632     JSValue func_obj;
    633     JSValue this_val;
    634     int argc;
    635     JSValue argv[0];
    636 } JSBoundFunction;
    637 
    638 typedef enum JSIteratorKindEnum {
    639     JS_ITERATOR_KIND_KEY,
    640     JS_ITERATOR_KIND_VALUE,
    641     JS_ITERATOR_KIND_KEY_AND_VALUE,
    642 } JSIteratorKindEnum;
    643 
    644 typedef struct JSForInIterator {
    645     JSValue obj;
    646     uint32_t idx;
    647     uint32_t atom_count;
    648     uint8_t in_prototype_chain;
    649     uint8_t is_array;
    650     JSPropertyEnum *tab_atom; /* is_array = FALSE */
    651 } JSForInIterator;
    652 
    653 typedef struct JSRegExp {
    654     JSString *pattern;
    655     JSString *bytecode; /* also contains the flags */
    656 } JSRegExp;
    657 
    658 typedef struct JSProxyData {
    659     JSValue target;
    660     JSValue handler;
    661     uint8_t is_func;
    662     uint8_t is_revoked;
    663 } JSProxyData;
    664 
    665 typedef struct JSArrayBuffer {
    666     int byte_length; /* 0 if detached */
    667     uint8_t detached;
    668     uint8_t shared; /* if shared, the array buffer cannot be detached */
    669     uint8_t *data; /* NULL if detached */
    670     struct list_head array_list;
    671     void *opaque;
    672     JSFreeArrayBufferDataFunc *free_func;
    673 } JSArrayBuffer;
    674 
    675 typedef struct JSTypedArray {
    676     struct list_head link; /* link to arraybuffer */
    677     JSObject *obj; /* back pointer to the TypedArray/DataView object */
    678     JSObject *buffer; /* based array buffer */
    679     uint32_t offset; /* offset in the array buffer */
    680     uint32_t length; /* length in the array buffer */
    681 } JSTypedArray;
    682 
    683 typedef struct JSAsyncFunctionState {
    684     JSGCObjectHeader header;
    685     JSValue this_val; /* 'this' argument */
    686     int argc; /* number of function arguments */
    687     BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */
    688     BOOL is_completed; /* TRUE if the function has returned. The stack
    689                           frame is no longer valid */
    690     JSValue resolving_funcs[2]; /* only used in JS async functions */
    691     JSStackFrame frame;
    692 } JSAsyncFunctionState;
    693 
    694 typedef enum {
    695    /* binary operators */
    696    JS_OVOP_ADD,
    697    JS_OVOP_SUB,
    698    JS_OVOP_MUL,
    699    JS_OVOP_DIV,
    700    JS_OVOP_MOD,
    701    JS_OVOP_POW,
    702    JS_OVOP_OR,
    703    JS_OVOP_AND,
    704    JS_OVOP_XOR,
    705    JS_OVOP_SHL,
    706    JS_OVOP_SAR,
    707    JS_OVOP_SHR,
    708    JS_OVOP_EQ,
    709    JS_OVOP_LESS,
    710 
    711    JS_OVOP_BINARY_COUNT,
    712    /* unary operators */
    713    JS_OVOP_POS = JS_OVOP_BINARY_COUNT,
    714    JS_OVOP_NEG,
    715    JS_OVOP_INC,
    716    JS_OVOP_DEC,
    717    JS_OVOP_NOT,
    718 
    719    JS_OVOP_COUNT,
    720 } JSOverloadableOperatorEnum;
    721 
    722 typedef struct {
    723     uint32_t operator_index;
    724     JSObject *ops[JS_OVOP_BINARY_COUNT]; /* self operators */
    725 } JSBinaryOperatorDefEntry;
    726 
    727 typedef struct {
    728     int count;
    729     JSBinaryOperatorDefEntry *tab;
    730 } JSBinaryOperatorDef;
    731 
    732 typedef struct {
    733     uint32_t operator_counter;
    734     BOOL is_primitive; /* OperatorSet for a primitive type */
    735     /* NULL if no operator is defined */
    736     JSObject *self_ops[JS_OVOP_COUNT]; /* self operators */
    737     JSBinaryOperatorDef left;
    738     JSBinaryOperatorDef right;
    739 } JSOperatorSetData;
    740 
    741 typedef struct JSReqModuleEntry {
    742     JSAtom module_name;
    743     JSModuleDef *module; /* used using resolution */
    744 } JSReqModuleEntry;
    745 
    746 typedef enum JSExportTypeEnum {
    747     JS_EXPORT_TYPE_LOCAL,
    748     JS_EXPORT_TYPE_INDIRECT,
    749 } JSExportTypeEnum;
    750 
    751 typedef struct JSExportEntry {
    752     union {
    753         struct {
    754             int var_idx; /* closure variable index */
    755             JSVarRef *var_ref; /* if != NULL, reference to the variable */
    756         } local; /* for local export */
    757         int req_module_idx; /* module for indirect export */
    758     } u;
    759     JSExportTypeEnum export_type;
    760     JSAtom local_name; /* '*' if export ns from. not used for local
    761                           export after compilation */
    762     JSAtom export_name; /* exported variable name */
    763 } JSExportEntry;
    764 
    765 typedef struct JSStarExportEntry {
    766     int req_module_idx; /* in req_module_entries */
    767 } JSStarExportEntry;
    768 
    769 typedef struct JSImportEntry {
    770     int var_idx; /* closure variable index */
    771     JSAtom import_name;
    772     int req_module_idx; /* in req_module_entries */
    773 } JSImportEntry;
    774 
    775 typedef enum {
    776     JS_MODULE_STATUS_UNLINKED,
    777     JS_MODULE_STATUS_LINKING,
    778     JS_MODULE_STATUS_LINKED,
    779     JS_MODULE_STATUS_EVALUATING,
    780     JS_MODULE_STATUS_EVALUATING_ASYNC,
    781     JS_MODULE_STATUS_EVALUATED,
    782 } JSModuleStatus;
    783 
    784 struct JSModuleDef {
    785     JSRefCountHeader header; /* must come first, 32-bit */
    786     JSAtom module_name;
    787     struct list_head link;
    788 
    789     JSReqModuleEntry *req_module_entries;
    790     int req_module_entries_count;
    791     int req_module_entries_size;
    792 
    793     JSExportEntry *export_entries;
    794     int export_entries_count;
    795     int export_entries_size;
    796 
    797     JSStarExportEntry *star_export_entries;
    798     int star_export_entries_count;
    799     int star_export_entries_size;
    800 
    801     JSImportEntry *import_entries;
    802     int import_entries_count;
    803     int import_entries_size;
    804 
    805     JSValue module_ns;
    806     JSValue func_obj; /* only used for JS modules */
    807     JSModuleInitFunc *init_func; /* only used for C modules */
    808     BOOL has_tla : 8; /* true if func_obj contains await */
    809     BOOL resolved : 8;
    810     BOOL func_created : 8;
    811     JSModuleStatus status : 8;
    812     /* temp use during js_module_link() & js_module_evaluate() */
    813     int dfs_index, dfs_ancestor_index;
    814     JSModuleDef *stack_prev;
    815     /* temp use during js_module_evaluate() */
    816     JSModuleDef **async_parent_modules;
    817     int async_parent_modules_count;
    818     int async_parent_modules_size;
    819     int pending_async_dependencies;
    820     BOOL async_evaluation;
    821     int64_t async_evaluation_timestamp;
    822     JSModuleDef *cycle_root;
    823     JSValue promise; /* corresponds to spec field: capability */
    824     JSValue resolving_funcs[2]; /* corresponds to spec field: capability */
    825 
    826     /* true if evaluation yielded an exception. It is saved in
    827        eval_exception */
    828     BOOL eval_has_exception : 8;
    829     JSValue eval_exception;
    830     JSValue meta_obj; /* for import.meta */
    831 };
    832 
    833 typedef struct JSJobEntry {
    834     struct list_head link;
    835     JSContext *ctx;
    836     JSJobFunc *job_func;
    837     int argc;
    838     JSValue argv[0];
    839 } JSJobEntry;
    840 
    841 typedef struct JSProperty {
    842     union {
    843         JSValue value;      /* JS_PROP_NORMAL */
    844         struct {            /* JS_PROP_GETSET */
    845             JSObject *getter; /* NULL if undefined */
    846             JSObject *setter; /* NULL if undefined */
    847         } getset;
    848         JSVarRef *var_ref;  /* JS_PROP_VARREF */
    849         struct {            /* JS_PROP_AUTOINIT */
    850             /* in order to use only 2 pointers, we compress the realm
    851                and the init function pointer */
    852             uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x)
    853                                        in the 2 low bits */
    854             void *opaque;
    855         } init;
    856     } u;
    857 } JSProperty;
    858 
    859 #define JS_PROP_INITIAL_SIZE 2
    860 #define JS_PROP_INITIAL_HASH_SIZE 4 /* must be a power of two */
    861 #define JS_ARRAY_INITIAL_SIZE 2
    862 
    863 typedef struct JSShapeProperty {
    864     uint32_t hash_next : 26; /* 0 if last in list */
    865     uint32_t flags : 6;   /* JS_PROP_XXX */
    866     JSAtom atom; /* JS_ATOM_NULL = free property entry */
    867 } JSShapeProperty;
    868 
    869 struct JSShape {
    870     /* hash table of size hash_mask + 1 before the start of the
    871        structure (see prop_hash_end()). */
    872     JSGCObjectHeader header;
    873     /* true if the shape is inserted in the shape hash table. If not,
    874        JSShape.hash is not valid */
    875     uint8_t is_hashed;
    876     /* If true, the shape may have small array index properties 'n' with 0
    877        <= n <= 2^31-1. If false, the shape is guaranteed not to have
    878        small array index properties */
    879     uint8_t has_small_array_index;
    880     uint32_t hash; /* current hash value */
    881     uint32_t prop_hash_mask;
    882     int prop_size; /* allocated properties */
    883     int prop_count; /* include deleted properties */
    884     int deleted_prop_count;
    885     JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */
    886     JSObject *proto;
    887     JSShapeProperty prop[0]; /* prop_size elements */
    888 };
    889 
    890 struct JSObject {
    891     union {
    892         JSGCObjectHeader header;
    893         struct {
    894             int __gc_ref_count; /* corresponds to header.ref_count */
    895             uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */
    896 
    897             uint8_t extensible : 1;
    898             uint8_t free_mark : 1; /* only used when freeing objects with cycles */
    899             uint8_t is_exotic : 1; /* TRUE if object has exotic property handlers */
    900             uint8_t fast_array : 1; /* TRUE if u.array is used for get/put (for JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS and typed arrays) */
    901             uint8_t is_constructor : 1; /* TRUE if object is a constructor function */
    902             uint8_t is_uncatchable_error : 1; /* if TRUE, error is not catchable */
    903             uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */
    904             uint8_t is_HTMLDDA : 1; /* specific annex B IsHtmlDDA behavior */
    905             uint16_t class_id; /* see JS_CLASS_x */
    906         };
    907     };
    908     /* byte offsets: 16/24 */
    909     JSShape *shape; /* prototype and property names + flag */
    910     JSProperty *prop; /* array of properties */
    911     /* byte offsets: 24/40 */
    912     struct JSMapRecord *first_weak_ref; /* XXX: use a bit and an external hash table? */
    913     /* byte offsets: 28/48 */
    914     union {
    915         void *opaque;
    916         struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */
    917         struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */
    918         struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */
    919         struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */
    920         struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */
    921 #ifdef CONFIG_BIGNUM
    922         struct JSFloatEnv *float_env; /* JS_CLASS_FLOAT_ENV */
    923         struct JSOperatorSetData *operator_set; /* JS_CLASS_OPERATOR_SET */
    924 #endif
    925         struct JSMapState *map_state;   /* JS_CLASS_MAP..JS_CLASS_WEAKSET */
    926         struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */
    927         struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */
    928         struct JSRegExpStringIteratorData *regexp_string_iterator_data; /* JS_CLASS_REGEXP_STRING_ITERATOR */
    929         struct JSGeneratorData *generator_data; /* JS_CLASS_GENERATOR */
    930         struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */
    931         struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */
    932         struct JSPromiseFunctionData *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, JS_CLASS_PROMISE_REJECT_FUNCTION */
    933         struct JSAsyncFunctionState *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */
    934         struct JSAsyncFromSyncIteratorData *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */
    935         struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */
    936         struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */
    937             /* also used by JS_CLASS_GENERATOR_FUNCTION, JS_CLASS_ASYNC_FUNCTION and JS_CLASS_ASYNC_GENERATOR_FUNCTION */
    938             struct JSFunctionBytecode *function_bytecode;
    939             JSVarRef **var_refs;
    940             JSObject *home_object; /* for 'super' access */
    941         } func;
    942         struct { /* JS_CLASS_C_FUNCTION: 12/20 bytes */
    943             JSContext *realm;
    944             JSCFunctionType c_function;
    945             uint8_t length;
    946             uint8_t cproto;
    947             int16_t magic;
    948         } cfunc;
    949         /* array part for fast arrays and typed arrays */
    950         struct { /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */
    951             union {
    952                 uint32_t size;          /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */
    953                 struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */
    954             } u1;
    955             union {
    956                 JSValue *values;        /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */
    957                 void *ptr;              /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */
    958                 int8_t *int8_ptr;       /* JS_CLASS_INT8_ARRAY */
    959                 uint8_t *uint8_ptr;     /* JS_CLASS_UINT8_ARRAY, JS_CLASS_UINT8C_ARRAY */
    960                 int16_t *int16_ptr;     /* JS_CLASS_INT16_ARRAY */
    961                 uint16_t *uint16_ptr;   /* JS_CLASS_UINT16_ARRAY */
    962                 int32_t *int32_ptr;     /* JS_CLASS_INT32_ARRAY */
    963                 uint32_t *uint32_ptr;   /* JS_CLASS_UINT32_ARRAY */
    964                 int64_t *int64_ptr;     /* JS_CLASS_INT64_ARRAY */
    965                 uint64_t *uint64_ptr;   /* JS_CLASS_UINT64_ARRAY */
    966                 float *float_ptr;       /* JS_CLASS_FLOAT32_ARRAY */
    967                 double *double_ptr;     /* JS_CLASS_FLOAT64_ARRAY */
    968             } u;
    969             uint32_t count; /* <= 2^31-1. 0 for a detached typed array */
    970         } array;    /* 12/20 bytes */
    971         JSRegExp regexp;    /* JS_CLASS_REGEXP: 8/16 bytes */
    972         JSValue object_data;    /* for JS_SetObjectData(): 8/16/16 bytes */
    973     } u;
    974     /* byte sizes: 40/48/72 */
    975 };
    976 
    977 enum {
    978     __JS_ATOM_NULL = JS_ATOM_NULL,
    979 #define DEF(name, str) JS_ATOM_ ## name,
    980 #include "quickjs-atom.h"
    981 #undef DEF
    982     JS_ATOM_END,
    983 };
    984 #define JS_ATOM_LAST_KEYWORD JS_ATOM_super
    985 #define JS_ATOM_LAST_STRICT_KEYWORD JS_ATOM_yield
    986 
    987 static const char js_atom_init[] =
    988 #define DEF(name, str) str "\0"
    989 #include "quickjs-atom.h"
    990 #undef DEF
    991 ;
    992 
    993 typedef enum OPCodeFormat {
    994 #define FMT(f) OP_FMT_ ## f,
    995 #define DEF(id, size, n_pop, n_push, f)
    996 #include "quickjs-opcode.h"
    997 #undef DEF
    998 #undef FMT
    999 } OPCodeFormat;
   1000 
   1001 enum OPCodeEnum {
   1002 #define FMT(f)
   1003 #define DEF(id, size, n_pop, n_push, f) OP_ ## id,
   1004 #define def(id, size, n_pop, n_push, f)
   1005 #include "quickjs-opcode.h"
   1006 #undef def
   1007 #undef DEF
   1008 #undef FMT
   1009     OP_COUNT, /* excluding temporary opcodes */
   1010     /* temporary opcodes : overlap with the short opcodes */
   1011     OP_TEMP_START = OP_nop + 1,
   1012     OP___dummy = OP_TEMP_START - 1,
   1013 #define FMT(f)
   1014 #define DEF(id, size, n_pop, n_push, f)
   1015 #define def(id, size, n_pop, n_push, f) OP_ ## id,
   1016 #include "quickjs-opcode.h"
   1017 #undef def
   1018 #undef DEF
   1019 #undef FMT
   1020     OP_TEMP_END,
   1021 };
   1022 
   1023 static int JS_InitAtoms(JSRuntime *rt);
   1024 static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len,
   1025                                int atom_type);
   1026 static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p);
   1027 static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b);
   1028 static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,
   1029                                   JSValueConst this_obj,
   1030                                   int argc, JSValueConst *argv, int flags);
   1031 static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj,
   1032                                       JSValueConst this_obj,
   1033                                       int argc, JSValueConst *argv, int flags);
   1034 static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj,
   1035                                JSValueConst this_obj, JSValueConst new_target,
   1036                                int argc, JSValue *argv, int flags);
   1037 static JSValue JS_CallConstructorInternal(JSContext *ctx,
   1038                                           JSValueConst func_obj,
   1039                                           JSValueConst new_target,
   1040                                           int argc, JSValue *argv, int flags);
   1041 static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj,
   1042                            int argc, JSValueConst *argv);
   1043 static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,
   1044                              int argc, JSValueConst *argv);
   1045 static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
   1046                                             JSValue val, BOOL is_array_ctor);
   1047 static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
   1048                              JSValueConst val, int flags, int scope_idx);
   1049 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...);
   1050 static __maybe_unused void JS_DumpAtoms(JSRuntime *rt);
   1051 static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p);
   1052 static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt);
   1053 static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p);
   1054 static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p);
   1055 static __maybe_unused void JS_DumpValueShort(JSRuntime *rt, JSValueConst val);
   1056 static __maybe_unused void JS_DumpValue(JSContext *ctx, JSValueConst val);
   1057 static __maybe_unused void JS_PrintValue(JSContext *ctx,
   1058                                                   const char *str,
   1059                                                   JSValueConst val);
   1060 static __maybe_unused void JS_DumpShapes(JSRuntime *rt);
   1061 static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val,
   1062                                  int argc, JSValueConst *argv, int magic);
   1063 static void js_array_finalizer(JSRuntime *rt, JSValue val);
   1064 static void js_array_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func);
   1065 static void js_object_data_finalizer(JSRuntime *rt, JSValue val);
   1066 static void js_object_data_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func);
   1067 static void js_c_function_finalizer(JSRuntime *rt, JSValue val);
   1068 static void js_c_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func);
   1069 static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val);
   1070 static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val,
   1071                                 JS_MarkFunc *mark_func);
   1072 static void js_bound_function_finalizer(JSRuntime *rt, JSValue val);
   1073 static void js_bound_function_mark(JSRuntime *rt, JSValueConst val,
   1074                                 JS_MarkFunc *mark_func);
   1075 static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val);
   1076 static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val,
   1077                                 JS_MarkFunc *mark_func);
   1078 static void js_regexp_finalizer(JSRuntime *rt, JSValue val);
   1079 static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val);
   1080 static void js_typed_array_finalizer(JSRuntime *rt, JSValue val);
   1081 static void js_typed_array_mark(JSRuntime *rt, JSValueConst val,
   1082                                 JS_MarkFunc *mark_func);
   1083 static void js_proxy_finalizer(JSRuntime *rt, JSValue val);
   1084 static void js_proxy_mark(JSRuntime *rt, JSValueConst val,
   1085                                 JS_MarkFunc *mark_func);
   1086 static void js_map_finalizer(JSRuntime *rt, JSValue val);
   1087 static void js_map_mark(JSRuntime *rt, JSValueConst val,
   1088                                 JS_MarkFunc *mark_func);
   1089 static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val);
   1090 static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val,
   1091                                 JS_MarkFunc *mark_func);
   1092 static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val);
   1093 static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val,
   1094                                 JS_MarkFunc *mark_func);
   1095 static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val);
   1096 static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val,
   1097                                 JS_MarkFunc *mark_func);
   1098 static void js_generator_finalizer(JSRuntime *rt, JSValue obj);
   1099 static void js_generator_mark(JSRuntime *rt, JSValueConst val,
   1100                                 JS_MarkFunc *mark_func);
   1101 static void js_promise_finalizer(JSRuntime *rt, JSValue val);
   1102 static void js_promise_mark(JSRuntime *rt, JSValueConst val,
   1103                                 JS_MarkFunc *mark_func);
   1104 static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val);
   1105 static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,
   1106                                 JS_MarkFunc *mark_func);
   1107 #ifdef CONFIG_BIGNUM
   1108 static void js_operator_set_finalizer(JSRuntime *rt, JSValue val);
   1109 static void js_operator_set_mark(JSRuntime *rt, JSValueConst val,
   1110                                  JS_MarkFunc *mark_func);
   1111 #endif
   1112 
   1113 #define HINT_STRING  0
   1114 #define HINT_NUMBER  1
   1115 #define HINT_NONE    2
   1116 #define HINT_FORCE_ORDINARY (1 << 4) // don't try Symbol.toPrimitive
   1117 static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint);
   1118 static JSValue JS_ToStringFree(JSContext *ctx, JSValue val);
   1119 static int JS_ToBoolFree(JSContext *ctx, JSValue val);
   1120 static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val);
   1121 static int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val);
   1122 static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val);
   1123 static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern,
   1124                                  JSValueConst flags);
   1125 static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor,
   1126                                               JSValue pattern, JSValue bc);
   1127 static void gc_decref(JSRuntime *rt);
   1128 static int JS_NewClass1(JSRuntime *rt, JSClassID class_id,
   1129                         const JSClassDef *class_def, JSAtom name);
   1130 
   1131 typedef enum JSStrictEqModeEnum {
   1132     JS_EQ_STRICT,
   1133     JS_EQ_SAME_VALUE,
   1134     JS_EQ_SAME_VALUE_ZERO,
   1135 } JSStrictEqModeEnum;
   1136 
   1137 static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,
   1138                           JSStrictEqModeEnum eq_mode);
   1139 static BOOL js_strict_eq(JSContext *ctx, JSValueConst op1, JSValueConst op2);
   1140 static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2);
   1141 static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2);
   1142 static JSValue JS_ToObject(JSContext *ctx, JSValueConst val);
   1143 static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val);
   1144 static JSProperty *add_property(JSContext *ctx,
   1145                                 JSObject *p, JSAtom prop, int prop_flags);
   1146 static JSValue JS_NewBigInt(JSContext *ctx);
   1147 static inline bf_t *JS_GetBigInt(JSValueConst val)
   1148 {
   1149     JSBigFloat *p = JS_VALUE_GET_PTR(val);
   1150     return &p->num;
   1151 }
   1152 static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val,
   1153                                  BOOL convert_to_safe_integer);
   1154 static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val);
   1155 static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val);
   1156 static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val);
   1157 static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf);
   1158 #ifdef CONFIG_BIGNUM
   1159 static void js_float_env_finalizer(JSRuntime *rt, JSValue val);
   1160 static JSValue JS_NewBigFloat(JSContext *ctx);
   1161 static inline bf_t *JS_GetBigFloat(JSValueConst val)
   1162 {
   1163     JSBigFloat *p = JS_VALUE_GET_PTR(val);
   1164     return &p->num;
   1165 }
   1166 static JSValue JS_NewBigDecimal(JSContext *ctx);
   1167 static inline bfdec_t *JS_GetBigDecimal(JSValueConst val)
   1168 {
   1169     JSBigDecimal *p = JS_VALUE_GET_PTR(val);
   1170     return &p->num;
   1171 }
   1172 static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val);
   1173 static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,
   1174                                    BOOL allow_null_or_undefined);
   1175 static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val);
   1176 #endif
   1177 JSValue JS_ThrowOutOfMemory(JSContext *ctx);
   1178 static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx);
   1179 static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj);
   1180 static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,
   1181                                    JSValueConst proto_val, BOOL throw_flag);
   1182 
   1183 static int js_resolve_proxy(JSContext *ctx, JSValueConst *pval, int throw_exception);
   1184 static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj);
   1185 static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj);
   1186 static int JS_CreateProperty(JSContext *ctx, JSObject *p,
   1187                              JSAtom prop, JSValueConst val,
   1188                              JSValueConst getter, JSValueConst setter,
   1189                              int flags);
   1190 static int js_string_memcmp(const JSString *p1, const JSString *p2, int len);
   1191 static void reset_weak_ref(JSRuntime *rt, JSObject *p);
   1192 static JSValue js_array_buffer_constructor3(JSContext *ctx,
   1193                                             JSValueConst new_target,
   1194                                             uint64_t len, JSClassID class_id,
   1195                                             uint8_t *buf,
   1196                                             JSFreeArrayBufferDataFunc *free_func,
   1197                                             void *opaque, BOOL alloc_flag);
   1198 static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj);
   1199 static JSValue js_typed_array_constructor(JSContext *ctx,
   1200                                           JSValueConst this_val,
   1201                                           int argc, JSValueConst *argv,
   1202                                           int classid);
   1203 static JSValue js_typed_array_constructor_ta(JSContext *ctx,
   1204                                              JSValueConst new_target,
   1205                                              JSValueConst src_obj,
   1206                                              int classid);
   1207 static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p);
   1208 static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p);
   1209 static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx);
   1210 static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx,
   1211                              BOOL is_arg);
   1212 static void __async_func_free(JSRuntime *rt, JSAsyncFunctionState *s);
   1213 static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s);
   1214 static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
   1215                                           JSValueConst this_obj,
   1216                                           int argc, JSValueConst *argv,
   1217                                           int flags);
   1218 static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val);
   1219 static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val,
   1220                                            JS_MarkFunc *mark_func);
   1221 static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
   1222                                const char *input, size_t input_len,
   1223                                const char *filename, int flags, int scope_idx);
   1224 static void js_free_module_def(JSContext *ctx, JSModuleDef *m);
   1225 static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,
   1226                                JS_MarkFunc *mark_func);
   1227 static JSValue js_import_meta(JSContext *ctx);
   1228 static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier);
   1229 static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref);
   1230 static JSValue js_new_promise_capability(JSContext *ctx,
   1231                                          JSValue *resolving_funcs,
   1232                                          JSValueConst ctor);
   1233 static __exception int perform_promise_then(JSContext *ctx,
   1234                                             JSValueConst promise,
   1235                                             JSValueConst *resolve_reject,
   1236                                             JSValueConst *cap_resolving_funcs);
   1237 static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val,
   1238                                   int argc, JSValueConst *argv, int magic);
   1239 static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val,
   1240                                int argc, JSValueConst *argv);
   1241 static int js_string_compare(JSContext *ctx,
   1242                              const JSString *p1, const JSString *p2);
   1243 static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val);
   1244 static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
   1245                                JSValue prop, JSValue val, int flags);
   1246 static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val);
   1247 static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val);
   1248 static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val);
   1249 static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,
   1250                                      JSObject *p, JSAtom prop);
   1251 static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc);
   1252 static void JS_AddIntrinsicBasicObjects(JSContext *ctx);
   1253 static void js_free_shape(JSRuntime *rt, JSShape *sh);
   1254 static void js_free_shape_null(JSRuntime *rt, JSShape *sh);
   1255 static int js_shape_prepare_update(JSContext *ctx, JSObject *p,
   1256                                    JSShapeProperty **pprs);
   1257 static int init_shape_hash(JSRuntime *rt);
   1258 static __exception int js_get_length32(JSContext *ctx, uint32_t *pres,
   1259                                        JSValueConst obj);
   1260 static __exception int js_get_length64(JSContext *ctx, int64_t *pres,
   1261                                        JSValueConst obj);
   1262 static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len);
   1263 static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen,
   1264                                JSValueConst array_arg);
   1265 static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj,
   1266                               JSValue **arrpp, uint32_t *countp);
   1267 static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx,
   1268                                               JSValueConst sync_iter);
   1269 static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val);
   1270 static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val,
   1271                                     JS_MarkFunc *mark_func);
   1272 static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj,
   1273                                        JSValueConst this_val,
   1274                                        int argc, JSValueConst *argv, int flags);
   1275 static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val);
   1276 static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,
   1277                           JSGCObjectTypeEnum type);
   1278 static void remove_gc_object(JSGCObjectHeader *h);
   1279 static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque);
   1280 static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,
   1281                                  void *opaque);
   1282 static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p,
   1283                                                JSAtom atom, void *opaque);
   1284 static JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val,
   1285                                  int argc, JSValueConst *argv, int is_map);
   1286 
   1287 static const JSClassExoticMethods js_arguments_exotic_methods;
   1288 static const JSClassExoticMethods js_string_exotic_methods;
   1289 static const JSClassExoticMethods js_proxy_exotic_methods;
   1290 static const JSClassExoticMethods js_module_ns_exotic_methods;
   1291 static JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT;
   1292 
   1293 static void js_trigger_gc(JSRuntime *rt, size_t size)
   1294 {
   1295     BOOL force_gc;
   1296 #ifdef FORCE_GC_AT_MALLOC
   1297     force_gc = TRUE;
   1298 #else
   1299     force_gc = ((rt->malloc_state.malloc_size + size) >
   1300                 rt->malloc_gc_threshold);
   1301 #endif
   1302     if (force_gc) {
   1303 #ifdef DUMP_GC
   1304         printf("GC: size=%" PRIu64 "\n",
   1305                (uint64_t)rt->malloc_state.malloc_size);
   1306 #endif
   1307         JS_RunGC(rt);
   1308         rt->malloc_gc_threshold = rt->malloc_state.malloc_size +
   1309             (rt->malloc_state.malloc_size >> 1);
   1310     }
   1311 }
   1312 
   1313 static size_t js_malloc_usable_size_unknown(const void *ptr)
   1314 {
   1315     return 0;
   1316 }
   1317 
   1318 void *js_malloc_rt(JSRuntime *rt, size_t size)
   1319 {
   1320     return rt->mf.js_malloc(&rt->malloc_state, size);
   1321 }
   1322 
   1323 void js_free_rt(JSRuntime *rt, void *ptr)
   1324 {
   1325     rt->mf.js_free(&rt->malloc_state, ptr);
   1326 }
   1327 
   1328 void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size)
   1329 {
   1330     return rt->mf.js_realloc(&rt->malloc_state, ptr, size);
   1331 }
   1332 
   1333 size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr)
   1334 {
   1335     return rt->mf.js_malloc_usable_size(ptr);
   1336 }
   1337 
   1338 void *js_mallocz_rt(JSRuntime *rt, size_t size)
   1339 {
   1340     void *ptr;
   1341     ptr = js_malloc_rt(rt, size);
   1342     if (!ptr)
   1343         return NULL;
   1344     return memset(ptr, 0, size);
   1345 }
   1346 
   1347 /* called by libbf */
   1348 static void *js_bf_realloc(void *opaque, void *ptr, size_t size)
   1349 {
   1350     JSRuntime *rt = opaque;
   1351     return js_realloc_rt(rt, ptr, size);
   1352 }
   1353 
   1354 /* Throw out of memory in case of error */
   1355 void *js_malloc(JSContext *ctx, size_t size)
   1356 {
   1357     void *ptr;
   1358     ptr = js_malloc_rt(ctx->rt, size);
   1359     if (unlikely(!ptr)) {
   1360         JS_ThrowOutOfMemory(ctx);
   1361         return NULL;
   1362     }
   1363     return ptr;
   1364 }
   1365 
   1366 /* Throw out of memory in case of error */
   1367 void *js_mallocz(JSContext *ctx, size_t size)
   1368 {
   1369     void *ptr;
   1370     ptr = js_mallocz_rt(ctx->rt, size);
   1371     if (unlikely(!ptr)) {
   1372         JS_ThrowOutOfMemory(ctx);
   1373         return NULL;
   1374     }
   1375     return ptr;
   1376 }
   1377 
   1378 void js_free(JSContext *ctx, void *ptr)
   1379 {
   1380     js_free_rt(ctx->rt, ptr);
   1381 }
   1382 
   1383 /* Throw out of memory in case of error */
   1384 void *js_realloc(JSContext *ctx, void *ptr, size_t size)
   1385 {
   1386     void *ret;
   1387     ret = js_realloc_rt(ctx->rt, ptr, size);
   1388     if (unlikely(!ret && size != 0)) {
   1389         JS_ThrowOutOfMemory(ctx);
   1390         return NULL;
   1391     }
   1392     return ret;
   1393 }
   1394 
   1395 /* store extra allocated size in *pslack if successful */
   1396 void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack)
   1397 {
   1398     void *ret;
   1399     ret = js_realloc_rt(ctx->rt, ptr, size);
   1400     if (unlikely(!ret && size != 0)) {
   1401         JS_ThrowOutOfMemory(ctx);
   1402         return NULL;
   1403     }
   1404     if (pslack) {
   1405         size_t new_size = js_malloc_usable_size_rt(ctx->rt, ret);
   1406         *pslack = (new_size > size) ? new_size - size : 0;
   1407     }
   1408     return ret;
   1409 }
   1410 
   1411 size_t js_malloc_usable_size(JSContext *ctx, const void *ptr)
   1412 {
   1413     return js_malloc_usable_size_rt(ctx->rt, ptr);
   1414 }
   1415 
   1416 /* Throw out of memory exception in case of error */
   1417 char *js_strndup(JSContext *ctx, const char *s, size_t n)
   1418 {
   1419     char *ptr;
   1420     ptr = js_malloc(ctx, n + 1);
   1421     if (ptr) {
   1422         memcpy(ptr, s, n);
   1423         ptr[n] = '\0';
   1424     }
   1425     return ptr;
   1426 }
   1427 
   1428 char *js_strdup(JSContext *ctx, const char *str)
   1429 {
   1430     return js_strndup(ctx, str, strlen(str));
   1431 }
   1432 
   1433 static no_inline int js_realloc_array(JSContext *ctx, void **parray,
   1434                                       int elem_size, int *psize, int req_size)
   1435 {
   1436     int new_size;
   1437     size_t slack;
   1438     void *new_array;
   1439     /* XXX: potential arithmetic overflow */
   1440     new_size = max_int(req_size, *psize * 3 / 2);
   1441     new_array = js_realloc2(ctx, *parray, new_size * elem_size, &slack);
   1442     if (!new_array)
   1443         return -1;
   1444     new_size += slack / elem_size;
   1445     *psize = new_size;
   1446     *parray = new_array;
   1447     return 0;
   1448 }
   1449 
   1450 /* resize the array and update its size if req_size > *psize */
   1451 static inline int js_resize_array(JSContext *ctx, void **parray, int elem_size,
   1452                                   int *psize, int req_size)
   1453 {
   1454     if (unlikely(req_size > *psize))
   1455         return js_realloc_array(ctx, parray, elem_size, psize, req_size);
   1456     else
   1457         return 0;
   1458 }
   1459 
   1460 static inline void js_dbuf_init(JSContext *ctx, DynBuf *s)
   1461 {
   1462     dbuf_init2(s, ctx->rt, (DynBufReallocFunc *)js_realloc_rt);
   1463 }
   1464 
   1465 static inline int is_digit(int c) {
   1466     return c >= '0' && c <= '9';
   1467 }
   1468 
   1469 static inline int string_get(const JSString *p, int idx) {
   1470     return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx];
   1471 }
   1472 
   1473 typedef struct JSClassShortDef {
   1474     JSAtom class_name;
   1475     JSClassFinalizer *finalizer;
   1476     JSClassGCMark *gc_mark;
   1477 } JSClassShortDef;
   1478 
   1479 static JSClassShortDef const js_std_class_def[] = {
   1480     { JS_ATOM_Object, NULL, NULL },                             /* JS_CLASS_OBJECT */
   1481     { JS_ATOM_Array, js_array_finalizer, js_array_mark },       /* JS_CLASS_ARRAY */
   1482     { JS_ATOM_Error, NULL, NULL }, /* JS_CLASS_ERROR */
   1483     { JS_ATOM_Number, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_NUMBER */
   1484     { JS_ATOM_String, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_STRING */
   1485     { JS_ATOM_Boolean, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BOOLEAN */
   1486     { JS_ATOM_Symbol, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_SYMBOL */
   1487     { JS_ATOM_Arguments, js_array_finalizer, js_array_mark },   /* JS_CLASS_ARGUMENTS */
   1488     { JS_ATOM_Arguments, NULL, NULL },                          /* JS_CLASS_MAPPED_ARGUMENTS */
   1489     { JS_ATOM_Date, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_DATE */
   1490     { JS_ATOM_Object, NULL, NULL },                             /* JS_CLASS_MODULE_NS */
   1491     { JS_ATOM_Function, js_c_function_finalizer, js_c_function_mark }, /* JS_CLASS_C_FUNCTION */
   1492     { JS_ATOM_Function, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_BYTECODE_FUNCTION */
   1493     { JS_ATOM_Function, js_bound_function_finalizer, js_bound_function_mark }, /* JS_CLASS_BOUND_FUNCTION */
   1494     { JS_ATOM_Function, js_c_function_data_finalizer, js_c_function_data_mark }, /* JS_CLASS_C_FUNCTION_DATA */
   1495     { JS_ATOM_GeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark },  /* JS_CLASS_GENERATOR_FUNCTION */
   1496     { JS_ATOM_ForInIterator, js_for_in_iterator_finalizer, js_for_in_iterator_mark },      /* JS_CLASS_FOR_IN_ITERATOR */
   1497     { JS_ATOM_RegExp, js_regexp_finalizer, NULL },                              /* JS_CLASS_REGEXP */
   1498     { JS_ATOM_ArrayBuffer, js_array_buffer_finalizer, NULL },                   /* JS_CLASS_ARRAY_BUFFER */
   1499     { JS_ATOM_SharedArrayBuffer, js_array_buffer_finalizer, NULL },             /* JS_CLASS_SHARED_ARRAY_BUFFER */
   1500     { JS_ATOM_Uint8ClampedArray, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8C_ARRAY */
   1501     { JS_ATOM_Int8Array, js_typed_array_finalizer, js_typed_array_mark },       /* JS_CLASS_INT8_ARRAY */
   1502     { JS_ATOM_Uint8Array, js_typed_array_finalizer, js_typed_array_mark },      /* JS_CLASS_UINT8_ARRAY */
   1503     { JS_ATOM_Int16Array, js_typed_array_finalizer, js_typed_array_mark },      /* JS_CLASS_INT16_ARRAY */
   1504     { JS_ATOM_Uint16Array, js_typed_array_finalizer, js_typed_array_mark },     /* JS_CLASS_UINT16_ARRAY */
   1505     { JS_ATOM_Int32Array, js_typed_array_finalizer, js_typed_array_mark },      /* JS_CLASS_INT32_ARRAY */
   1506     { JS_ATOM_Uint32Array, js_typed_array_finalizer, js_typed_array_mark },     /* JS_CLASS_UINT32_ARRAY */
   1507     { JS_ATOM_BigInt64Array, js_typed_array_finalizer, js_typed_array_mark },   /* JS_CLASS_BIG_INT64_ARRAY */
   1508     { JS_ATOM_BigUint64Array, js_typed_array_finalizer, js_typed_array_mark },  /* JS_CLASS_BIG_UINT64_ARRAY */
   1509     { JS_ATOM_Float32Array, js_typed_array_finalizer, js_typed_array_mark },    /* JS_CLASS_FLOAT32_ARRAY */
   1510     { JS_ATOM_Float64Array, js_typed_array_finalizer, js_typed_array_mark },    /* JS_CLASS_FLOAT64_ARRAY */
   1511     { JS_ATOM_DataView, js_typed_array_finalizer, js_typed_array_mark },        /* JS_CLASS_DATAVIEW */
   1512     { JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark },      /* JS_CLASS_BIG_INT */
   1513 #ifdef CONFIG_BIGNUM
   1514     { JS_ATOM_BigFloat, js_object_data_finalizer, js_object_data_mark },    /* JS_CLASS_BIG_FLOAT */
   1515     { JS_ATOM_BigFloatEnv, js_float_env_finalizer, NULL },      /* JS_CLASS_FLOAT_ENV */
   1516     { JS_ATOM_BigDecimal, js_object_data_finalizer, js_object_data_mark },    /* JS_CLASS_BIG_DECIMAL */
   1517     { JS_ATOM_OperatorSet, js_operator_set_finalizer, js_operator_set_mark },    /* JS_CLASS_OPERATOR_SET */
   1518 #endif
   1519     { JS_ATOM_Map, js_map_finalizer, js_map_mark },             /* JS_CLASS_MAP */
   1520     { JS_ATOM_Set, js_map_finalizer, js_map_mark },             /* JS_CLASS_SET */
   1521     { JS_ATOM_WeakMap, js_map_finalizer, js_map_mark },         /* JS_CLASS_WEAKMAP */
   1522     { JS_ATOM_WeakSet, js_map_finalizer, js_map_mark },         /* JS_CLASS_WEAKSET */
   1523     { JS_ATOM_Map_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_MAP_ITERATOR */
   1524     { JS_ATOM_Set_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_SET_ITERATOR */
   1525     { JS_ATOM_Array_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_ARRAY_ITERATOR */
   1526     { JS_ATOM_String_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_STRING_ITERATOR */
   1527     { JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */
   1528     { JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /* JS_CLASS_GENERATOR */
   1529 };
   1530 
   1531 static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab,
   1532                             int start, int count)
   1533 {
   1534     JSClassDef cm_s, *cm = &cm_s;
   1535     int i, class_id;
   1536 
   1537     for(i = 0; i < count; i++) {
   1538         class_id = i + start;
   1539         memset(cm, 0, sizeof(*cm));
   1540         cm->finalizer = tab[i].finalizer;
   1541         cm->gc_mark = tab[i].gc_mark;
   1542         if (JS_NewClass1(rt, class_id, cm, tab[i].class_name) < 0)
   1543             return -1;
   1544     }
   1545     return 0;
   1546 }
   1547 
   1548 static JSValue JS_ThrowUnsupportedOperation(JSContext *ctx)
   1549 {
   1550     return JS_ThrowTypeError(ctx, "unsupported operation");
   1551 }
   1552 
   1553 static JSValue invalid_to_string(JSContext *ctx, JSValueConst val)
   1554 {
   1555     return JS_ThrowUnsupportedOperation(ctx);
   1556 }
   1557 
   1558 static JSValue invalid_from_string(JSContext *ctx, const char *buf,
   1559                                    int radix, int flags, slimb_t *pexponent)
   1560 {
   1561     return JS_NAN;
   1562 }
   1563 
   1564 static int invalid_unary_arith(JSContext *ctx,
   1565                                JSValue *pres, OPCodeEnum op, JSValue op1)
   1566 {
   1567     JS_FreeValue(ctx, op1);
   1568     JS_ThrowUnsupportedOperation(ctx);
   1569     return -1;
   1570 }
   1571 
   1572 static int invalid_binary_arith(JSContext *ctx, OPCodeEnum op,
   1573                                 JSValue *pres, JSValue op1, JSValue op2)
   1574 {
   1575     JS_FreeValue(ctx, op1);
   1576     JS_FreeValue(ctx, op2);
   1577     JS_ThrowUnsupportedOperation(ctx);
   1578     return -1;
   1579 }
   1580 
   1581 static JSValue invalid_mul_pow10_to_float64(JSContext *ctx, const bf_t *a,
   1582                                             int64_t exponent)
   1583 {
   1584     return JS_ThrowUnsupportedOperation(ctx);
   1585 }
   1586 
   1587 static int invalid_mul_pow10(JSContext *ctx, JSValue *sp)
   1588 {
   1589     JS_ThrowUnsupportedOperation(ctx);
   1590     return -1;
   1591 }
   1592 
   1593 static void set_dummy_numeric_ops(JSNumericOperations *ops)
   1594 {
   1595     ops->to_string = invalid_to_string;
   1596     ops->from_string = invalid_from_string;
   1597     ops->unary_arith = invalid_unary_arith;
   1598     ops->binary_arith = invalid_binary_arith;
   1599     ops->mul_pow10_to_float64 = invalid_mul_pow10_to_float64;
   1600     ops->mul_pow10 = invalid_mul_pow10;
   1601 }
   1602 
   1603 #if !defined(CONFIG_STACK_CHECK)
   1604 /* no stack limitation */
   1605 static inline uintptr_t js_get_stack_pointer(void)
   1606 {
   1607     return 0;
   1608 }
   1609 
   1610 static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)
   1611 {
   1612     return FALSE;
   1613 }
   1614 #else
   1615 /* Note: OS and CPU dependent */
   1616 static inline uintptr_t js_get_stack_pointer(void)
   1617 {
   1618     return (uintptr_t)__builtin_frame_address(0);
   1619 }
   1620 
   1621 static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)
   1622 {
   1623     uintptr_t sp;
   1624     sp = js_get_stack_pointer() - alloca_size;
   1625     return unlikely(sp < rt->stack_limit);
   1626 }
   1627 #endif
   1628 
   1629 JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque)
   1630 {
   1631     JSRuntime *rt;
   1632     JSMallocState ms;
   1633 
   1634     memset(&ms, 0, sizeof(ms));
   1635     ms.opaque = opaque;
   1636     ms.malloc_limit = -1;
   1637 
   1638     rt = mf->js_malloc(&ms, sizeof(JSRuntime));
   1639     if (!rt)
   1640         return NULL;
   1641     memset(rt, 0, sizeof(*rt));
   1642     rt->mf = *mf;
   1643     if (!rt->mf.js_malloc_usable_size) {
   1644         /* use dummy function if none provided */
   1645         rt->mf.js_malloc_usable_size = js_malloc_usable_size_unknown;
   1646     }
   1647     rt->malloc_state = ms;
   1648     rt->malloc_gc_threshold = 256 * 1024;
   1649 
   1650     bf_context_init(&rt->bf_ctx, js_bf_realloc, rt);
   1651     set_dummy_numeric_ops(&rt->bigint_ops);
   1652 #ifdef CONFIG_BIGNUM
   1653     set_dummy_numeric_ops(&rt->bigfloat_ops);
   1654     set_dummy_numeric_ops(&rt->bigdecimal_ops);
   1655 #endif
   1656 
   1657     init_list_head(&rt->context_list);
   1658     init_list_head(&rt->gc_obj_list);
   1659     init_list_head(&rt->gc_zero_ref_count_list);
   1660     rt->gc_phase = JS_GC_PHASE_NONE;
   1661 
   1662 #ifdef DUMP_LEAKS
   1663     init_list_head(&rt->string_list);
   1664 #endif
   1665     init_list_head(&rt->job_list);
   1666 
   1667     if (JS_InitAtoms(rt))
   1668         goto fail;
   1669 
   1670     /* create the object, array and function classes */
   1671     if (init_class_range(rt, js_std_class_def, JS_CLASS_OBJECT,
   1672                          countof(js_std_class_def)) < 0)
   1673         goto fail;
   1674     rt->class_array[JS_CLASS_ARGUMENTS].exotic = &js_arguments_exotic_methods;
   1675     rt->class_array[JS_CLASS_STRING].exotic = &js_string_exotic_methods;
   1676     rt->class_array[JS_CLASS_MODULE_NS].exotic = &js_module_ns_exotic_methods;
   1677 
   1678     rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function;
   1679     rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_c_function_data_call;
   1680     rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function;
   1681     rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_generator_function_call;
   1682     if (init_shape_hash(rt))
   1683         goto fail;
   1684 
   1685     rt->stack_size = JS_DEFAULT_STACK_SIZE;
   1686     JS_UpdateStackTop(rt);
   1687 
   1688     rt->current_exception = JS_UNINITIALIZED;
   1689 
   1690     return rt;
   1691  fail:
   1692     JS_FreeRuntime(rt);
   1693     return NULL;
   1694 }
   1695 
   1696 void *JS_GetRuntimeOpaque(JSRuntime *rt)
   1697 {
   1698     return rt->user_opaque;
   1699 }
   1700 
   1701 void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque)
   1702 {
   1703     rt->user_opaque = opaque;
   1704 }
   1705 
   1706 /* default memory allocation functions with memory limitation */
   1707 static size_t js_def_malloc_usable_size(const void *ptr)
   1708 {
   1709 #if defined(__APPLE__)
   1710     return malloc_size(ptr);
   1711 #elif defined(_WIN32)
   1712     return _msize((void *)ptr);
   1713 #elif defined(EMSCRIPTEN)
   1714     return 0;
   1715 #elif defined(__linux__) || defined(__GLIBC__)
   1716     return malloc_usable_size((void *)ptr);
   1717 #else
   1718     /* change this to `return 0;` if compilation fails */
   1719     return malloc_usable_size((void *)ptr);
   1720 #endif
   1721 }
   1722 
   1723 static void *js_def_malloc(JSMallocState *s, size_t size)
   1724 {
   1725     void *ptr;
   1726 
   1727     /* Do not allocate zero bytes: behavior is platform dependent */
   1728     assert(size != 0);
   1729 
   1730     if (unlikely(s->malloc_size + size > s->malloc_limit))
   1731         return NULL;
   1732 
   1733     ptr = malloc(size);
   1734     if (!ptr)
   1735         return NULL;
   1736 
   1737     s->malloc_count++;
   1738     s->malloc_size += js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
   1739     return ptr;
   1740 }
   1741 
   1742 static void js_def_free(JSMallocState *s, void *ptr)
   1743 {
   1744     if (!ptr)
   1745         return;
   1746 
   1747     s->malloc_count--;
   1748     s->malloc_size -= js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
   1749     free(ptr);
   1750 }
   1751 
   1752 static void *js_def_realloc(JSMallocState *s, void *ptr, size_t size)
   1753 {
   1754     size_t old_size;
   1755 
   1756     if (!ptr) {
   1757         if (size == 0)
   1758             return NULL;
   1759         return js_def_malloc(s, size);
   1760     }
   1761     old_size = js_def_malloc_usable_size(ptr);
   1762     if (size == 0) {
   1763         s->malloc_count--;
   1764         s->malloc_size -= old_size + MALLOC_OVERHEAD;
   1765         free(ptr);
   1766         return NULL;
   1767     }
   1768     if (s->malloc_size + size - old_size > s->malloc_limit)
   1769         return NULL;
   1770 
   1771     ptr = realloc(ptr, size);
   1772     if (!ptr)
   1773         return NULL;
   1774 
   1775     s->malloc_size += js_def_malloc_usable_size(ptr) - old_size;
   1776     return ptr;
   1777 }
   1778 
   1779 static const JSMallocFunctions def_malloc_funcs = {
   1780     js_def_malloc,
   1781     js_def_free,
   1782     js_def_realloc,
   1783     js_def_malloc_usable_size,
   1784 };
   1785 
   1786 JSRuntime *JS_NewRuntime(void)
   1787 {
   1788     return JS_NewRuntime2(&def_malloc_funcs, NULL);
   1789 }
   1790 
   1791 void JS_SetMemoryLimit(JSRuntime *rt, size_t limit)
   1792 {
   1793     rt->malloc_state.malloc_limit = limit;
   1794 }
   1795 
   1796 /* use -1 to disable automatic GC */
   1797 void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold)
   1798 {
   1799     rt->malloc_gc_threshold = gc_threshold;
   1800 }
   1801 
   1802 #define malloc(s) malloc_is_forbidden(s)
   1803 #define free(p) free_is_forbidden(p)
   1804 #define realloc(p,s) realloc_is_forbidden(p,s)
   1805 
   1806 void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque)
   1807 {
   1808     rt->interrupt_handler = cb;
   1809     rt->interrupt_opaque = opaque;
   1810 }
   1811 
   1812 void JS_SetCanBlock(JSRuntime *rt, BOOL can_block)
   1813 {
   1814     rt->can_block = can_block;
   1815 }
   1816 
   1817 void JS_SetSharedArrayBufferFunctions(JSRuntime *rt,
   1818                                       const JSSharedArrayBufferFunctions *sf)
   1819 {
   1820     rt->sab_funcs = *sf;
   1821 }
   1822 
   1823 /* return 0 if OK, < 0 if exception */
   1824 int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func,
   1825                   int argc, JSValueConst *argv)
   1826 {
   1827     JSRuntime *rt = ctx->rt;
   1828     JSJobEntry *e;
   1829     int i;
   1830 
   1831     e = js_malloc(ctx, sizeof(*e) + argc * sizeof(JSValue));
   1832     if (!e)
   1833         return -1;
   1834     e->ctx = ctx;
   1835     e->job_func = job_func;
   1836     e->argc = argc;
   1837     for(i = 0; i < argc; i++) {
   1838         e->argv[i] = JS_DupValue(ctx, argv[i]);
   1839     }
   1840     list_add_tail(&e->link, &rt->job_list);
   1841     return 0;
   1842 }
   1843 
   1844 BOOL JS_IsJobPending(JSRuntime *rt)
   1845 {
   1846     return !list_empty(&rt->job_list);
   1847 }
   1848 
   1849 /* return < 0 if exception, 0 if no job pending, 1 if a job was
   1850    executed successfully. the context of the job is stored in '*pctx' */
   1851 int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx)
   1852 {
   1853     JSContext *ctx;
   1854     JSJobEntry *e;
   1855     JSValue res;
   1856     int i, ret;
   1857 
   1858     if (list_empty(&rt->job_list)) {
   1859         *pctx = NULL;
   1860         return 0;
   1861     }
   1862 
   1863     /* get the first pending job and execute it */
   1864     e = list_entry(rt->job_list.next, JSJobEntry, link);
   1865     list_del(&e->link);
   1866     ctx = e->ctx;
   1867     res = e->job_func(e->ctx, e->argc, (JSValueConst *)e->argv);
   1868     for(i = 0; i < e->argc; i++)
   1869         JS_FreeValue(ctx, e->argv[i]);
   1870     if (JS_IsException(res))
   1871         ret = -1;
   1872     else
   1873         ret = 1;
   1874     JS_FreeValue(ctx, res);
   1875     js_free(ctx, e);
   1876     *pctx = ctx;
   1877     return ret;
   1878 }
   1879 
   1880 static inline uint32_t atom_get_free(const JSAtomStruct *p)
   1881 {
   1882     return (uintptr_t)p >> 1;
   1883 }
   1884 
   1885 static inline BOOL atom_is_free(const JSAtomStruct *p)
   1886 {
   1887     return (uintptr_t)p & 1;
   1888 }
   1889 
   1890 static inline JSAtomStruct *atom_set_free(uint32_t v)
   1891 {
   1892     return (JSAtomStruct *)(((uintptr_t)v << 1) | 1);
   1893 }
   1894 
   1895 /* Note: the string contents are uninitialized */
   1896 static JSString *js_alloc_string_rt(JSRuntime *rt, int max_len, int is_wide_char)
   1897 {
   1898     JSString *str;
   1899     str = js_malloc_rt(rt, sizeof(JSString) + (max_len << is_wide_char) + 1 - is_wide_char);
   1900     if (unlikely(!str))
   1901         return NULL;
   1902     str->header.ref_count = 1;
   1903     str->is_wide_char = is_wide_char;
   1904     str->len = max_len;
   1905     str->atom_type = 0;
   1906     str->hash = 0;          /* optional but costless */
   1907     str->hash_next = 0;     /* optional */
   1908 #ifdef DUMP_LEAKS
   1909     list_add_tail(&str->link, &rt->string_list);
   1910 #endif
   1911     return str;
   1912 }
   1913 
   1914 static JSString *js_alloc_string(JSContext *ctx, int max_len, int is_wide_char)
   1915 {
   1916     JSString *p;
   1917     p = js_alloc_string_rt(ctx->rt, max_len, is_wide_char);
   1918     if (unlikely(!p)) {
   1919         JS_ThrowOutOfMemory(ctx);
   1920         return NULL;
   1921     }
   1922     return p;
   1923 }
   1924 
   1925 /* same as JS_FreeValueRT() but faster */
   1926 static inline void js_free_string(JSRuntime *rt, JSString *str)
   1927 {
   1928     if (--str->header.ref_count <= 0) {
   1929         if (str->atom_type) {
   1930             JS_FreeAtomStruct(rt, str);
   1931         } else {
   1932 #ifdef DUMP_LEAKS
   1933             list_del(&str->link);
   1934 #endif
   1935             js_free_rt(rt, str);
   1936         }
   1937     }
   1938 }
   1939 
   1940 void JS_SetRuntimeInfo(JSRuntime *rt, const char *s)
   1941 {
   1942     if (rt)
   1943         rt->rt_info = s;
   1944 }
   1945 
   1946 void JS_FreeRuntime(JSRuntime *rt)
   1947 {
   1948     struct list_head *el, *el1;
   1949     int i;
   1950 
   1951     JS_FreeValueRT(rt, rt->current_exception);
   1952 
   1953     list_for_each_safe(el, el1, &rt->job_list) {
   1954         JSJobEntry *e = list_entry(el, JSJobEntry, link);
   1955         for(i = 0; i < e->argc; i++)
   1956             JS_FreeValueRT(rt, e->argv[i]);
   1957         js_free_rt(rt, e);
   1958     }
   1959     init_list_head(&rt->job_list);
   1960 
   1961     JS_RunGC(rt);
   1962 
   1963 #ifdef DUMP_LEAKS
   1964     /* leaking objects */
   1965     {
   1966         BOOL header_done;
   1967         JSGCObjectHeader *p;
   1968         int count;
   1969 
   1970         /* remove the internal refcounts to display only the object
   1971            referenced externally */
   1972         list_for_each(el, &rt->gc_obj_list) {
   1973             p = list_entry(el, JSGCObjectHeader, link);
   1974             p->mark = 0;
   1975         }
   1976         gc_decref(rt);
   1977 
   1978         header_done = FALSE;
   1979         list_for_each(el, &rt->gc_obj_list) {
   1980             p = list_entry(el, JSGCObjectHeader, link);
   1981             if (p->ref_count != 0) {
   1982                 if (!header_done) {
   1983                     printf("Object leaks:\n");
   1984                     JS_DumpObjectHeader(rt);
   1985                     header_done = TRUE;
   1986                 }
   1987                 JS_DumpGCObject(rt, p);
   1988             }
   1989         }
   1990 
   1991         count = 0;
   1992         list_for_each(el, &rt->gc_obj_list) {
   1993             p = list_entry(el, JSGCObjectHeader, link);
   1994             if (p->ref_count == 0) {
   1995                 count++;
   1996             }
   1997         }
   1998         if (count != 0)
   1999             printf("Secondary object leaks: %d\n", count);
   2000     }
   2001 #endif
   2002     assert(list_empty(&rt->gc_obj_list));
   2003 
   2004     /* free the classes */
   2005     for(i = 0; i < rt->class_count; i++) {
   2006         JSClass *cl = &rt->class_array[i];
   2007         if (cl->class_id != 0) {
   2008             JS_FreeAtomRT(rt, cl->class_name);
   2009         }
   2010     }
   2011     js_free_rt(rt, rt->class_array);
   2012 
   2013     bf_context_end(&rt->bf_ctx);
   2014 
   2015 #ifdef DUMP_LEAKS
   2016     /* only the atoms defined in JS_InitAtoms() should be left */
   2017     {
   2018         BOOL header_done = FALSE;
   2019 
   2020         for(i = 0; i < rt->atom_size; i++) {
   2021             JSAtomStruct *p = rt->atom_array[i];
   2022             if (!atom_is_free(p) /* && p->str*/) {
   2023                 if (i >= JS_ATOM_END || p->header.ref_count != 1) {
   2024                     if (!header_done) {
   2025                         header_done = TRUE;
   2026                         if (rt->rt_info) {
   2027                             printf("%s:1: atom leakage:", rt->rt_info);
   2028                         } else {
   2029                             printf("Atom leaks:\n"
   2030                                    "    %6s %6s %s\n",
   2031                                    "ID", "REFCNT", "NAME");
   2032                         }
   2033                     }
   2034                     if (rt->rt_info) {
   2035                         printf(" ");
   2036                     } else {
   2037                         printf("    %6u %6u ", i, p->header.ref_count);
   2038                     }
   2039                     switch (p->atom_type) {
   2040                     case JS_ATOM_TYPE_STRING:
   2041                         JS_DumpString(rt, p);
   2042                         break;
   2043                     case JS_ATOM_TYPE_GLOBAL_SYMBOL:
   2044                         printf("Symbol.for(");
   2045                         JS_DumpString(rt, p);
   2046                         printf(")");
   2047                         break;
   2048                     case JS_ATOM_TYPE_SYMBOL:
   2049                         if (p->hash == JS_ATOM_HASH_SYMBOL) {
   2050                             printf("Symbol(");
   2051                             JS_DumpString(rt, p);
   2052                             printf(")");
   2053                         } else {
   2054                             printf("Private(");
   2055                             JS_DumpString(rt, p);
   2056                             printf(")");
   2057                         }
   2058                         break;
   2059                     }
   2060                     if (rt->rt_info) {
   2061                         printf(":%u", p->header.ref_count);
   2062                     } else {
   2063                         printf("\n");
   2064                     }
   2065                 }
   2066             }
   2067         }
   2068         if (rt->rt_info && header_done)
   2069             printf("\n");
   2070     }
   2071 #endif
   2072 
   2073     /* free the atoms */
   2074     for(i = 0; i < rt->atom_size; i++) {
   2075         JSAtomStruct *p = rt->atom_array[i];
   2076         if (!atom_is_free(p)) {
   2077 #ifdef DUMP_LEAKS
   2078             list_del(&p->link);
   2079 #endif
   2080             js_free_rt(rt, p);
   2081         }
   2082     }
   2083     js_free_rt(rt, rt->atom_array);
   2084     js_free_rt(rt, rt->atom_hash);
   2085     js_free_rt(rt, rt->shape_hash);
   2086 #ifdef DUMP_LEAKS
   2087     if (!list_empty(&rt->string_list)) {
   2088         if (rt->rt_info) {
   2089             printf("%s:1: string leakage:", rt->rt_info);
   2090         } else {
   2091             printf("String leaks:\n"
   2092                    "    %6s %s\n",
   2093                    "REFCNT", "VALUE");
   2094         }
   2095         list_for_each_safe(el, el1, &rt->string_list) {
   2096             JSString *str = list_entry(el, JSString, link);
   2097             if (rt->rt_info) {
   2098                 printf(" ");
   2099             } else {
   2100                 printf("    %6u ", str->header.ref_count);
   2101             }
   2102             JS_DumpString(rt, str);
   2103             if (rt->rt_info) {
   2104                 printf(":%u", str->header.ref_count);
   2105             } else {
   2106                 printf("\n");
   2107             }
   2108             list_del(&str->link);
   2109             js_free_rt(rt, str);
   2110         }
   2111         if (rt->rt_info)
   2112             printf("\n");
   2113     }
   2114     {
   2115         JSMallocState *s = &rt->malloc_state;
   2116         if (s->malloc_count > 1) {
   2117             if (rt->rt_info)
   2118                 printf("%s:1: ", rt->rt_info);
   2119             printf("Memory leak: %"PRIu64" bytes lost in %"PRIu64" block%s\n",
   2120                    (uint64_t)(s->malloc_size - sizeof(JSRuntime)),
   2121                    (uint64_t)(s->malloc_count - 1), &"s"[s->malloc_count == 2]);
   2122         }
   2123     }
   2124 #endif
   2125 
   2126     {
   2127         JSMallocState ms = rt->malloc_state;
   2128         rt->mf.js_free(&ms, rt);
   2129     }
   2130 }
   2131 
   2132 JSContext *JS_NewContextRaw(JSRuntime *rt)
   2133 {
   2134     JSContext *ctx;
   2135     int i;
   2136 
   2137     ctx = js_mallocz_rt(rt, sizeof(JSContext));
   2138     if (!ctx)
   2139         return NULL;
   2140     ctx->header.ref_count = 1;
   2141     add_gc_object(rt, &ctx->header, JS_GC_OBJ_TYPE_JS_CONTEXT);
   2142 
   2143     ctx->class_proto = js_malloc_rt(rt, sizeof(ctx->class_proto[0]) *
   2144                                     rt->class_count);
   2145     if (!ctx->class_proto) {
   2146         js_free_rt(rt, ctx);
   2147         return NULL;
   2148     }
   2149     ctx->rt = rt;
   2150     list_add_tail(&ctx->link, &rt->context_list);
   2151     ctx->bf_ctx = &rt->bf_ctx;
   2152 #ifdef CONFIG_BIGNUM
   2153     ctx->fp_env.prec = 113;
   2154     ctx->fp_env.flags = bf_set_exp_bits(15) | BF_RNDN | BF_FLAG_SUBNORMAL;
   2155 #endif
   2156     for(i = 0; i < rt->class_count; i++)
   2157         ctx->class_proto[i] = JS_NULL;
   2158     ctx->array_ctor = JS_NULL;
   2159     ctx->regexp_ctor = JS_NULL;
   2160     ctx->promise_ctor = JS_NULL;
   2161     init_list_head(&ctx->loaded_modules);
   2162 
   2163     JS_AddIntrinsicBasicObjects(ctx);
   2164     return ctx;
   2165 }
   2166 
   2167 JSContext *JS_NewContext(JSRuntime *rt)
   2168 {
   2169     JSContext *ctx;
   2170 
   2171     ctx = JS_NewContextRaw(rt);
   2172     if (!ctx)
   2173         return NULL;
   2174 
   2175     JS_AddIntrinsicBaseObjects(ctx);
   2176     JS_AddIntrinsicDate(ctx);
   2177     JS_AddIntrinsicEval(ctx);
   2178     JS_AddIntrinsicStringNormalize(ctx);
   2179     JS_AddIntrinsicRegExp(ctx);
   2180     JS_AddIntrinsicJSON(ctx);
   2181     JS_AddIntrinsicProxy(ctx);
   2182     JS_AddIntrinsicMapSet(ctx);
   2183     JS_AddIntrinsicTypedArrays(ctx);
   2184     JS_AddIntrinsicPromise(ctx);
   2185     JS_AddIntrinsicBigInt(ctx);
   2186     return ctx;
   2187 }
   2188 
   2189 void *JS_GetContextOpaque(JSContext *ctx)
   2190 {
   2191     return ctx->user_opaque;
   2192 }
   2193 
   2194 void JS_SetContextOpaque(JSContext *ctx, void *opaque)
   2195 {
   2196     ctx->user_opaque = opaque;
   2197 }
   2198 
   2199 /* set the new value and free the old value after (freeing the value
   2200    can reallocate the object data) */
   2201 static inline void set_value(JSContext *ctx, JSValue *pval, JSValue new_val)
   2202 {
   2203     JSValue old_val;
   2204     old_val = *pval;
   2205     *pval = new_val;
   2206     JS_FreeValue(ctx, old_val);
   2207 }
   2208 
   2209 void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj)
   2210 {
   2211     JSRuntime *rt = ctx->rt;
   2212     assert(class_id < rt->class_count);
   2213     set_value(ctx, &ctx->class_proto[class_id], obj);
   2214 }
   2215 
   2216 JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id)
   2217 {
   2218     JSRuntime *rt = ctx->rt;
   2219     assert(class_id < rt->class_count);
   2220     return JS_DupValue(ctx, ctx->class_proto[class_id]);
   2221 }
   2222 
   2223 typedef enum JSFreeModuleEnum {
   2224     JS_FREE_MODULE_ALL,
   2225     JS_FREE_MODULE_NOT_RESOLVED,
   2226 } JSFreeModuleEnum;
   2227 
   2228 /* XXX: would be more efficient with separate module lists */
   2229 static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag)
   2230 {
   2231     struct list_head *el, *el1;
   2232     list_for_each_safe(el, el1, &ctx->loaded_modules) {
   2233         JSModuleDef *m = list_entry(el, JSModuleDef, link);
   2234         if (flag == JS_FREE_MODULE_ALL ||
   2235             (flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved)) {
   2236             js_free_module_def(ctx, m);
   2237         }
   2238     }
   2239 }
   2240 
   2241 JSContext *JS_DupContext(JSContext *ctx)
   2242 {
   2243     ctx->header.ref_count++;
   2244     return ctx;
   2245 }
   2246 
   2247 /* used by the GC */
   2248 static void JS_MarkContext(JSRuntime *rt, JSContext *ctx,
   2249                            JS_MarkFunc *mark_func)
   2250 {
   2251     int i;
   2252     struct list_head *el;
   2253 
   2254     /* modules are not seen by the GC, so we directly mark the objects
   2255        referenced by each module */
   2256     list_for_each(el, &ctx->loaded_modules) {
   2257         JSModuleDef *m = list_entry(el, JSModuleDef, link);
   2258         js_mark_module_def(rt, m, mark_func);
   2259     }
   2260 
   2261     JS_MarkValue(rt, ctx->global_obj, mark_func);
   2262     JS_MarkValue(rt, ctx->global_var_obj, mark_func);
   2263 
   2264     JS_MarkValue(rt, ctx->throw_type_error, mark_func);
   2265     JS_MarkValue(rt, ctx->eval_obj, mark_func);
   2266 
   2267     JS_MarkValue(rt, ctx->array_proto_values, mark_func);
   2268     for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
   2269         JS_MarkValue(rt, ctx->native_error_proto[i], mark_func);
   2270     }
   2271     for(i = 0; i < rt->class_count; i++) {
   2272         JS_MarkValue(rt, ctx->class_proto[i], mark_func);
   2273     }
   2274     JS_MarkValue(rt, ctx->iterator_proto, mark_func);
   2275     JS_MarkValue(rt, ctx->async_iterator_proto, mark_func);
   2276     JS_MarkValue(rt, ctx->promise_ctor, mark_func);
   2277     JS_MarkValue(rt, ctx->array_ctor, mark_func);
   2278     JS_MarkValue(rt, ctx->regexp_ctor, mark_func);
   2279     JS_MarkValue(rt, ctx->function_ctor, mark_func);
   2280     JS_MarkValue(rt, ctx->function_proto, mark_func);
   2281 
   2282     if (ctx->array_shape)
   2283         mark_func(rt, &ctx->array_shape->header);
   2284 }
   2285 
   2286 void JS_FreeContext(JSContext *ctx)
   2287 {
   2288     JSRuntime *rt = ctx->rt;
   2289     int i;
   2290 
   2291     if (--ctx->header.ref_count > 0)
   2292         return;
   2293     assert(ctx->header.ref_count == 0);
   2294 
   2295 #ifdef DUMP_ATOMS
   2296     JS_DumpAtoms(ctx->rt);
   2297 #endif
   2298 #ifdef DUMP_SHAPES
   2299     JS_DumpShapes(ctx->rt);
   2300 #endif
   2301 #ifdef DUMP_OBJECTS
   2302     {
   2303         struct list_head *el;
   2304         JSGCObjectHeader *p;
   2305         printf("JSObjects: {\n");
   2306         JS_DumpObjectHeader(ctx->rt);
   2307         list_for_each(el, &rt->gc_obj_list) {
   2308             p = list_entry(el, JSGCObjectHeader, link);
   2309             JS_DumpGCObject(rt, p);
   2310         }
   2311         printf("}\n");
   2312     }
   2313 #endif
   2314 #ifdef DUMP_MEM
   2315     {
   2316         JSMemoryUsage stats;
   2317         JS_ComputeMemoryUsage(rt, &stats);
   2318         JS_DumpMemoryUsage(stdout, &stats, rt);
   2319     }
   2320 #endif
   2321 
   2322     js_free_modules(ctx, JS_FREE_MODULE_ALL);
   2323 
   2324     JS_FreeValue(ctx, ctx->global_obj);
   2325     JS_FreeValue(ctx, ctx->global_var_obj);
   2326 
   2327     JS_FreeValue(ctx, ctx->throw_type_error);
   2328     JS_FreeValue(ctx, ctx->eval_obj);
   2329 
   2330     JS_FreeValue(ctx, ctx->array_proto_values);
   2331     for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
   2332         JS_FreeValue(ctx, ctx->native_error_proto[i]);
   2333     }
   2334     for(i = 0; i < rt->class_count; i++) {
   2335         JS_FreeValue(ctx, ctx->class_proto[i]);
   2336     }
   2337     js_free_rt(rt, ctx->class_proto);
   2338     JS_FreeValue(ctx, ctx->iterator_proto);
   2339     JS_FreeValue(ctx, ctx->async_iterator_proto);
   2340     JS_FreeValue(ctx, ctx->promise_ctor);
   2341     JS_FreeValue(ctx, ctx->array_ctor);
   2342     JS_FreeValue(ctx, ctx->regexp_ctor);
   2343     JS_FreeValue(ctx, ctx->function_ctor);
   2344     JS_FreeValue(ctx, ctx->function_proto);
   2345 
   2346     js_free_shape_null(ctx->rt, ctx->array_shape);
   2347 
   2348     list_del(&ctx->link);
   2349     remove_gc_object(&ctx->header);
   2350     js_free_rt(ctx->rt, ctx);
   2351 }
   2352 
   2353 JSRuntime *JS_GetRuntime(JSContext *ctx)
   2354 {
   2355     return ctx->rt;
   2356 }
   2357 
   2358 static void update_stack_limit(JSRuntime *rt)
   2359 {
   2360     if (rt->stack_size == 0) {
   2361         rt->stack_limit = 0; /* no limit */
   2362     } else {
   2363         rt->stack_limit = rt->stack_top - rt->stack_size;
   2364     }
   2365 }
   2366 
   2367 void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size)
   2368 {
   2369     rt->stack_size = stack_size;
   2370     update_stack_limit(rt);
   2371 }
   2372 
   2373 void JS_UpdateStackTop(JSRuntime *rt)
   2374 {
   2375     rt->stack_top = js_get_stack_pointer();
   2376     update_stack_limit(rt);
   2377 }
   2378 
   2379 static inline BOOL is_strict_mode(JSContext *ctx)
   2380 {
   2381     JSStackFrame *sf = ctx->rt->current_stack_frame;
   2382     return (sf && (sf->js_mode & JS_MODE_STRICT));
   2383 }
   2384 
   2385 #ifdef CONFIG_BIGNUM
   2386 static inline BOOL is_math_mode(JSContext *ctx)
   2387 {
   2388     JSStackFrame *sf = ctx->rt->current_stack_frame;
   2389     return (sf && (sf->js_mode & JS_MODE_MATH));
   2390 }
   2391 #else
   2392 static inline BOOL is_math_mode(JSContext *ctx)
   2393 {
   2394     return FALSE;
   2395 }
   2396 #endif
   2397 
   2398 /* JSAtom support */
   2399 
   2400 #define JS_ATOM_TAG_INT (1U << 31)
   2401 #define JS_ATOM_MAX_INT (JS_ATOM_TAG_INT - 1)
   2402 #define JS_ATOM_MAX     ((1U << 30) - 1)
   2403 
   2404 /* return the max count from the hash size */
   2405 #define JS_ATOM_COUNT_RESIZE(n) ((n) * 2)
   2406 
   2407 static inline BOOL __JS_AtomIsConst(JSAtom v)
   2408 {
   2409 #if defined(DUMP_LEAKS) && DUMP_LEAKS > 1
   2410         return (int32_t)v <= 0;
   2411 #else
   2412         return (int32_t)v < JS_ATOM_END;
   2413 #endif
   2414 }
   2415 
   2416 static inline BOOL __JS_AtomIsTaggedInt(JSAtom v)
   2417 {
   2418     return (v & JS_ATOM_TAG_INT) != 0;
   2419 }
   2420 
   2421 static inline JSAtom __JS_AtomFromUInt32(uint32_t v)
   2422 {
   2423     return v | JS_ATOM_TAG_INT;
   2424 }
   2425 
   2426 static inline uint32_t __JS_AtomToUInt32(JSAtom atom)
   2427 {
   2428     return atom & ~JS_ATOM_TAG_INT;
   2429 }
   2430 
   2431 static inline int is_num(int c)
   2432 {
   2433     return c >= '0' && c <= '9';
   2434 }
   2435 
   2436 /* return TRUE if the string is a number n with 0 <= n <= 2^32-1 */
   2437 static inline BOOL is_num_string(uint32_t *pval, const JSString *p)
   2438 {
   2439     uint32_t n;
   2440     uint64_t n64;
   2441     int c, i, len;
   2442 
   2443     len = p->len;
   2444     if (len == 0 || len > 10)
   2445         return FALSE;
   2446     c = string_get(p, 0);
   2447     if (is_num(c)) {
   2448         if (c == '0') {
   2449             if (len != 1)
   2450                 return FALSE;
   2451             n = 0;
   2452         } else {
   2453             n = c - '0';
   2454             for(i = 1; i < len; i++) {
   2455                 c = string_get(p, i);
   2456                 if (!is_num(c))
   2457                     return FALSE;
   2458                 n64 = (uint64_t)n * 10 + (c - '0');
   2459                 if ((n64 >> 32) != 0)
   2460                     return FALSE;
   2461                 n = n64;
   2462             }
   2463         }
   2464         *pval = n;
   2465         return TRUE;
   2466     } else {
   2467         return FALSE;
   2468     }
   2469 }
   2470 
   2471 /* XXX: could use faster version ? */
   2472 static inline uint32_t hash_string8(const uint8_t *str, size_t len, uint32_t h)
   2473 {
   2474     size_t i;
   2475 
   2476     for(i = 0; i < len; i++)
   2477         h = h * 263 + str[i];
   2478     return h;
   2479 }
   2480 
   2481 static inline uint32_t hash_string16(const uint16_t *str,
   2482                                      size_t len, uint32_t h)
   2483 {
   2484     size_t i;
   2485 
   2486     for(i = 0; i < len; i++)
   2487         h = h * 263 + str[i];
   2488     return h;
   2489 }
   2490 
   2491 static uint32_t hash_string(const JSString *str, uint32_t h)
   2492 {
   2493     if (str->is_wide_char)
   2494         h = hash_string16(str->u.str16, str->len, h);
   2495     else
   2496         h = hash_string8(str->u.str8, str->len, h);
   2497     return h;
   2498 }
   2499 
   2500 static __maybe_unused void JS_DumpChar(JSRuntime *rt, int c, int sep)
   2501 {
   2502     if (c == sep || c == '\\') {
   2503         putchar('\\');
   2504         putchar(c);
   2505     } else if (c >= ' ' && c <= 126) {
   2506         putchar(c);
   2507     } else if (c == '\n') {
   2508         putchar('\\');
   2509         putchar('n');
   2510     } else {
   2511         printf("\\u%04x", c);
   2512     }
   2513 }
   2514 
   2515 static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p)
   2516 {
   2517     int i, sep;
   2518 
   2519     if (p == NULL) {
   2520         printf("<null>");
   2521         return;
   2522     }
   2523     printf("%d", p->header.ref_count);
   2524     sep = (p->header.ref_count == 1) ? '\"' : '\'';
   2525     putchar(sep);
   2526     for(i = 0; i < p->len; i++) {
   2527         JS_DumpChar(rt, string_get(p, i), sep);
   2528     }
   2529     putchar(sep);
   2530 }
   2531 
   2532 static __maybe_unused void JS_DumpAtoms(JSRuntime *rt)
   2533 {
   2534     JSAtomStruct *p;
   2535     int h, i;
   2536     /* This only dumps hashed atoms, not JS_ATOM_TYPE_SYMBOL atoms */
   2537     printf("JSAtom count=%d size=%d hash_size=%d:\n",
   2538            rt->atom_count, rt->atom_size, rt->atom_hash_size);
   2539     printf("JSAtom hash table: {\n");
   2540     for(i = 0; i < rt->atom_hash_size; i++) {
   2541         h = rt->atom_hash[i];
   2542         if (h) {
   2543             printf("  %d:", i);
   2544             while (h) {
   2545                 p = rt->atom_array[h];
   2546                 printf(" ");
   2547                 JS_DumpString(rt, p);
   2548                 h = p->hash_next;
   2549             }
   2550             printf("\n");
   2551         }
   2552     }
   2553     printf("}\n");
   2554     printf("JSAtom table: {\n");
   2555     for(i = 0; i < rt->atom_size; i++) {
   2556         p = rt->atom_array[i];
   2557         if (!atom_is_free(p)) {
   2558             printf("  %d: { %d %08x ", i, p->atom_type, p->hash);
   2559             if (!(p->len == 0 && p->is_wide_char != 0))
   2560                 JS_DumpString(rt, p);
   2561             printf(" %d }\n", p->hash_next);
   2562         }
   2563     }
   2564     printf("}\n");
   2565 }
   2566 
   2567 static int JS_ResizeAtomHash(JSRuntime *rt, int new_hash_size)
   2568 {
   2569     JSAtomStruct *p;
   2570     uint32_t new_hash_mask, h, i, hash_next1, j, *new_hash;
   2571 
   2572     assert((new_hash_size & (new_hash_size - 1)) == 0); /* power of two */
   2573     new_hash_mask = new_hash_size - 1;
   2574     new_hash = js_mallocz_rt(rt, sizeof(rt->atom_hash[0]) * new_hash_size);
   2575     if (!new_hash)
   2576         return -1;
   2577     for(i = 0; i < rt->atom_hash_size; i++) {
   2578         h = rt->atom_hash[i];
   2579         while (h != 0) {
   2580             p = rt->atom_array[h];
   2581             hash_next1 = p->hash_next;
   2582             /* add in new hash table */
   2583             j = p->hash & new_hash_mask;
   2584             p->hash_next = new_hash[j];
   2585             new_hash[j] = h;
   2586             h = hash_next1;
   2587         }
   2588     }
   2589     js_free_rt(rt, rt->atom_hash);
   2590     rt->atom_hash = new_hash;
   2591     rt->atom_hash_size = new_hash_size;
   2592     rt->atom_count_resize = JS_ATOM_COUNT_RESIZE(new_hash_size);
   2593     //    JS_DumpAtoms(rt);
   2594     return 0;
   2595 }
   2596 
   2597 static int JS_InitAtoms(JSRuntime *rt)
   2598 {
   2599     int i, len, atom_type;
   2600     const char *p;
   2601 
   2602     rt->atom_hash_size = 0;
   2603     rt->atom_hash = NULL;
   2604     rt->atom_count = 0;
   2605     rt->atom_size = 0;
   2606     rt->atom_free_index = 0;
   2607     if (JS_ResizeAtomHash(rt, 256))     /* there are at least 195 predefined atoms */
   2608         return -1;
   2609 
   2610     p = js_atom_init;
   2611     for(i = 1; i < JS_ATOM_END; i++) {
   2612         if (i == JS_ATOM_Private_brand)
   2613             atom_type = JS_ATOM_TYPE_PRIVATE;
   2614         else if (i >= JS_ATOM_Symbol_toPrimitive)
   2615             atom_type = JS_ATOM_TYPE_SYMBOL;
   2616         else
   2617             atom_type = JS_ATOM_TYPE_STRING;
   2618         len = strlen(p);
   2619         if (__JS_NewAtomInit(rt, p, len, atom_type) == JS_ATOM_NULL)
   2620             return -1;
   2621         p = p + len + 1;
   2622     }
   2623     return 0;
   2624 }
   2625 
   2626 static JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v)
   2627 {
   2628     JSAtomStruct *p;
   2629 
   2630     if (!__JS_AtomIsConst(v)) {
   2631         p = rt->atom_array[v];
   2632         p->header.ref_count++;
   2633     }
   2634     return v;
   2635 }
   2636 
   2637 JSAtom JS_DupAtom(JSContext *ctx, JSAtom v)
   2638 {
   2639     JSRuntime *rt;
   2640     JSAtomStruct *p;
   2641 
   2642     if (!__JS_AtomIsConst(v)) {
   2643         rt = ctx->rt;
   2644         p = rt->atom_array[v];
   2645         p->header.ref_count++;
   2646     }
   2647     return v;
   2648 }
   2649 
   2650 static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v)
   2651 {
   2652     JSRuntime *rt;
   2653     JSAtomStruct *p;
   2654 
   2655     rt = ctx->rt;
   2656     if (__JS_AtomIsTaggedInt(v))
   2657         return JS_ATOM_KIND_STRING;
   2658     p = rt->atom_array[v];
   2659     switch(p->atom_type) {
   2660     case JS_ATOM_TYPE_STRING:
   2661         return JS_ATOM_KIND_STRING;
   2662     case JS_ATOM_TYPE_GLOBAL_SYMBOL:
   2663         return JS_ATOM_KIND_SYMBOL;
   2664     case JS_ATOM_TYPE_SYMBOL:
   2665         switch(p->hash) {
   2666         case JS_ATOM_HASH_SYMBOL:
   2667             return JS_ATOM_KIND_SYMBOL;
   2668         case JS_ATOM_HASH_PRIVATE:
   2669             return JS_ATOM_KIND_PRIVATE;
   2670         default:
   2671             abort();
   2672         }
   2673     default:
   2674         abort();
   2675     }
   2676 }
   2677 
   2678 static BOOL JS_AtomIsString(JSContext *ctx, JSAtom v)
   2679 {
   2680     return JS_AtomGetKind(ctx, v) == JS_ATOM_KIND_STRING;
   2681 }
   2682 
   2683 static JSAtom js_get_atom_index(JSRuntime *rt, JSAtomStruct *p)
   2684 {
   2685     uint32_t i = p->hash_next;  /* atom_index */
   2686     if (p->atom_type != JS_ATOM_TYPE_SYMBOL) {
   2687         JSAtomStruct *p1;
   2688 
   2689         i = rt->atom_hash[p->hash & (rt->atom_hash_size - 1)];
   2690         p1 = rt->atom_array[i];
   2691         while (p1 != p) {
   2692             assert(i != 0);
   2693             i = p1->hash_next;
   2694             p1 = rt->atom_array[i];
   2695         }
   2696     }
   2697     return i;
   2698 }
   2699 
   2700 /* string case (internal). Return JS_ATOM_NULL if error. 'str' is
   2701    freed. */
   2702 static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type)
   2703 {
   2704     uint32_t h, h1, i;
   2705     JSAtomStruct *p;
   2706     int len;
   2707 
   2708 #if 0
   2709     printf("__JS_NewAtom: ");  JS_DumpString(rt, str); printf("\n");
   2710 #endif
   2711     if (atom_type < JS_ATOM_TYPE_SYMBOL) {
   2712         /* str is not NULL */
   2713         if (str->atom_type == atom_type) {
   2714             /* str is the atom, return its index */
   2715             i = js_get_atom_index(rt, str);
   2716             /* reduce string refcount and increase atom's unless constant */
   2717             if (__JS_AtomIsConst(i))
   2718                 str->header.ref_count--;
   2719             return i;
   2720         }
   2721         /* try and locate an already registered atom */
   2722         len = str->len;
   2723         h = hash_string(str, atom_type);
   2724         h &= JS_ATOM_HASH_MASK;
   2725         h1 = h & (rt->atom_hash_size - 1);
   2726         i = rt->atom_hash[h1];
   2727         while (i != 0) {
   2728             p = rt->atom_array[i];
   2729             if (p->hash == h &&
   2730                 p->atom_type == atom_type &&
   2731                 p->len == len &&
   2732                 js_string_memcmp(p, str, len) == 0) {
   2733                 if (!__JS_AtomIsConst(i))
   2734                     p->header.ref_count++;
   2735                 goto done;
   2736             }
   2737             i = p->hash_next;
   2738         }
   2739     } else {
   2740         h1 = 0; /* avoid warning */
   2741         if (atom_type == JS_ATOM_TYPE_SYMBOL) {
   2742             h = JS_ATOM_HASH_SYMBOL;
   2743         } else {
   2744             h = JS_ATOM_HASH_PRIVATE;
   2745             atom_type = JS_ATOM_TYPE_SYMBOL;
   2746         }
   2747     }
   2748 
   2749     if (rt->atom_free_index == 0) {
   2750         /* allow new atom entries */
   2751         uint32_t new_size, start;
   2752         JSAtomStruct **new_array;
   2753 
   2754         /* alloc new with size progression 3/2:
   2755            4 6 9 13 19 28 42 63 94 141 211 316 474 711 1066 1599 2398 3597 5395 8092
   2756            preallocating space for predefined atoms (at least 195).
   2757          */
   2758         new_size = max_int(211, rt->atom_size * 3 / 2);
   2759         if (new_size > JS_ATOM_MAX)
   2760             goto fail;
   2761         /* XXX: should use realloc2 to use slack space */
   2762         new_array = js_realloc_rt(rt, rt->atom_array, sizeof(*new_array) * new_size);
   2763         if (!new_array)
   2764             goto fail;
   2765         /* Note: the atom 0 is not used */
   2766         start = rt->atom_size;
   2767         if (start == 0) {
   2768             /* JS_ATOM_NULL entry */
   2769             p = js_mallocz_rt(rt, sizeof(JSAtomStruct));
   2770             if (!p) {
   2771                 js_free_rt(rt, new_array);
   2772                 goto fail;
   2773             }
   2774             p->header.ref_count = 1;  /* not refcounted */
   2775             p->atom_type = JS_ATOM_TYPE_SYMBOL;
   2776 #ifdef DUMP_LEAKS
   2777             list_add_tail(&p->link, &rt->string_list);
   2778 #endif
   2779             new_array[0] = p;
   2780             rt->atom_count++;
   2781             start = 1;
   2782         }
   2783         rt->atom_size = new_size;
   2784         rt->atom_array = new_array;
   2785         rt->atom_free_index = start;
   2786         for(i = start; i < new_size; i++) {
   2787             uint32_t next;
   2788             if (i == (new_size - 1))
   2789                 next = 0;
   2790             else
   2791                 next = i + 1;
   2792             rt->atom_array[i] = atom_set_free(next);
   2793         }
   2794     }
   2795 
   2796     if (str) {
   2797         if (str->atom_type == 0) {
   2798             p = str;
   2799             p->atom_type = atom_type;
   2800         } else {
   2801             p = js_malloc_rt(rt, sizeof(JSString) +
   2802                              (str->len << str->is_wide_char) +
   2803                              1 - str->is_wide_char);
   2804             if (unlikely(!p))
   2805                 goto fail;
   2806             p->header.ref_count = 1;
   2807             p->is_wide_char = str->is_wide_char;
   2808             p->len = str->len;
   2809 #ifdef DUMP_LEAKS
   2810             list_add_tail(&p->link, &rt->string_list);
   2811 #endif
   2812             memcpy(p->u.str8, str->u.str8, (str->len << str->is_wide_char) +
   2813                    1 - str->is_wide_char);
   2814             js_free_string(rt, str);
   2815         }
   2816     } else {
   2817         p = js_malloc_rt(rt, sizeof(JSAtomStruct)); /* empty wide string */
   2818         if (!p)
   2819             return JS_ATOM_NULL;
   2820         p->header.ref_count = 1;
   2821         p->is_wide_char = 1;    /* Hack to represent NULL as a JSString */
   2822         p->len = 0;
   2823 #ifdef DUMP_LEAKS
   2824         list_add_tail(&p->link, &rt->string_list);
   2825 #endif
   2826     }
   2827 
   2828     /* use an already free entry */
   2829     i = rt->atom_free_index;
   2830     rt->atom_free_index = atom_get_free(rt->atom_array[i]);
   2831     rt->atom_array[i] = p;
   2832 
   2833     p->hash = h;
   2834     p->hash_next = i;   /* atom_index */
   2835     p->atom_type = atom_type;
   2836 
   2837     rt->atom_count++;
   2838 
   2839     if (atom_type != JS_ATOM_TYPE_SYMBOL) {
   2840         p->hash_next = rt->atom_hash[h1];
   2841         rt->atom_hash[h1] = i;
   2842         if (unlikely(rt->atom_count >= rt->atom_count_resize))
   2843             JS_ResizeAtomHash(rt, rt->atom_hash_size * 2);
   2844     }
   2845 
   2846     //    JS_DumpAtoms(rt);
   2847     return i;
   2848 
   2849  fail:
   2850     i = JS_ATOM_NULL;
   2851  done:
   2852     if (str)
   2853         js_free_string(rt, str);
   2854     return i;
   2855 }
   2856 
   2857 /* only works with zero terminated 8 bit strings */
   2858 static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len,
   2859                                int atom_type)
   2860 {
   2861     JSString *p;
   2862     p = js_alloc_string_rt(rt, len, 0);
   2863     if (!p)
   2864         return JS_ATOM_NULL;
   2865     memcpy(p->u.str8, str, len);
   2866     p->u.str8[len] = '\0';
   2867     return __JS_NewAtom(rt, p, atom_type);
   2868 }
   2869 
   2870 /* Warning: str must be ASCII only */
   2871 static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len,
   2872                             int atom_type)
   2873 {
   2874     uint32_t h, h1, i;
   2875     JSAtomStruct *p;
   2876 
   2877     h = hash_string8((const uint8_t *)str, len, JS_ATOM_TYPE_STRING);
   2878     h &= JS_ATOM_HASH_MASK;
   2879     h1 = h & (rt->atom_hash_size - 1);
   2880     i = rt->atom_hash[h1];
   2881     while (i != 0) {
   2882         p = rt->atom_array[i];
   2883         if (p->hash == h &&
   2884             p->atom_type == JS_ATOM_TYPE_STRING &&
   2885             p->len == len &&
   2886             p->is_wide_char == 0 &&
   2887             memcmp(p->u.str8, str, len) == 0) {
   2888             if (!__JS_AtomIsConst(i))
   2889                 p->header.ref_count++;
   2890             return i;
   2891         }
   2892         i = p->hash_next;
   2893     }
   2894     return JS_ATOM_NULL;
   2895 }
   2896 
   2897 static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p)
   2898 {
   2899 #if 0   /* JS_ATOM_NULL is not refcounted: __JS_AtomIsConst() includes 0 */
   2900     if (unlikely(i == JS_ATOM_NULL)) {
   2901         p->header.ref_count = INT32_MAX / 2;
   2902         return;
   2903     }
   2904 #endif
   2905     uint32_t i = p->hash_next;  /* atom_index */
   2906     if (p->atom_type != JS_ATOM_TYPE_SYMBOL) {
   2907         JSAtomStruct *p0, *p1;
   2908         uint32_t h0;
   2909 
   2910         h0 = p->hash & (rt->atom_hash_size - 1);
   2911         i = rt->atom_hash[h0];
   2912         p1 = rt->atom_array[i];
   2913         if (p1 == p) {
   2914             rt->atom_hash[h0] = p1->hash_next;
   2915         } else {
   2916             for(;;) {
   2917                 assert(i != 0);
   2918                 p0 = p1;
   2919                 i = p1->hash_next;
   2920                 p1 = rt->atom_array[i];
   2921                 if (p1 == p) {
   2922                     p0->hash_next = p1->hash_next;
   2923                     break;
   2924                 }
   2925             }
   2926         }
   2927     }
   2928     /* insert in free atom list */
   2929     rt->atom_array[i] = atom_set_free(rt->atom_free_index);
   2930     rt->atom_free_index = i;
   2931     /* free the string structure */
   2932 #ifdef DUMP_LEAKS
   2933     list_del(&p->link);
   2934 #endif
   2935     js_free_rt(rt, p);
   2936     rt->atom_count--;
   2937     assert(rt->atom_count >= 0);
   2938 }
   2939 
   2940 static void __JS_FreeAtom(JSRuntime *rt, uint32_t i)
   2941 {
   2942     JSAtomStruct *p;
   2943 
   2944     p = rt->atom_array[i];
   2945     if (--p->header.ref_count > 0)
   2946         return;
   2947     JS_FreeAtomStruct(rt, p);
   2948 }
   2949 
   2950 /* Warning: 'p' is freed */
   2951 static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p)
   2952 {
   2953     JSRuntime *rt = ctx->rt;
   2954     uint32_t n;
   2955     if (is_num_string(&n, p)) {
   2956         if (n <= JS_ATOM_MAX_INT) {
   2957             js_free_string(rt, p);
   2958             return __JS_AtomFromUInt32(n);
   2959         }
   2960     }
   2961     /* XXX: should generate an exception */
   2962     return __JS_NewAtom(rt, p, JS_ATOM_TYPE_STRING);
   2963 }
   2964 
   2965 /* str is UTF-8 encoded */
   2966 JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len)
   2967 {
   2968     JSValue val;
   2969 
   2970     if (len == 0 || !is_digit(*str)) {
   2971         // XXX: this will not work if UTF-8 encoded str contains non ASCII bytes
   2972         JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING);
   2973         if (atom)
   2974             return atom;
   2975     }
   2976     val = JS_NewStringLen(ctx, str, len);
   2977     if (JS_IsException(val))
   2978         return JS_ATOM_NULL;
   2979     return JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(val));
   2980 }
   2981 
   2982 JSAtom JS_NewAtom(JSContext *ctx, const char *str)
   2983 {
   2984     return JS_NewAtomLen(ctx, str, strlen(str));
   2985 }
   2986 
   2987 JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n)
   2988 {
   2989     if (n <= JS_ATOM_MAX_INT) {
   2990         return __JS_AtomFromUInt32(n);
   2991     } else {
   2992         char buf[11];
   2993         JSValue val;
   2994         snprintf(buf, sizeof(buf), "%u", n);
   2995         val = JS_NewString(ctx, buf);
   2996         if (JS_IsException(val))
   2997             return JS_ATOM_NULL;
   2998         return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val),
   2999                             JS_ATOM_TYPE_STRING);
   3000     }
   3001 }
   3002 
   3003 static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n)
   3004 {
   3005     if ((uint64_t)n <= JS_ATOM_MAX_INT) {
   3006         return __JS_AtomFromUInt32((uint32_t)n);
   3007     } else {
   3008         char buf[24];
   3009         JSValue val;
   3010         snprintf(buf, sizeof(buf), "%" PRId64 , n);
   3011         val = JS_NewString(ctx, buf);
   3012         if (JS_IsException(val))
   3013             return JS_ATOM_NULL;
   3014         return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val),
   3015                             JS_ATOM_TYPE_STRING);
   3016     }
   3017 }
   3018 
   3019 /* 'p' is freed */
   3020 static JSValue JS_NewSymbol(JSContext *ctx, JSString *p, int atom_type)
   3021 {
   3022     JSRuntime *rt = ctx->rt;
   3023     JSAtom atom;
   3024     atom = __JS_NewAtom(rt, p, atom_type);
   3025     if (atom == JS_ATOM_NULL)
   3026         return JS_ThrowOutOfMemory(ctx);
   3027     return JS_MKPTR(JS_TAG_SYMBOL, rt->atom_array[atom]);
   3028 }
   3029 
   3030 /* descr must be a non-numeric string atom */
   3031 static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr,
   3032                                     int atom_type)
   3033 {
   3034     JSRuntime *rt = ctx->rt;
   3035     JSString *p;
   3036 
   3037     assert(!__JS_AtomIsTaggedInt(descr));
   3038     assert(descr < rt->atom_size);
   3039     p = rt->atom_array[descr];
   3040     JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
   3041     return JS_NewSymbol(ctx, p, atom_type);
   3042 }
   3043 
   3044 #define ATOM_GET_STR_BUF_SIZE 64
   3045 
   3046 /* Should only be used for debug. */
   3047 static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size,
   3048                                    JSAtom atom)
   3049 {
   3050     if (__JS_AtomIsTaggedInt(atom)) {
   3051         snprintf(buf, buf_size, "%u", __JS_AtomToUInt32(atom));
   3052     } else {
   3053         JSAtomStruct *p;
   3054         assert(atom < rt->atom_size);
   3055         if (atom == JS_ATOM_NULL) {
   3056             snprintf(buf, buf_size, "<null>");
   3057         } else {
   3058             int i, c;
   3059             char *q;
   3060             JSString *str;
   3061 
   3062             q = buf;
   3063             p = rt->atom_array[atom];
   3064             assert(!atom_is_free(p));
   3065             str = p;
   3066             if (str) {
   3067                 if (!str->is_wide_char) {
   3068                     /* special case ASCII strings */
   3069                     c = 0;
   3070                     for(i = 0; i < str->len; i++) {
   3071                         c |= str->u.str8[i];
   3072                     }
   3073                     if (c < 0x80)
   3074                         return (const char *)str->u.str8;
   3075                 }
   3076                 for(i = 0; i < str->len; i++) {
   3077                     c = string_get(str, i);
   3078                     if ((q - buf) >= buf_size - UTF8_CHAR_LEN_MAX)
   3079                         break;
   3080                     if (c < 128) {
   3081                         *q++ = c;
   3082                     } else {
   3083                         q += unicode_to_utf8((uint8_t *)q, c);
   3084                     }
   3085                 }
   3086             }
   3087             *q = '\0';
   3088         }
   3089     }
   3090     return buf;
   3091 }
   3092 
   3093 static const char *JS_AtomGetStr(JSContext *ctx, char *buf, int buf_size, JSAtom atom)
   3094 {
   3095     return JS_AtomGetStrRT(ctx->rt, buf, buf_size, atom);
   3096 }
   3097 
   3098 static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, BOOL force_string)
   3099 {
   3100     char buf[ATOM_GET_STR_BUF_SIZE];
   3101 
   3102     if (__JS_AtomIsTaggedInt(atom)) {
   3103         snprintf(buf, sizeof(buf), "%u", __JS_AtomToUInt32(atom));
   3104         return JS_NewString(ctx, buf);
   3105     } else {
   3106         JSRuntime *rt = ctx->rt;
   3107         JSAtomStruct *p;
   3108         assert(atom < rt->atom_size);
   3109         p = rt->atom_array[atom];
   3110         if (p->atom_type == JS_ATOM_TYPE_STRING) {
   3111             goto ret_string;
   3112         } else if (force_string) {
   3113             if (p->len == 0 && p->is_wide_char != 0) {
   3114                 /* no description string */
   3115                 p = rt->atom_array[JS_ATOM_empty_string];
   3116             }
   3117         ret_string:
   3118             return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
   3119         } else {
   3120             return JS_DupValue(ctx, JS_MKPTR(JS_TAG_SYMBOL, p));
   3121         }
   3122     }
   3123 }
   3124 
   3125 JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom)
   3126 {
   3127     return __JS_AtomToValue(ctx, atom, FALSE);
   3128 }
   3129 
   3130 JSValue JS_AtomToString(JSContext *ctx, JSAtom atom)
   3131 {
   3132     return __JS_AtomToValue(ctx, atom, TRUE);
   3133 }
   3134 
   3135 /* return TRUE if the atom is an array index (i.e. 0 <= index <=
   3136    2^32-2 and return its value */
   3137 static BOOL JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom)
   3138 {
   3139     if (__JS_AtomIsTaggedInt(atom)) {
   3140         *pval = __JS_AtomToUInt32(atom);
   3141         return TRUE;
   3142     } else {
   3143         JSRuntime *rt = ctx->rt;
   3144         JSAtomStruct *p;
   3145         uint32_t val;
   3146 
   3147         assert(atom < rt->atom_size);
   3148         p = rt->atom_array[atom];
   3149         if (p->atom_type == JS_ATOM_TYPE_STRING &&
   3150             is_num_string(&val, p) && val != -1) {
   3151             *pval = val;
   3152             return TRUE;
   3153         } else {
   3154             *pval = 0;
   3155             return FALSE;
   3156         }
   3157     }
   3158 }
   3159 
   3160 /* This test must be fast if atom is not a numeric index (e.g. a
   3161    method name). Return JS_UNDEFINED if not a numeric
   3162    index. JS_EXCEPTION can also be returned. */
   3163 static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom)
   3164 {
   3165     JSRuntime *rt = ctx->rt;
   3166     JSAtomStruct *p1;
   3167     JSString *p;
   3168     int c, len, ret;
   3169     JSValue num, str;
   3170 
   3171     if (__JS_AtomIsTaggedInt(atom))
   3172         return JS_NewInt32(ctx, __JS_AtomToUInt32(atom));
   3173     assert(atom < rt->atom_size);
   3174     p1 = rt->atom_array[atom];
   3175     if (p1->atom_type != JS_ATOM_TYPE_STRING)
   3176         return JS_UNDEFINED;
   3177     p = p1;
   3178     len = p->len;
   3179     if (p->is_wide_char) {
   3180         const uint16_t *r = p->u.str16, *r_end = p->u.str16 + len;
   3181         if (r >= r_end)
   3182             return JS_UNDEFINED;
   3183         c = *r;
   3184         if (c == '-') {
   3185             if (r >= r_end)
   3186                 return JS_UNDEFINED;
   3187             r++;
   3188             c = *r;
   3189             /* -0 case is specific */
   3190             if (c == '0' && len == 2)
   3191                 goto minus_zero;
   3192         }
   3193         /* XXX: should test NaN, but the tests do not check it */
   3194         if (!is_num(c)) {
   3195             /* XXX: String should be normalized, therefore 8-bit only */
   3196             const uint16_t nfinity16[7] = { 'n', 'f', 'i', 'n', 'i', 't', 'y' };
   3197             if (!(c =='I' && (r_end - r) == 8 &&
   3198                   !memcmp(r + 1, nfinity16, sizeof(nfinity16))))
   3199                 return JS_UNDEFINED;
   3200         }
   3201     } else {
   3202         const uint8_t *r = p->u.str8, *r_end = p->u.str8 + len;
   3203         if (r >= r_end)
   3204             return JS_UNDEFINED;
   3205         c = *r;
   3206         if (c == '-') {
   3207             if (r >= r_end)
   3208                 return JS_UNDEFINED;
   3209             r++;
   3210             c = *r;
   3211             /* -0 case is specific */
   3212             if (c == '0' && len == 2) {
   3213             minus_zero:
   3214                 return __JS_NewFloat64(ctx, -0.0);
   3215             }
   3216         }
   3217         if (!is_num(c)) {
   3218             if (!(c =='I' && (r_end - r) == 8 &&
   3219                   !memcmp(r + 1, "nfinity", 7)))
   3220                 return JS_UNDEFINED;
   3221         }
   3222     }
   3223     /* XXX: bignum: would be better to only accept integer to avoid
   3224        relying on current floating point precision */
   3225     /* this is ECMA CanonicalNumericIndexString primitive */
   3226     num = JS_ToNumber(ctx, JS_MKPTR(JS_TAG_STRING, p));
   3227     if (JS_IsException(num))
   3228         return num;
   3229     str = JS_ToString(ctx, num);
   3230     if (JS_IsException(str)) {
   3231         JS_FreeValue(ctx, num);
   3232         return str;
   3233     }
   3234     ret = js_string_compare(ctx, p, JS_VALUE_GET_STRING(str));
   3235     JS_FreeValue(ctx, str);
   3236     if (ret == 0) {
   3237         return num;
   3238     } else {
   3239         JS_FreeValue(ctx, num);
   3240         return JS_UNDEFINED;
   3241     }
   3242 }
   3243 
   3244 /* return -1 if exception or TRUE/FALSE */
   3245 static int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom)
   3246 {
   3247     JSValue num;
   3248     num = JS_AtomIsNumericIndex1(ctx, atom);
   3249     if (likely(JS_IsUndefined(num)))
   3250         return FALSE;
   3251     if (JS_IsException(num))
   3252         return -1;
   3253     JS_FreeValue(ctx, num);
   3254     return TRUE;
   3255 }
   3256 
   3257 void JS_FreeAtom(JSContext *ctx, JSAtom v)
   3258 {
   3259     if (!__JS_AtomIsConst(v))
   3260         __JS_FreeAtom(ctx->rt, v);
   3261 }
   3262 
   3263 void JS_FreeAtomRT(JSRuntime *rt, JSAtom v)
   3264 {
   3265     if (!__JS_AtomIsConst(v))
   3266         __JS_FreeAtom(rt, v);
   3267 }
   3268 
   3269 /* return TRUE if 'v' is a symbol with a string description */
   3270 static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v)
   3271 {
   3272     JSRuntime *rt;
   3273     JSAtomStruct *p;
   3274 
   3275     rt = ctx->rt;
   3276     if (__JS_AtomIsTaggedInt(v))
   3277         return FALSE;
   3278     p = rt->atom_array[v];
   3279     return (((p->atom_type == JS_ATOM_TYPE_SYMBOL &&
   3280               p->hash == JS_ATOM_HASH_SYMBOL) ||
   3281              p->atom_type == JS_ATOM_TYPE_GLOBAL_SYMBOL) &&
   3282             !(p->len == 0 && p->is_wide_char != 0));
   3283 }
   3284 
   3285 static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom)
   3286 {
   3287     char buf[ATOM_GET_STR_BUF_SIZE];
   3288     const char *p;
   3289     int i;
   3290 
   3291     /* XXX: should handle embedded null characters */
   3292     /* XXX: should move encoding code to JS_AtomGetStr */
   3293     p = JS_AtomGetStr(ctx, buf, sizeof(buf), atom);
   3294     for (i = 0; p[i]; i++) {
   3295         int c = (unsigned char)p[i];
   3296         if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
   3297               (c == '_' || c == '$') || (c >= '0' && c <= '9' && i > 0)))
   3298             break;
   3299     }
   3300     if (i > 0 && p[i] == '\0') {
   3301         printf("%s", p);
   3302     } else {
   3303         putchar('"');
   3304         printf("%.*s", i, p);
   3305         for (; p[i]; i++) {
   3306             int c = (unsigned char)p[i];
   3307             if (c == '\"' || c == '\\') {
   3308                 putchar('\\');
   3309                 putchar(c);
   3310             } else if (c >= ' ' && c <= 126) {
   3311                 putchar(c);
   3312             } else if (c == '\n') {
   3313                 putchar('\\');
   3314                 putchar('n');
   3315             } else {
   3316                 printf("\\u%04x", c);
   3317             }
   3318         }
   3319         putchar('\"');
   3320     }
   3321 }
   3322 
   3323 /* free with JS_FreeCString() */
   3324 const char *JS_AtomToCString(JSContext *ctx, JSAtom atom)
   3325 {
   3326     JSValue str;
   3327     const char *cstr;
   3328 
   3329     str = JS_AtomToString(ctx, atom);
   3330     if (JS_IsException(str))
   3331         return NULL;
   3332     cstr = JS_ToCString(ctx, str);
   3333     JS_FreeValue(ctx, str);
   3334     return cstr;
   3335 }
   3336 
   3337 /* return a string atom containing name concatenated with str1 */
   3338 static JSAtom js_atom_concat_str(JSContext *ctx, JSAtom name, const char *str1)
   3339 {
   3340     JSValue str;
   3341     JSAtom atom;
   3342     const char *cstr;
   3343     char *cstr2;
   3344     size_t len, len1;
   3345 
   3346     str = JS_AtomToString(ctx, name);
   3347     if (JS_IsException(str))
   3348         return JS_ATOM_NULL;
   3349     cstr = JS_ToCStringLen(ctx, &len, str);
   3350     if (!cstr)
   3351         goto fail;
   3352     len1 = strlen(str1);
   3353     cstr2 = js_malloc(ctx, len + len1 + 1);
   3354     if (!cstr2)
   3355         goto fail;
   3356     memcpy(cstr2, cstr, len);
   3357     memcpy(cstr2 + len, str1, len1);
   3358     cstr2[len + len1] = '\0';
   3359     atom = JS_NewAtomLen(ctx, cstr2, len + len1);
   3360     js_free(ctx, cstr2);
   3361     JS_FreeCString(ctx, cstr);
   3362     JS_FreeValue(ctx, str);
   3363     return atom;
   3364  fail:
   3365     JS_FreeCString(ctx, cstr);
   3366     JS_FreeValue(ctx, str);
   3367     return JS_ATOM_NULL;
   3368 }
   3369 
   3370 static JSAtom js_atom_concat_num(JSContext *ctx, JSAtom name, uint32_t n)
   3371 {
   3372     char buf[16];
   3373     snprintf(buf, sizeof(buf), "%u", n);
   3374     return js_atom_concat_str(ctx, name, buf);
   3375 }
   3376 
   3377 static inline BOOL JS_IsEmptyString(JSValueConst v)
   3378 {
   3379     return JS_VALUE_GET_TAG(v) == JS_TAG_STRING && JS_VALUE_GET_STRING(v)->len == 0;
   3380 }
   3381 
   3382 /* JSClass support */
   3383 
   3384 #ifdef CONFIG_ATOMICS
   3385 static pthread_mutex_t js_class_id_mutex = PTHREAD_MUTEX_INITIALIZER;
   3386 #endif
   3387 
   3388 /* a new class ID is allocated if *pclass_id != 0 */
   3389 JSClassID JS_NewClassID(JSClassID *pclass_id)
   3390 {
   3391     JSClassID class_id;
   3392 #ifdef CONFIG_ATOMICS
   3393     pthread_mutex_lock(&js_class_id_mutex);
   3394 #endif
   3395     class_id = *pclass_id;
   3396     if (class_id == 0) {
   3397         class_id = js_class_id_alloc++;
   3398         *pclass_id = class_id;
   3399     }
   3400 #ifdef CONFIG_ATOMICS
   3401     pthread_mutex_unlock(&js_class_id_mutex);
   3402 #endif
   3403     return class_id;
   3404 }
   3405 
   3406 JSClassID JS_GetClassID(JSValue v)
   3407 {
   3408     JSObject *p;
   3409     if (JS_VALUE_GET_TAG(v) != JS_TAG_OBJECT)
   3410         return JS_INVALID_CLASS_ID;
   3411     p = JS_VALUE_GET_OBJ(v);
   3412     return p->class_id;
   3413 }
   3414 
   3415 BOOL JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id)
   3416 {
   3417     return (class_id < rt->class_count &&
   3418             rt->class_array[class_id].class_id != 0);
   3419 }
   3420 
   3421 /* create a new object internal class. Return -1 if error, 0 if
   3422    OK. The finalizer can be NULL if none is needed. */
   3423 static int JS_NewClass1(JSRuntime *rt, JSClassID class_id,
   3424                         const JSClassDef *class_def, JSAtom name)
   3425 {
   3426     int new_size, i;
   3427     JSClass *cl, *new_class_array;
   3428     struct list_head *el;
   3429 
   3430     if (class_id >= (1 << 16))
   3431         return -1;
   3432     if (class_id < rt->class_count &&
   3433         rt->class_array[class_id].class_id != 0)
   3434         return -1;
   3435 
   3436     if (class_id >= rt->class_count) {
   3437         new_size = max_int(JS_CLASS_INIT_COUNT,
   3438                            max_int(class_id + 1, rt->class_count * 3 / 2));
   3439 
   3440         /* reallocate the context class prototype array, if any */
   3441         list_for_each(el, &rt->context_list) {
   3442             JSContext *ctx = list_entry(el, JSContext, link);
   3443             JSValue *new_tab;
   3444             new_tab = js_realloc_rt(rt, ctx->class_proto,
   3445                                     sizeof(ctx->class_proto[0]) * new_size);
   3446             if (!new_tab)
   3447                 return -1;
   3448             for(i = rt->class_count; i < new_size; i++)
   3449                 new_tab[i] = JS_NULL;
   3450             ctx->class_proto = new_tab;
   3451         }
   3452         /* reallocate the class array */
   3453         new_class_array = js_realloc_rt(rt, rt->class_array,
   3454                                         sizeof(JSClass) * new_size);
   3455         if (!new_class_array)
   3456             return -1;
   3457         memset(new_class_array + rt->class_count, 0,
   3458                (new_size - rt->class_count) * sizeof(JSClass));
   3459         rt->class_array = new_class_array;
   3460         rt->class_count = new_size;
   3461     }
   3462     cl = &rt->class_array[class_id];
   3463     cl->class_id = class_id;
   3464     cl->class_name = JS_DupAtomRT(rt, name);
   3465     cl->finalizer = class_def->finalizer;
   3466     cl->gc_mark = class_def->gc_mark;
   3467     cl->call = class_def->call;
   3468     cl->exotic = class_def->exotic;
   3469     return 0;
   3470 }
   3471 
   3472 int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def)
   3473 {
   3474     int ret, len;
   3475     JSAtom name;
   3476 
   3477     len = strlen(class_def->class_name);
   3478     name = __JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);
   3479     if (name == JS_ATOM_NULL) {
   3480         name = __JS_NewAtomInit(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);
   3481         if (name == JS_ATOM_NULL)
   3482             return -1;
   3483     }
   3484     ret = JS_NewClass1(rt, class_id, class_def, name);
   3485     JS_FreeAtomRT(rt, name);
   3486     return ret;
   3487 }
   3488 
   3489 static JSValue js_new_string8(JSContext *ctx, const uint8_t *buf, int len)
   3490 {
   3491     JSString *str;
   3492 
   3493     if (len <= 0) {
   3494         return JS_AtomToString(ctx, JS_ATOM_empty_string);
   3495     }
   3496     str = js_alloc_string(ctx, len, 0);
   3497     if (!str)
   3498         return JS_EXCEPTION;
   3499     memcpy(str->u.str8, buf, len);
   3500     str->u.str8[len] = '\0';
   3501     return JS_MKPTR(JS_TAG_STRING, str);
   3502 }
   3503 
   3504 static JSValue js_new_string16(JSContext *ctx, const uint16_t *buf, int len)
   3505 {
   3506     JSString *str;
   3507     str = js_alloc_string(ctx, len, 1);
   3508     if (!str)
   3509         return JS_EXCEPTION;
   3510     memcpy(str->u.str16, buf, len * 2);
   3511     return JS_MKPTR(JS_TAG_STRING, str);
   3512 }
   3513 
   3514 static JSValue js_new_string_char(JSContext *ctx, uint16_t c)
   3515 {
   3516     if (c < 0x100) {
   3517         uint8_t ch8 = c;
   3518         return js_new_string8(ctx, &ch8, 1);
   3519     } else {
   3520         uint16_t ch16 = c;
   3521         return js_new_string16(ctx, &ch16, 1);
   3522     }
   3523 }
   3524 
   3525 static JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end)
   3526 {
   3527     int len = end - start;
   3528     if (start == 0 && end == p->len) {
   3529         return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
   3530     }
   3531     if (p->is_wide_char && len > 0) {
   3532         JSString *str;
   3533         int i;
   3534         uint16_t c = 0;
   3535         for (i = start; i < end; i++) {
   3536             c |= p->u.str16[i];
   3537         }
   3538         if (c > 0xFF)
   3539             return js_new_string16(ctx, p->u.str16 + start, len);
   3540 
   3541         str = js_alloc_string(ctx, len, 0);
   3542         if (!str)
   3543             return JS_EXCEPTION;
   3544         for (i = 0; i < len; i++) {
   3545             str->u.str8[i] = p->u.str16[start + i];
   3546         }
   3547         str->u.str8[len] = '\0';
   3548         return JS_MKPTR(JS_TAG_STRING, str);
   3549     } else {
   3550         return js_new_string8(ctx, p->u.str8 + start, len);
   3551     }
   3552 }
   3553 
   3554 typedef struct StringBuffer {
   3555     JSContext *ctx;
   3556     JSString *str;
   3557     int len;
   3558     int size;
   3559     int is_wide_char;
   3560     int error_status;
   3561 } StringBuffer;
   3562 
   3563 /* It is valid to call string_buffer_end() and all string_buffer functions even
   3564    if string_buffer_init() or another string_buffer function returns an error.
   3565    If the error_status is set, string_buffer_end() returns JS_EXCEPTION.
   3566  */
   3567 static int string_buffer_init2(JSContext *ctx, StringBuffer *s, int size,
   3568                                int is_wide)
   3569 {
   3570     s->ctx = ctx;
   3571     s->size = size;
   3572     s->len = 0;
   3573     s->is_wide_char = is_wide;
   3574     s->error_status = 0;
   3575     s->str = js_alloc_string(ctx, size, is_wide);
   3576     if (unlikely(!s->str)) {
   3577         s->size = 0;
   3578         return s->error_status = -1;
   3579     }
   3580 #ifdef DUMP_LEAKS
   3581     /* the StringBuffer may reallocate the JSString, only link it at the end */
   3582     list_del(&s->str->link);
   3583 #endif
   3584     return 0;
   3585 }
   3586 
   3587 static inline int string_buffer_init(JSContext *ctx, StringBuffer *s, int size)
   3588 {
   3589     return string_buffer_init2(ctx, s, size, 0);
   3590 }
   3591 
   3592 static void string_buffer_free(StringBuffer *s)
   3593 {
   3594     js_free(s->ctx, s->str);
   3595     s->str = NULL;
   3596 }
   3597 
   3598 static int string_buffer_set_error(StringBuffer *s)
   3599 {
   3600     js_free(s->ctx, s->str);
   3601     s->str = NULL;
   3602     s->size = 0;
   3603     s->len = 0;
   3604     return s->error_status = -1;
   3605 }
   3606 
   3607 static no_inline int string_buffer_widen(StringBuffer *s, int size)
   3608 {
   3609     JSString *str;
   3610     size_t slack;
   3611     int i;
   3612 
   3613     if (s->error_status)
   3614         return -1;
   3615 
   3616     str = js_realloc2(s->ctx, s->str, sizeof(JSString) + (size << 1), &slack);
   3617     if (!str)
   3618         return string_buffer_set_error(s);
   3619     size += slack >> 1;
   3620     for(i = s->len; i-- > 0;) {
   3621         str->u.str16[i] = str->u.str8[i];
   3622     }
   3623     s->is_wide_char = 1;
   3624     s->size = size;
   3625     s->str = str;
   3626     return 0;
   3627 }
   3628 
   3629 static no_inline int string_buffer_realloc(StringBuffer *s, int new_len, int c)
   3630 {
   3631     JSString *new_str;
   3632     int new_size;
   3633     size_t new_size_bytes, slack;
   3634 
   3635     if (s->error_status)
   3636         return -1;
   3637 
   3638     if (new_len > JS_STRING_LEN_MAX) {
   3639         JS_ThrowInternalError(s->ctx, "string too long");
   3640         return string_buffer_set_error(s);
   3641     }
   3642     new_size = min_int(max_int(new_len, s->size * 3 / 2), JS_STRING_LEN_MAX);
   3643     if (!s->is_wide_char && c >= 0x100) {
   3644         return string_buffer_widen(s, new_size);
   3645     }
   3646     new_size_bytes = sizeof(JSString) + (new_size << s->is_wide_char) + 1 - s->is_wide_char;
   3647     new_str = js_realloc2(s->ctx, s->str, new_size_bytes, &slack);
   3648     if (!new_str)
   3649         return string_buffer_set_error(s);
   3650     new_size = min_int(new_size + (slack >> s->is_wide_char), JS_STRING_LEN_MAX);
   3651     s->size = new_size;
   3652     s->str = new_str;
   3653     return 0;
   3654 }
   3655 
   3656 static no_inline int string_buffer_putc_slow(StringBuffer *s, uint32_t c)
   3657 {
   3658     if (unlikely(s->len >= s->size)) {
   3659         if (string_buffer_realloc(s, s->len + 1, c))
   3660             return -1;
   3661     }
   3662     if (s->is_wide_char) {
   3663         s->str->u.str16[s->len++] = c;
   3664     } else if (c < 0x100) {
   3665         s->str->u.str8[s->len++] = c;
   3666     } else {
   3667         if (string_buffer_widen(s, s->size))
   3668             return -1;
   3669         s->str->u.str16[s->len++] = c;
   3670     }
   3671     return 0;
   3672 }
   3673 
   3674 /* 0 <= c <= 0xff */
   3675 static int string_buffer_putc8(StringBuffer *s, uint32_t c)
   3676 {
   3677     if (unlikely(s->len >= s->size)) {
   3678         if (string_buffer_realloc(s, s->len + 1, c))
   3679             return -1;
   3680     }
   3681     if (s->is_wide_char) {
   3682         s->str->u.str16[s->len++] = c;
   3683     } else {
   3684         s->str->u.str8[s->len++] = c;
   3685     }
   3686     return 0;
   3687 }
   3688 
   3689 /* 0 <= c <= 0xffff */
   3690 static int string_buffer_putc16(StringBuffer *s, uint32_t c)
   3691 {
   3692     if (likely(s->len < s->size)) {
   3693         if (s->is_wide_char) {
   3694             s->str->u.str16[s->len++] = c;
   3695             return 0;
   3696         } else if (c < 0x100) {
   3697             s->str->u.str8[s->len++] = c;
   3698             return 0;
   3699         }
   3700     }
   3701     return string_buffer_putc_slow(s, c);
   3702 }
   3703 
   3704 /* 0 <= c <= 0x10ffff */
   3705 static int string_buffer_putc(StringBuffer *s, uint32_t c)
   3706 {
   3707     if (unlikely(c >= 0x10000)) {
   3708         /* surrogate pair */
   3709         if (string_buffer_putc16(s, get_hi_surrogate(c)))
   3710             return -1;
   3711         c = get_lo_surrogate(c);
   3712     }
   3713     return string_buffer_putc16(s, c);
   3714 }
   3715 
   3716 static int string_getc(const JSString *p, int *pidx)
   3717 {
   3718     int idx, c, c1;
   3719     idx = *pidx;
   3720     if (p->is_wide_char) {
   3721         c = p->u.str16[idx++];
   3722         if (is_hi_surrogate(c) && idx < p->len) {
   3723             c1 = p->u.str16[idx];
   3724             if (is_lo_surrogate(c1)) {
   3725                 c = from_surrogate(c, c1);
   3726                 idx++;
   3727             }
   3728         }
   3729     } else {
   3730         c = p->u.str8[idx++];
   3731     }
   3732     *pidx = idx;
   3733     return c;
   3734 }
   3735 
   3736 static int string_buffer_write8(StringBuffer *s, const uint8_t *p, int len)
   3737 {
   3738     int i;
   3739 
   3740     if (s->len + len > s->size) {
   3741         if (string_buffer_realloc(s, s->len + len, 0))
   3742             return -1;
   3743     }
   3744     if (s->is_wide_char) {
   3745         for (i = 0; i < len; i++) {
   3746             s->str->u.str16[s->len + i] = p[i];
   3747         }
   3748         s->len += len;
   3749     } else {
   3750         memcpy(&s->str->u.str8[s->len], p, len);
   3751         s->len += len;
   3752     }
   3753     return 0;
   3754 }
   3755 
   3756 static int string_buffer_write16(StringBuffer *s, const uint16_t *p, int len)
   3757 {
   3758     int c = 0, i;
   3759 
   3760     for (i = 0; i < len; i++) {
   3761         c |= p[i];
   3762     }
   3763     if (s->len + len > s->size) {
   3764         if (string_buffer_realloc(s, s->len + len, c))
   3765             return -1;
   3766     } else if (!s->is_wide_char && c >= 0x100) {
   3767         if (string_buffer_widen(s, s->size))
   3768             return -1;
   3769     }
   3770     if (s->is_wide_char) {
   3771         memcpy(&s->str->u.str16[s->len], p, len << 1);
   3772         s->len += len;
   3773     } else {
   3774         for (i = 0; i < len; i++) {
   3775             s->str->u.str8[s->len + i] = p[i];
   3776         }
   3777         s->len += len;
   3778     }
   3779     return 0;
   3780 }
   3781 
   3782 /* appending an ASCII string */
   3783 static int string_buffer_puts8(StringBuffer *s, const char *str)
   3784 {
   3785     return string_buffer_write8(s, (const uint8_t *)str, strlen(str));
   3786 }
   3787 
   3788 static int string_buffer_concat(StringBuffer *s, const JSString *p,
   3789                                 uint32_t from, uint32_t to)
   3790 {
   3791     if (to <= from)
   3792         return 0;
   3793     if (p->is_wide_char)
   3794         return string_buffer_write16(s, p->u.str16 + from, to - from);
   3795     else
   3796         return string_buffer_write8(s, p->u.str8 + from, to - from);
   3797 }
   3798 
   3799 static int string_buffer_concat_value(StringBuffer *s, JSValueConst v)
   3800 {
   3801     JSString *p;
   3802     JSValue v1;
   3803     int res;
   3804 
   3805     if (s->error_status) {
   3806         /* prevent exception overload */
   3807         return -1;
   3808     }
   3809     if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {
   3810         v1 = JS_ToString(s->ctx, v);
   3811         if (JS_IsException(v1))
   3812             return string_buffer_set_error(s);
   3813         p = JS_VALUE_GET_STRING(v1);
   3814         res = string_buffer_concat(s, p, 0, p->len);
   3815         JS_FreeValue(s->ctx, v1);
   3816         return res;
   3817     }
   3818     p = JS_VALUE_GET_STRING(v);
   3819     return string_buffer_concat(s, p, 0, p->len);
   3820 }
   3821 
   3822 static int string_buffer_concat_value_free(StringBuffer *s, JSValue v)
   3823 {
   3824     JSString *p;
   3825     int res;
   3826 
   3827     if (s->error_status) {
   3828         /* prevent exception overload */
   3829         JS_FreeValue(s->ctx, v);
   3830         return -1;
   3831     }
   3832     if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {
   3833         v = JS_ToStringFree(s->ctx, v);
   3834         if (JS_IsException(v))
   3835             return string_buffer_set_error(s);
   3836     }
   3837     p = JS_VALUE_GET_STRING(v);
   3838     res = string_buffer_concat(s, p, 0, p->len);
   3839     JS_FreeValue(s->ctx, v);
   3840     return res;
   3841 }
   3842 
   3843 static int string_buffer_fill(StringBuffer *s, int c, int count)
   3844 {
   3845     /* XXX: optimize */
   3846     if (s->len + count > s->size) {
   3847         if (string_buffer_realloc(s, s->len + count, c))
   3848             return -1;
   3849     }
   3850     while (count-- > 0) {
   3851         if (string_buffer_putc16(s, c))
   3852             return -1;
   3853     }
   3854     return 0;
   3855 }
   3856 
   3857 static JSValue string_buffer_end(StringBuffer *s)
   3858 {
   3859     JSString *str;
   3860     str = s->str;
   3861     if (s->error_status)
   3862         return JS_EXCEPTION;
   3863     if (s->len == 0) {
   3864         js_free(s->ctx, str);
   3865         s->str = NULL;
   3866         return JS_AtomToString(s->ctx, JS_ATOM_empty_string);
   3867     }
   3868     if (s->len < s->size) {
   3869         /* smaller size so js_realloc should not fail, but OK if it does */
   3870         /* XXX: should add some slack to avoid unnecessary calls */
   3871         /* XXX: might need to use malloc+free to ensure smaller size */
   3872         str = js_realloc_rt(s->ctx->rt, str, sizeof(JSString) +
   3873                             (s->len << s->is_wide_char) + 1 - s->is_wide_char);
   3874         if (str == NULL)
   3875             str = s->str;
   3876         s->str = str;
   3877     }
   3878     if (!s->is_wide_char)
   3879         str->u.str8[s->len] = 0;
   3880 #ifdef DUMP_LEAKS
   3881     list_add_tail(&str->link, &s->ctx->rt->string_list);
   3882 #endif
   3883     str->is_wide_char = s->is_wide_char;
   3884     str->len = s->len;
   3885     s->str = NULL;
   3886     return JS_MKPTR(JS_TAG_STRING, str);
   3887 }
   3888 
   3889 /* create a string from a UTF-8 buffer */
   3890 JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len)
   3891 {
   3892     const uint8_t *p, *p_end, *p_start, *p_next;
   3893     uint32_t c;
   3894     StringBuffer b_s, *b = &b_s;
   3895     size_t len1;
   3896 
   3897     p_start = (const uint8_t *)buf;
   3898     p_end = p_start + buf_len;
   3899     p = p_start;
   3900     while (p < p_end && *p < 128)
   3901         p++;
   3902     len1 = p - p_start;
   3903     if (len1 > JS_STRING_LEN_MAX)
   3904         return JS_ThrowInternalError(ctx, "string too long");
   3905     if (p == p_end) {
   3906         /* ASCII string */
   3907         return js_new_string8(ctx, (const uint8_t *)buf, buf_len);
   3908     } else {
   3909         if (string_buffer_init(ctx, b, buf_len))
   3910             goto fail;
   3911         string_buffer_write8(b, p_start, len1);
   3912         while (p < p_end) {
   3913             if (*p < 128) {
   3914                 string_buffer_putc8(b, *p++);
   3915             } else {
   3916                 /* parse utf-8 sequence, return 0xFFFFFFFF for error */
   3917                 c = unicode_from_utf8(p, p_end - p, &p_next);
   3918                 if (c < 0x10000) {
   3919                     p = p_next;
   3920                 } else if (c <= 0x10FFFF) {
   3921                     p = p_next;
   3922                     /* surrogate pair */
   3923                     string_buffer_putc16(b, get_hi_surrogate(c));
   3924                     c = get_lo_surrogate(c);
   3925                 } else {
   3926                     /* invalid char */
   3927                     c = 0xfffd;
   3928                     /* skip the invalid chars */
   3929                     /* XXX: seems incorrect. Why not just use c = *p++; ? */
   3930                     while (p < p_end && (*p >= 0x80 && *p < 0xc0))
   3931                         p++;
   3932                     if (p < p_end) {
   3933                         p++;
   3934                         while (p < p_end && (*p >= 0x80 && *p < 0xc0))
   3935                             p++;
   3936                     }
   3937                 }
   3938                 string_buffer_putc16(b, c);
   3939             }
   3940         }
   3941     }
   3942     return string_buffer_end(b);
   3943 
   3944  fail:
   3945     string_buffer_free(b);
   3946     return JS_EXCEPTION;
   3947 }
   3948 
   3949 static JSValue JS_ConcatString3(JSContext *ctx, const char *str1,
   3950                                 JSValue str2, const char *str3)
   3951 {
   3952     StringBuffer b_s, *b = &b_s;
   3953     int len1, len3;
   3954     JSString *p;
   3955 
   3956     if (unlikely(JS_VALUE_GET_TAG(str2) != JS_TAG_STRING)) {
   3957         str2 = JS_ToStringFree(ctx, str2);
   3958         if (JS_IsException(str2))
   3959             goto fail;
   3960     }
   3961     p = JS_VALUE_GET_STRING(str2);
   3962     len1 = strlen(str1);
   3963     len3 = strlen(str3);
   3964 
   3965     if (string_buffer_init2(ctx, b, len1 + p->len + len3, p->is_wide_char))
   3966         goto fail;
   3967 
   3968     string_buffer_write8(b, (const uint8_t *)str1, len1);
   3969     string_buffer_concat(b, p, 0, p->len);
   3970     string_buffer_write8(b, (const uint8_t *)str3, len3);
   3971 
   3972     JS_FreeValue(ctx, str2);
   3973     return string_buffer_end(b);
   3974 
   3975  fail:
   3976     JS_FreeValue(ctx, str2);
   3977     return JS_EXCEPTION;
   3978 }
   3979 
   3980 JSValue JS_NewString(JSContext *ctx, const char *str)
   3981 {
   3982     return JS_NewStringLen(ctx, str, strlen(str));
   3983 }
   3984 
   3985 JSValue JS_NewAtomString(JSContext *ctx, const char *str)
   3986 {
   3987     JSAtom atom = JS_NewAtom(ctx, str);
   3988     if (atom == JS_ATOM_NULL)
   3989         return JS_EXCEPTION;
   3990     JSValue val = JS_AtomToString(ctx, atom);
   3991     JS_FreeAtom(ctx, atom);
   3992     return val;
   3993 }
   3994 
   3995 /* return (NULL, 0) if exception. */
   3996 /* return pointer into a JSString with a live ref_count */
   3997 /* cesu8 determines if non-BMP1 codepoints are encoded as 1 or 2 utf-8 sequences */
   3998 const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, BOOL cesu8)
   3999 {
   4000     JSValue val;
   4001     JSString *str, *str_new;
   4002     int pos, len, c, c1;
   4003     uint8_t *q;
   4004 
   4005     if (JS_VALUE_GET_TAG(val1) != JS_TAG_STRING) {
   4006         val = JS_ToString(ctx, val1);
   4007         if (JS_IsException(val))
   4008             goto fail;
   4009     } else {
   4010         val = JS_DupValue(ctx, val1);
   4011     }
   4012 
   4013     str = JS_VALUE_GET_STRING(val);
   4014     len = str->len;
   4015     if (!str->is_wide_char) {
   4016         const uint8_t *src = str->u.str8;
   4017         int count;
   4018 
   4019         /* count the number of non-ASCII characters */
   4020         /* Scanning the whole string is required for ASCII strings,
   4021            and computing the number of non-ASCII bytes is less expensive
   4022            than testing each byte, hence this method is faster for ASCII
   4023            strings, which is the most common case.
   4024          */
   4025         count = 0;
   4026         for (pos = 0; pos < len; pos++) {
   4027             count += src[pos] >> 7;
   4028         }
   4029         if (count == 0) {
   4030             if (plen)
   4031                 *plen = len;
   4032             return (const char *)src;
   4033         }
   4034         str_new = js_alloc_string(ctx, len + count, 0);
   4035         if (!str_new)
   4036             goto fail;
   4037         q = str_new->u.str8;
   4038         for (pos = 0; pos < len; pos++) {
   4039             c = src[pos];
   4040             if (c < 0x80) {
   4041                 *q++ = c;
   4042             } else {
   4043                 *q++ = (c >> 6) | 0xc0;
   4044                 *q++ = (c & 0x3f) | 0x80;
   4045             }
   4046         }
   4047     } else {
   4048         const uint16_t *src = str->u.str16;
   4049         /* Allocate 3 bytes per 16 bit code point. Surrogate pairs may
   4050            produce 4 bytes but use 2 code points.
   4051          */
   4052         str_new = js_alloc_string(ctx, len * 3, 0);
   4053         if (!str_new)
   4054             goto fail;
   4055         q = str_new->u.str8;
   4056         pos = 0;
   4057         while (pos < len) {
   4058             c = src[pos++];
   4059             if (c < 0x80) {
   4060                 *q++ = c;
   4061             } else {
   4062                 if (is_hi_surrogate(c)) {
   4063                     if (pos < len && !cesu8) {
   4064                         c1 = src[pos];
   4065                         if (is_lo_surrogate(c1)) {
   4066                             pos++;
   4067                             c = from_surrogate(c, c1);
   4068                         } else {
   4069                             /* Keep unmatched surrogate code points */
   4070                             /* c = 0xfffd; */ /* error */
   4071                         }
   4072                     } else {
   4073                         /* Keep unmatched surrogate code points */
   4074                         /* c = 0xfffd; */ /* error */
   4075                     }
   4076                 }
   4077                 q += unicode_to_utf8(q, c);
   4078             }
   4079         }
   4080     }
   4081 
   4082     *q = '\0';
   4083     str_new->len = q - str_new->u.str8;
   4084     JS_FreeValue(ctx, val);
   4085     if (plen)
   4086         *plen = str_new->len;
   4087     return (const char *)str_new->u.str8;
   4088  fail:
   4089     if (plen)
   4090         *plen = 0;
   4091     return NULL;
   4092 }
   4093 
   4094 void JS_FreeCString(JSContext *ctx, const char *ptr)
   4095 {
   4096     JSString *p;
   4097     if (!ptr)
   4098         return;
   4099     /* purposely removing constness */
   4100     p = container_of(ptr, JSString, u);
   4101     JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
   4102 }
   4103 
   4104 static int memcmp16_8(const uint16_t *src1, const uint8_t *src2, int len)
   4105 {
   4106     int c, i;
   4107     for(i = 0; i < len; i++) {
   4108         c = src1[i] - src2[i];
   4109         if (c != 0)
   4110             return c;
   4111     }
   4112     return 0;
   4113 }
   4114 
   4115 static int memcmp16(const uint16_t *src1, const uint16_t *src2, int len)
   4116 {
   4117     int c, i;
   4118     for(i = 0; i < len; i++) {
   4119         c = src1[i] - src2[i];
   4120         if (c != 0)
   4121             return c;
   4122     }
   4123     return 0;
   4124 }
   4125 
   4126 static int js_string_memcmp(const JSString *p1, const JSString *p2, int len)
   4127 {
   4128     int res;
   4129 
   4130     if (likely(!p1->is_wide_char)) {
   4131         if (likely(!p2->is_wide_char))
   4132             res = memcmp(p1->u.str8, p2->u.str8, len);
   4133         else
   4134             res = -memcmp16_8(p2->u.str16, p1->u.str8, len);
   4135     } else {
   4136         if (!p2->is_wide_char)
   4137             res = memcmp16_8(p1->u.str16, p2->u.str8, len);
   4138         else
   4139             res = memcmp16(p1->u.str16, p2->u.str16, len);
   4140     }
   4141     return res;
   4142 }
   4143 
   4144 /* return < 0, 0 or > 0 */
   4145 static int js_string_compare(JSContext *ctx,
   4146                              const JSString *p1, const JSString *p2)
   4147 {
   4148     int res, len;
   4149     len = min_int(p1->len, p2->len);
   4150     res = js_string_memcmp(p1, p2, len);
   4151     if (res == 0) {
   4152         if (p1->len == p2->len)
   4153             res = 0;
   4154         else if (p1->len < p2->len)
   4155             res = -1;
   4156         else
   4157             res = 1;
   4158     }
   4159     return res;
   4160 }
   4161 
   4162 static void copy_str16(uint16_t *dst, const JSString *p, int offset, int len)
   4163 {
   4164     if (p->is_wide_char) {
   4165         memcpy(dst, p->u.str16 + offset, len * 2);
   4166     } else {
   4167         const uint8_t *src1 = p->u.str8 + offset;
   4168         int i;
   4169 
   4170         for(i = 0; i < len; i++)
   4171             dst[i] = src1[i];
   4172     }
   4173 }
   4174 
   4175 static JSValue JS_ConcatString1(JSContext *ctx,
   4176                                 const JSString *p1, const JSString *p2)
   4177 {
   4178     JSString *p;
   4179     uint32_t len;
   4180     int is_wide_char;
   4181 
   4182     len = p1->len + p2->len;
   4183     if (len > JS_STRING_LEN_MAX)
   4184         return JS_ThrowInternalError(ctx, "string too long");
   4185     is_wide_char = p1->is_wide_char | p2->is_wide_char;
   4186     p = js_alloc_string(ctx, len, is_wide_char);
   4187     if (!p)
   4188         return JS_EXCEPTION;
   4189     if (!is_wide_char) {
   4190         memcpy(p->u.str8, p1->u.str8, p1->len);
   4191         memcpy(p->u.str8 + p1->len, p2->u.str8, p2->len);
   4192         p->u.str8[len] = '\0';
   4193     } else {
   4194         copy_str16(p->u.str16, p1, 0, p1->len);
   4195         copy_str16(p->u.str16 + p1->len, p2, 0, p2->len);
   4196     }
   4197     return JS_MKPTR(JS_TAG_STRING, p);
   4198 }
   4199 
   4200 static BOOL JS_ConcatStringInPlace(JSContext *ctx, JSString *p1, JSValueConst op2) {
   4201     if (JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) {
   4202         JSString *p2 = JS_VALUE_GET_STRING(op2);
   4203         size_t size1;
   4204 
   4205         if (p2->len == 0)
   4206             return TRUE;
   4207         if (p1->header.ref_count != 1)
   4208             return FALSE;
   4209         size1 = js_malloc_usable_size(ctx, p1);
   4210         if (p1->is_wide_char) {
   4211             if (size1 >= sizeof(*p1) + ((p1->len + p2->len) << 1)) {
   4212                 if (p2->is_wide_char) {
   4213                     memcpy(p1->u.str16 + p1->len, p2->u.str16, p2->len << 1);
   4214                     p1->len += p2->len;
   4215                     return TRUE;
   4216                 } else {
   4217                     size_t i;
   4218                     for (i = 0; i < p2->len; i++) {
   4219                         p1->u.str16[p1->len++] = p2->u.str8[i];
   4220                     }
   4221                     return TRUE;
   4222                 }
   4223             }
   4224         } else if (!p2->is_wide_char) {
   4225             if (size1 >= sizeof(*p1) + p1->len + p2->len + 1) {
   4226                 memcpy(p1->u.str8 + p1->len, p2->u.str8, p2->len);
   4227                 p1->len += p2->len;
   4228                 p1->u.str8[p1->len] = '\0';
   4229                 return TRUE;
   4230             }
   4231         }
   4232     }
   4233     return FALSE;
   4234 }
   4235 
   4236 /* op1 and op2 are converted to strings. For convenience, op1 or op2 =
   4237    JS_EXCEPTION are accepted and return JS_EXCEPTION.  */
   4238 static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2)
   4239 {
   4240     JSValue ret;
   4241     JSString *p1, *p2;
   4242 
   4243     if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_STRING)) {
   4244         op1 = JS_ToStringFree(ctx, op1);
   4245         if (JS_IsException(op1)) {
   4246             JS_FreeValue(ctx, op2);
   4247             return JS_EXCEPTION;
   4248         }
   4249     }
   4250     if (unlikely(JS_VALUE_GET_TAG(op2) != JS_TAG_STRING)) {
   4251         op2 = JS_ToStringFree(ctx, op2);
   4252         if (JS_IsException(op2)) {
   4253             JS_FreeValue(ctx, op1);
   4254             return JS_EXCEPTION;
   4255         }
   4256     }
   4257     p1 = JS_VALUE_GET_STRING(op1);
   4258     if (JS_ConcatStringInPlace(ctx, p1, op2)) {
   4259         JS_FreeValue(ctx, op2);
   4260         return op1;
   4261     }
   4262     p2 = JS_VALUE_GET_STRING(op2);
   4263     ret = JS_ConcatString1(ctx, p1, p2);
   4264     JS_FreeValue(ctx, op1);
   4265     JS_FreeValue(ctx, op2);
   4266     return ret;
   4267 }
   4268 
   4269 /* Shape support */
   4270 
   4271 static inline size_t get_shape_size(size_t hash_size, size_t prop_size)
   4272 {
   4273     return hash_size * sizeof(uint32_t) + sizeof(JSShape) +
   4274         prop_size * sizeof(JSShapeProperty);
   4275 }
   4276 
   4277 static inline JSShape *get_shape_from_alloc(void *sh_alloc, size_t hash_size)
   4278 {
   4279     return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size);
   4280 }
   4281 
   4282 static inline uint32_t *prop_hash_end(JSShape *sh)
   4283 {
   4284     return (uint32_t *)sh;
   4285 }
   4286 
   4287 static inline void *get_alloc_from_shape(JSShape *sh)
   4288 {
   4289     return prop_hash_end(sh) - ((intptr_t)sh->prop_hash_mask + 1);
   4290 }
   4291 
   4292 static inline JSShapeProperty *get_shape_prop(JSShape *sh)
   4293 {
   4294     return sh->prop;
   4295 }
   4296 
   4297 static int init_shape_hash(JSRuntime *rt)
   4298 {
   4299     rt->shape_hash_bits = 4;   /* 16 shapes */
   4300     rt->shape_hash_size = 1 << rt->shape_hash_bits;
   4301     rt->shape_hash_count = 0;
   4302     rt->shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) *
   4303                                    rt->shape_hash_size);
   4304     if (!rt->shape_hash)
   4305         return -1;
   4306     return 0;
   4307 }
   4308 
   4309 /* same magic hash multiplier as the Linux kernel */
   4310 static uint32_t shape_hash(uint32_t h, uint32_t val)
   4311 {
   4312     return (h + val) * 0x9e370001;
   4313 }
   4314 
   4315 /* truncate the shape hash to 'hash_bits' bits */
   4316 static uint32_t get_shape_hash(uint32_t h, int hash_bits)
   4317 {
   4318     return h >> (32 - hash_bits);
   4319 }
   4320 
   4321 static uint32_t shape_initial_hash(JSObject *proto)
   4322 {
   4323     uint32_t h;
   4324     h = shape_hash(1, (uintptr_t)proto);
   4325     if (sizeof(proto) > 4)
   4326         h = shape_hash(h, (uint64_t)(uintptr_t)proto >> 32);
   4327     return h;
   4328 }
   4329 
   4330 static int resize_shape_hash(JSRuntime *rt, int new_shape_hash_bits)
   4331 {
   4332     int new_shape_hash_size, i;
   4333     uint32_t h;
   4334     JSShape **new_shape_hash, *sh, *sh_next;
   4335 
   4336     new_shape_hash_size = 1 << new_shape_hash_bits;
   4337     new_shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) *
   4338                                    new_shape_hash_size);
   4339     if (!new_shape_hash)
   4340         return -1;
   4341     for(i = 0; i < rt->shape_hash_size; i++) {
   4342         for(sh = rt->shape_hash[i]; sh != NULL; sh = sh_next) {
   4343             sh_next = sh->shape_hash_next;
   4344             h = get_shape_hash(sh->hash, new_shape_hash_bits);
   4345             sh->shape_hash_next = new_shape_hash[h];
   4346             new_shape_hash[h] = sh;
   4347         }
   4348     }
   4349     js_free_rt(rt, rt->shape_hash);
   4350     rt->shape_hash_bits = new_shape_hash_bits;
   4351     rt->shape_hash_size = new_shape_hash_size;
   4352     rt->shape_hash = new_shape_hash;
   4353     return 0;
   4354 }
   4355 
   4356 static void js_shape_hash_link(JSRuntime *rt, JSShape *sh)
   4357 {
   4358     uint32_t h;
   4359     h = get_shape_hash(sh->hash, rt->shape_hash_bits);
   4360     sh->shape_hash_next = rt->shape_hash[h];
   4361     rt->shape_hash[h] = sh;
   4362     rt->shape_hash_count++;
   4363 }
   4364 
   4365 static void js_shape_hash_unlink(JSRuntime *rt, JSShape *sh)
   4366 {
   4367     uint32_t h;
   4368     JSShape **psh;
   4369 
   4370     h = get_shape_hash(sh->hash, rt->shape_hash_bits);
   4371     psh = &rt->shape_hash[h];
   4372     while (*psh != sh)
   4373         psh = &(*psh)->shape_hash_next;
   4374     *psh = sh->shape_hash_next;
   4375     rt->shape_hash_count--;
   4376 }
   4377 
   4378 /* create a new empty shape with prototype 'proto' */
   4379 static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto,
   4380                                         int hash_size, int prop_size)
   4381 {
   4382     JSRuntime *rt = ctx->rt;
   4383     void *sh_alloc;
   4384     JSShape *sh;
   4385 
   4386     /* resize the shape hash table if necessary */
   4387     if (2 * (rt->shape_hash_count + 1) > rt->shape_hash_size) {
   4388         resize_shape_hash(rt, rt->shape_hash_bits + 1);
   4389     }
   4390 
   4391     sh_alloc = js_malloc(ctx, get_shape_size(hash_size, prop_size));
   4392     if (!sh_alloc)
   4393         return NULL;
   4394     sh = get_shape_from_alloc(sh_alloc, hash_size);
   4395     sh->header.ref_count = 1;
   4396     add_gc_object(rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);
   4397     if (proto)
   4398         JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, proto));
   4399     sh->proto = proto;
   4400     memset(prop_hash_end(sh) - hash_size, 0, sizeof(prop_hash_end(sh)[0]) *
   4401            hash_size);
   4402     sh->prop_hash_mask = hash_size - 1;
   4403     sh->prop_size = prop_size;
   4404     sh->prop_count = 0;
   4405     sh->deleted_prop_count = 0;
   4406 
   4407     /* insert in the hash table */
   4408     sh->hash = shape_initial_hash(proto);
   4409     sh->is_hashed = TRUE;
   4410     sh->has_small_array_index = FALSE;
   4411     js_shape_hash_link(ctx->rt, sh);
   4412     return sh;
   4413 }
   4414 
   4415 static JSShape *js_new_shape(JSContext *ctx, JSObject *proto)
   4416 {
   4417     return js_new_shape2(ctx, proto, JS_PROP_INITIAL_HASH_SIZE,
   4418                          JS_PROP_INITIAL_SIZE);
   4419 }
   4420 
   4421 /* The shape is cloned. The new shape is not inserted in the shape
   4422    hash table */
   4423 static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1)
   4424 {
   4425     JSShape *sh;
   4426     void *sh_alloc, *sh_alloc1;
   4427     size_t size;
   4428     JSShapeProperty *pr;
   4429     uint32_t i, hash_size;
   4430 
   4431     hash_size = sh1->prop_hash_mask + 1;
   4432     size = get_shape_size(hash_size, sh1->prop_size);
   4433     sh_alloc = js_malloc(ctx, size);
   4434     if (!sh_alloc)
   4435         return NULL;
   4436     sh_alloc1 = get_alloc_from_shape(sh1);
   4437     memcpy(sh_alloc, sh_alloc1, size);
   4438     sh = get_shape_from_alloc(sh_alloc, hash_size);
   4439     sh->header.ref_count = 1;
   4440     add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);
   4441     sh->is_hashed = FALSE;
   4442     if (sh->proto) {
   4443         JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
   4444     }
   4445     for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {
   4446         JS_DupAtom(ctx, pr->atom);
   4447     }
   4448     return sh;
   4449 }
   4450 
   4451 static JSShape *js_dup_shape(JSShape *sh)
   4452 {
   4453     sh->header.ref_count++;
   4454     return sh;
   4455 }
   4456 
   4457 static void js_free_shape0(JSRuntime *rt, JSShape *sh)
   4458 {
   4459     uint32_t i;
   4460     JSShapeProperty *pr;
   4461 
   4462     assert(sh->header.ref_count == 0);
   4463     if (sh->is_hashed)
   4464         js_shape_hash_unlink(rt, sh);
   4465     if (sh->proto != NULL) {
   4466         JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
   4467     }
   4468     pr = get_shape_prop(sh);
   4469     for(i = 0; i < sh->prop_count; i++) {
   4470         JS_FreeAtomRT(rt, pr->atom);
   4471         pr++;
   4472     }
   4473     remove_gc_object(&sh->header);
   4474     js_free_rt(rt, get_alloc_from_shape(sh));
   4475 }
   4476 
   4477 static void js_free_shape(JSRuntime *rt, JSShape *sh)
   4478 {
   4479     if (unlikely(--sh->header.ref_count <= 0)) {
   4480         js_free_shape0(rt, sh);
   4481     }
   4482 }
   4483 
   4484 static void js_free_shape_null(JSRuntime *rt, JSShape *sh)
   4485 {
   4486     if (sh)
   4487         js_free_shape(rt, sh);
   4488 }
   4489 
   4490 /* make space to hold at least 'count' properties */
   4491 static no_inline int resize_properties(JSContext *ctx, JSShape **psh,
   4492                                        JSObject *p, uint32_t count)
   4493 {
   4494     JSShape *sh;
   4495     uint32_t new_size, new_hash_size, new_hash_mask, i;
   4496     JSShapeProperty *pr;
   4497     void *sh_alloc;
   4498     intptr_t h;
   4499     JSShape *old_sh;
   4500 
   4501     sh = *psh;
   4502     new_size = max_int(count, sh->prop_size * 3 / 2);
   4503     /* Reallocate prop array first to avoid crash or size inconsistency
   4504        in case of memory allocation failure */
   4505     if (p) {
   4506         JSProperty *new_prop;
   4507         new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size);
   4508         if (unlikely(!new_prop))
   4509             return -1;
   4510         p->prop = new_prop;
   4511     }
   4512     new_hash_size = sh->prop_hash_mask + 1;
   4513     while (new_hash_size < new_size)
   4514         new_hash_size = 2 * new_hash_size;
   4515     /* resize the property shapes. Using js_realloc() is not possible in
   4516        case the GC runs during the allocation */
   4517     old_sh = sh;
   4518     sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));
   4519     if (!sh_alloc)
   4520         return -1;
   4521     sh = get_shape_from_alloc(sh_alloc, new_hash_size);
   4522     list_del(&old_sh->header.link);
   4523     /* copy all the shape properties */
   4524     memcpy(sh, old_sh,
   4525            sizeof(JSShape) + sizeof(sh->prop[0]) * old_sh->prop_count);
   4526     list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
   4527 
   4528     if (new_hash_size != (sh->prop_hash_mask + 1)) {
   4529         /* resize the hash table and the properties */
   4530         new_hash_mask = new_hash_size - 1;
   4531         sh->prop_hash_mask = new_hash_mask;
   4532         memset(prop_hash_end(sh) - new_hash_size, 0,
   4533                sizeof(prop_hash_end(sh)[0]) * new_hash_size);
   4534         for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) {
   4535             if (pr->atom != JS_ATOM_NULL) {
   4536                 h = ((uintptr_t)pr->atom & new_hash_mask);
   4537                 pr->hash_next = prop_hash_end(sh)[-h - 1];
   4538                 prop_hash_end(sh)[-h - 1] = i + 1;
   4539             }
   4540         }
   4541     } else {
   4542         /* just copy the previous hash table */
   4543         memcpy(prop_hash_end(sh) - new_hash_size, prop_hash_end(old_sh) - new_hash_size,
   4544                sizeof(prop_hash_end(sh)[0]) * new_hash_size);
   4545     }
   4546     js_free(ctx, get_alloc_from_shape(old_sh));
   4547     *psh = sh;
   4548     sh->prop_size = new_size;
   4549     return 0;
   4550 }
   4551 
   4552 /* remove the deleted properties. */
   4553 static int compact_properties(JSContext *ctx, JSObject *p)
   4554 {
   4555     JSShape *sh, *old_sh;
   4556     void *sh_alloc;
   4557     intptr_t h;
   4558     uint32_t new_hash_size, i, j, new_hash_mask, new_size;
   4559     JSShapeProperty *old_pr, *pr;
   4560     JSProperty *prop, *new_prop;
   4561 
   4562     sh = p->shape;
   4563     assert(!sh->is_hashed);
   4564 
   4565     new_size = max_int(JS_PROP_INITIAL_SIZE,
   4566                        sh->prop_count - sh->deleted_prop_count);
   4567     assert(new_size <= sh->prop_size);
   4568 
   4569     new_hash_size = sh->prop_hash_mask + 1;
   4570     while ((new_hash_size / 2) >= new_size)
   4571         new_hash_size = new_hash_size / 2;
   4572     new_hash_mask = new_hash_size - 1;
   4573 
   4574     /* resize the hash table and the properties */
   4575     old_sh = sh;
   4576     sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));
   4577     if (!sh_alloc)
   4578         return -1;
   4579     sh = get_shape_from_alloc(sh_alloc, new_hash_size);
   4580     list_del(&old_sh->header.link);
   4581     memcpy(sh, old_sh, sizeof(JSShape));
   4582     list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
   4583 
   4584     memset(prop_hash_end(sh) - new_hash_size, 0,
   4585            sizeof(prop_hash_end(sh)[0]) * new_hash_size);
   4586 
   4587     j = 0;
   4588     old_pr = old_sh->prop;
   4589     pr = sh->prop;
   4590     prop = p->prop;
   4591     for(i = 0; i < sh->prop_count; i++) {
   4592         if (old_pr->atom != JS_ATOM_NULL) {
   4593             pr->atom = old_pr->atom;
   4594             pr->flags = old_pr->flags;
   4595             h = ((uintptr_t)old_pr->atom & new_hash_mask);
   4596             pr->hash_next = prop_hash_end(sh)[-h - 1];
   4597             prop_hash_end(sh)[-h - 1] = j + 1;
   4598             prop[j] = prop[i];
   4599             j++;
   4600             pr++;
   4601         }
   4602         old_pr++;
   4603     }
   4604     assert(j == (sh->prop_count - sh->deleted_prop_count));
   4605     sh->prop_hash_mask = new_hash_mask;
   4606     sh->prop_size = new_size;
   4607     sh->deleted_prop_count = 0;
   4608     sh->prop_count = j;
   4609 
   4610     p->shape = sh;
   4611     js_free(ctx, get_alloc_from_shape(old_sh));
   4612 
   4613     /* reduce the size of the object properties */
   4614     new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size);
   4615     if (new_prop)
   4616         p->prop = new_prop;
   4617     return 0;
   4618 }
   4619 
   4620 static int add_shape_property(JSContext *ctx, JSShape **psh,
   4621                               JSObject *p, JSAtom atom, int prop_flags)
   4622 {
   4623     JSRuntime *rt = ctx->rt;
   4624     JSShape *sh = *psh;
   4625     JSShapeProperty *pr, *prop;
   4626     uint32_t hash_mask, new_shape_hash = 0;
   4627     intptr_t h;
   4628 
   4629     /* update the shape hash */
   4630     if (sh->is_hashed) {
   4631         js_shape_hash_unlink(rt, sh);
   4632         new_shape_hash = shape_hash(shape_hash(sh->hash, atom), prop_flags);
   4633     }
   4634 
   4635     if (unlikely(sh->prop_count >= sh->prop_size)) {
   4636         if (resize_properties(ctx, psh, p, sh->prop_count + 1)) {
   4637             /* in case of error, reinsert in the hash table.
   4638                sh is still valid if resize_properties() failed */
   4639             if (sh->is_hashed)
   4640                 js_shape_hash_link(rt, sh);
   4641             return -1;
   4642         }
   4643         sh = *psh;
   4644     }
   4645     if (sh->is_hashed) {
   4646         sh->hash = new_shape_hash;
   4647         js_shape_hash_link(rt, sh);
   4648     }
   4649     /* Initialize the new shape property.
   4650        The object property at p->prop[sh->prop_count] is uninitialized */
   4651     prop = get_shape_prop(sh);
   4652     pr = &prop[sh->prop_count++];
   4653     pr->atom = JS_DupAtom(ctx, atom);
   4654     pr->flags = prop_flags;
   4655     sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom);
   4656     /* add in hash table */
   4657     hash_mask = sh->prop_hash_mask;
   4658     h = atom & hash_mask;
   4659     pr->hash_next = prop_hash_end(sh)[-h - 1];
   4660     prop_hash_end(sh)[-h - 1] = sh->prop_count;
   4661     return 0;
   4662 }
   4663 
   4664 /* find a hashed empty shape matching the prototype. Return NULL if
   4665    not found */
   4666 static JSShape *find_hashed_shape_proto(JSRuntime *rt, JSObject *proto)
   4667 {
   4668     JSShape *sh1;
   4669     uint32_t h, h1;
   4670 
   4671     h = shape_initial_hash(proto);
   4672     h1 = get_shape_hash(h, rt->shape_hash_bits);
   4673     for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) {
   4674         if (sh1->hash == h &&
   4675             sh1->proto == proto &&
   4676             sh1->prop_count == 0) {
   4677             return sh1;
   4678         }
   4679     }
   4680     return NULL;
   4681 }
   4682 
   4683 /* find a hashed shape matching sh + (prop, prop_flags). Return NULL if
   4684    not found */
   4685 static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh,
   4686                                        JSAtom atom, int prop_flags)
   4687 {
   4688     JSShape *sh1;
   4689     uint32_t h, h1, i, n;
   4690 
   4691     h = sh->hash;
   4692     h = shape_hash(h, atom);
   4693     h = shape_hash(h, prop_flags);
   4694     h1 = get_shape_hash(h, rt->shape_hash_bits);
   4695     for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) {
   4696         /* we test the hash first so that the rest is done only if the
   4697            shapes really match */
   4698         if (sh1->hash == h &&
   4699             sh1->proto == sh->proto &&
   4700             sh1->prop_count == ((n = sh->prop_count) + 1)) {
   4701             for(i = 0; i < n; i++) {
   4702                 if (unlikely(sh1->prop[i].atom != sh->prop[i].atom) ||
   4703                     unlikely(sh1->prop[i].flags != sh->prop[i].flags))
   4704                     goto next;
   4705             }
   4706             if (unlikely(sh1->prop[n].atom != atom) ||
   4707                 unlikely(sh1->prop[n].flags != prop_flags))
   4708                 goto next;
   4709             return sh1;
   4710         }
   4711     next: ;
   4712     }
   4713     return NULL;
   4714 }
   4715 
   4716 static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh)
   4717 {
   4718     char atom_buf[ATOM_GET_STR_BUF_SIZE];
   4719     int j;
   4720 
   4721     /* XXX: should output readable class prototype */
   4722     printf("%5d %3d%c %14p %5d %5d", i,
   4723            sh->header.ref_count, " *"[sh->is_hashed],
   4724            (void *)sh->proto, sh->prop_size, sh->prop_count);
   4725     for(j = 0; j < sh->prop_count; j++) {
   4726         printf(" %s", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf),
   4727                                       sh->prop[j].atom));
   4728     }
   4729     printf("\n");
   4730 }
   4731 
   4732 static __maybe_unused void JS_DumpShapes(JSRuntime *rt)
   4733 {
   4734     int i;
   4735     JSShape *sh;
   4736     struct list_head *el;
   4737     JSObject *p;
   4738     JSGCObjectHeader *gp;
   4739 
   4740     printf("JSShapes: {\n");
   4741     printf("%5s %4s %14s %5s %5s %s\n", "SLOT", "REFS", "PROTO", "SIZE", "COUNT", "PROPS");
   4742     for(i = 0; i < rt->shape_hash_size; i++) {
   4743         for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) {
   4744             JS_DumpShape(rt, i, sh);
   4745             assert(sh->is_hashed);
   4746         }
   4747     }
   4748     /* dump non-hashed shapes */
   4749     list_for_each(el, &rt->gc_obj_list) {
   4750         gp = list_entry(el, JSGCObjectHeader, link);
   4751         if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
   4752             p = (JSObject *)gp;
   4753             if (!p->shape->is_hashed) {
   4754                 JS_DumpShape(rt, -1, p->shape);
   4755             }
   4756         }
   4757     }
   4758     printf("}\n");
   4759 }
   4760 
   4761 static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID class_id)
   4762 {
   4763     JSObject *p;
   4764 
   4765     js_trigger_gc(ctx->rt, sizeof(JSObject));
   4766     p = js_malloc(ctx, sizeof(JSObject));
   4767     if (unlikely(!p))
   4768         goto fail;
   4769     p->class_id = class_id;
   4770     p->extensible = TRUE;
   4771     p->free_mark = 0;
   4772     p->is_exotic = 0;
   4773     p->fast_array = 0;
   4774     p->is_constructor = 0;
   4775     p->is_uncatchable_error = 0;
   4776     p->tmp_mark = 0;
   4777     p->is_HTMLDDA = 0;
   4778     p->first_weak_ref = NULL;
   4779     p->u.opaque = NULL;
   4780     p->shape = sh;
   4781     p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size);
   4782     if (unlikely(!p->prop)) {
   4783         js_free(ctx, p);
   4784     fail:
   4785         js_free_shape(ctx->rt, sh);
   4786         return JS_EXCEPTION;
   4787     }
   4788 
   4789     switch(class_id) {
   4790     case JS_CLASS_OBJECT:
   4791         break;
   4792     case JS_CLASS_ARRAY:
   4793         {
   4794             JSProperty *pr;
   4795             p->is_exotic = 1;
   4796             p->fast_array = 1;
   4797             p->u.array.u.values = NULL;
   4798             p->u.array.count = 0;
   4799             p->u.array.u1.size = 0;
   4800             /* the length property is always the first one */
   4801             if (likely(sh == ctx->array_shape)) {
   4802                 pr = &p->prop[0];
   4803             } else {
   4804                 /* only used for the first array */
   4805                 /* cannot fail */
   4806                 pr = add_property(ctx, p, JS_ATOM_length,
   4807                                   JS_PROP_WRITABLE | JS_PROP_LENGTH);
   4808             }
   4809             pr->u.value = JS_NewInt32(ctx, 0);
   4810         }
   4811         break;
   4812     case JS_CLASS_C_FUNCTION:
   4813         p->prop[0].u.value = JS_UNDEFINED;
   4814         break;
   4815     case JS_CLASS_ARGUMENTS:
   4816     case JS_CLASS_UINT8C_ARRAY:
   4817     case JS_CLASS_INT8_ARRAY:
   4818     case JS_CLASS_UINT8_ARRAY:
   4819     case JS_CLASS_INT16_ARRAY:
   4820     case JS_CLASS_UINT16_ARRAY:
   4821     case JS_CLASS_INT32_ARRAY:
   4822     case JS_CLASS_UINT32_ARRAY:
   4823     case JS_CLASS_BIG_INT64_ARRAY:
   4824     case JS_CLASS_BIG_UINT64_ARRAY:
   4825     case JS_CLASS_FLOAT32_ARRAY:
   4826     case JS_CLASS_FLOAT64_ARRAY:
   4827         p->is_exotic = 1;
   4828         p->fast_array = 1;
   4829         p->u.array.u.ptr = NULL;
   4830         p->u.array.count = 0;
   4831         break;
   4832     case JS_CLASS_DATAVIEW:
   4833         p->u.array.u.ptr = NULL;
   4834         p->u.array.count = 0;
   4835         break;
   4836     case JS_CLASS_NUMBER:
   4837     case JS_CLASS_STRING:
   4838     case JS_CLASS_BOOLEAN:
   4839     case JS_CLASS_SYMBOL:
   4840     case JS_CLASS_DATE:
   4841     case JS_CLASS_BIG_INT:
   4842 #ifdef CONFIG_BIGNUM
   4843     case JS_CLASS_BIG_FLOAT:
   4844     case JS_CLASS_BIG_DECIMAL:
   4845 #endif
   4846         p->u.object_data = JS_UNDEFINED;
   4847         goto set_exotic;
   4848     case JS_CLASS_REGEXP:
   4849         p->u.regexp.pattern = NULL;
   4850         p->u.regexp.bytecode = NULL;
   4851         goto set_exotic;
   4852     default:
   4853     set_exotic:
   4854         if (ctx->rt->class_array[class_id].exotic) {
   4855             p->is_exotic = 1;
   4856         }
   4857         break;
   4858     }
   4859     p->header.ref_count = 1;
   4860     add_gc_object(ctx->rt, &p->header, JS_GC_OBJ_TYPE_JS_OBJECT);
   4861     return JS_MKPTR(JS_TAG_OBJECT, p);
   4862 }
   4863 
   4864 static JSObject *get_proto_obj(JSValueConst proto_val)
   4865 {
   4866     if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT)
   4867         return NULL;
   4868     else
   4869         return JS_VALUE_GET_OBJ(proto_val);
   4870 }
   4871 
   4872 /* WARNING: proto must be an object or JS_NULL */
   4873 JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto_val,
   4874                                JSClassID class_id)
   4875 {
   4876     JSShape *sh;
   4877     JSObject *proto;
   4878 
   4879     proto = get_proto_obj(proto_val);
   4880     sh = find_hashed_shape_proto(ctx->rt, proto);
   4881     if (likely(sh)) {
   4882         sh = js_dup_shape(sh);
   4883     } else {
   4884         sh = js_new_shape(ctx, proto);
   4885         if (!sh)
   4886             return JS_EXCEPTION;
   4887     }
   4888     return JS_NewObjectFromShape(ctx, sh, class_id);
   4889 }
   4890 
   4891 #if 0
   4892 static JSValue JS_GetObjectData(JSContext *ctx, JSValueConst obj)
   4893 {
   4894     JSObject *p;
   4895 
   4896     if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
   4897         p = JS_VALUE_GET_OBJ(obj);
   4898         switch(p->class_id) {
   4899         case JS_CLASS_NUMBER:
   4900         case JS_CLASS_STRING:
   4901         case JS_CLASS_BOOLEAN:
   4902         case JS_CLASS_SYMBOL:
   4903         case JS_CLASS_DATE:
   4904         case JS_CLASS_BIG_INT:
   4905 #ifdef CONFIG_BIGNUM
   4906         case JS_CLASS_BIG_FLOAT:
   4907         case JS_CLASS_BIG_DECIMAL:
   4908 #endif
   4909             return JS_DupValue(ctx, p->u.object_data);
   4910         }
   4911     }
   4912     return JS_UNDEFINED;
   4913 }
   4914 #endif
   4915 
   4916 static int JS_SetObjectData(JSContext *ctx, JSValueConst obj, JSValue val)
   4917 {
   4918     JSObject *p;
   4919 
   4920     if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
   4921         p = JS_VALUE_GET_OBJ(obj);
   4922         switch(p->class_id) {
   4923         case JS_CLASS_NUMBER:
   4924         case JS_CLASS_STRING:
   4925         case JS_CLASS_BOOLEAN:
   4926         case JS_CLASS_SYMBOL:
   4927         case JS_CLASS_DATE:
   4928         case JS_CLASS_BIG_INT:
   4929 #ifdef CONFIG_BIGNUM
   4930         case JS_CLASS_BIG_FLOAT:
   4931         case JS_CLASS_BIG_DECIMAL:
   4932 #endif
   4933             JS_FreeValue(ctx, p->u.object_data);
   4934             p->u.object_data = val;
   4935             return 0;
   4936         }
   4937     }
   4938     JS_FreeValue(ctx, val);
   4939     if (!JS_IsException(obj))
   4940         JS_ThrowTypeError(ctx, "invalid object type");
   4941     return -1;
   4942 }
   4943 
   4944 JSValue JS_NewObjectClass(JSContext *ctx, int class_id)
   4945 {
   4946     return JS_NewObjectProtoClass(ctx, ctx->class_proto[class_id], class_id);
   4947 }
   4948 
   4949 JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto)
   4950 {
   4951     return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT);
   4952 }
   4953 
   4954 JSValue JS_NewArray(JSContext *ctx)
   4955 {
   4956     return JS_NewObjectFromShape(ctx, js_dup_shape(ctx->array_shape),
   4957                                  JS_CLASS_ARRAY);
   4958 }
   4959 
   4960 JSValue JS_NewObject(JSContext *ctx)
   4961 {
   4962     /* inline JS_NewObjectClass(ctx, JS_CLASS_OBJECT); */
   4963     return JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_OBJECT);
   4964 }
   4965 
   4966 static void js_function_set_properties(JSContext *ctx, JSValueConst func_obj,
   4967                                        JSAtom name, int len)
   4968 {
   4969     /* ES6 feature non compatible with ES5.1: length is configurable */
   4970     JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, JS_NewInt32(ctx, len),
   4971                            JS_PROP_CONFIGURABLE);
   4972     JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name,
   4973                            JS_AtomToString(ctx, name), JS_PROP_CONFIGURABLE);
   4974 }
   4975 
   4976 static BOOL js_class_has_bytecode(JSClassID class_id)
   4977 {
   4978     return (class_id == JS_CLASS_BYTECODE_FUNCTION ||
   4979             class_id == JS_CLASS_GENERATOR_FUNCTION ||
   4980             class_id == JS_CLASS_ASYNC_FUNCTION ||
   4981             class_id == JS_CLASS_ASYNC_GENERATOR_FUNCTION);
   4982 }
   4983 
   4984 /* return NULL without exception if not a function or no bytecode */
   4985 static JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val)
   4986 {
   4987     JSObject *p;
   4988     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
   4989         return NULL;
   4990     p = JS_VALUE_GET_OBJ(val);
   4991     if (!js_class_has_bytecode(p->class_id))
   4992         return NULL;
   4993     return p->u.func.function_bytecode;
   4994 }
   4995 
   4996 static void js_method_set_home_object(JSContext *ctx, JSValueConst func_obj,
   4997                                       JSValueConst home_obj)
   4998 {
   4999     JSObject *p, *p1;
   5000     JSFunctionBytecode *b;
   5001 
   5002     if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)
   5003         return;
   5004     p = JS_VALUE_GET_OBJ(func_obj);
   5005     if (!js_class_has_bytecode(p->class_id))
   5006         return;
   5007     b = p->u.func.function_bytecode;
   5008     if (b->need_home_object) {
   5009         p1 = p->u.func.home_object;
   5010         if (p1) {
   5011             JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
   5012         }
   5013         if (JS_VALUE_GET_TAG(home_obj) == JS_TAG_OBJECT)
   5014             p1 = JS_VALUE_GET_OBJ(JS_DupValue(ctx, home_obj));
   5015         else
   5016             p1 = NULL;
   5017         p->u.func.home_object = p1;
   5018     }
   5019 }
   5020 
   5021 static JSValue js_get_function_name(JSContext *ctx, JSAtom name)
   5022 {
   5023     JSValue name_str;
   5024 
   5025     name_str = JS_AtomToString(ctx, name);
   5026     if (JS_AtomSymbolHasDescription(ctx, name)) {
   5027         name_str = JS_ConcatString3(ctx, "[", name_str, "]");
   5028     }
   5029     return name_str;
   5030 }
   5031 
   5032 /* Modify the name of a method according to the atom and
   5033    'flags'. 'flags' is a bitmask of JS_PROP_HAS_GET and
   5034    JS_PROP_HAS_SET. Also set the home object of the method.
   5035    Return < 0 if exception. */
   5036 static int js_method_set_properties(JSContext *ctx, JSValueConst func_obj,
   5037                                     JSAtom name, int flags, JSValueConst home_obj)
   5038 {
   5039     JSValue name_str;
   5040 
   5041     name_str = js_get_function_name(ctx, name);
   5042     if (flags & JS_PROP_HAS_GET) {
   5043         name_str = JS_ConcatString3(ctx, "get ", name_str, "");
   5044     } else if (flags & JS_PROP_HAS_SET) {
   5045         name_str = JS_ConcatString3(ctx, "set ", name_str, "");
   5046     }
   5047     if (JS_IsException(name_str))
   5048         return -1;
   5049     if (JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name_str,
   5050                                JS_PROP_CONFIGURABLE) < 0)
   5051         return -1;
   5052     js_method_set_home_object(ctx, func_obj, home_obj);
   5053     return 0;
   5054 }
   5055 
   5056 /* Note: at least 'length' arguments will be readable in 'argv' */
   5057 static JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func,
   5058                                 const char *name,
   5059                                 int length, JSCFunctionEnum cproto, int magic,
   5060                                 JSValueConst proto_val)
   5061 {
   5062     JSValue func_obj;
   5063     JSObject *p;
   5064     JSAtom name_atom;
   5065 
   5066     func_obj = JS_NewObjectProtoClass(ctx, proto_val, JS_CLASS_C_FUNCTION);
   5067     if (JS_IsException(func_obj))
   5068         return func_obj;
   5069     p = JS_VALUE_GET_OBJ(func_obj);
   5070     p->u.cfunc.realm = JS_DupContext(ctx);
   5071     p->u.cfunc.c_function.generic = func;
   5072     p->u.cfunc.length = length;
   5073     p->u.cfunc.cproto = cproto;
   5074     p->u.cfunc.magic = magic;
   5075     p->is_constructor = (cproto == JS_CFUNC_constructor ||
   5076                          cproto == JS_CFUNC_constructor_magic ||
   5077                          cproto == JS_CFUNC_constructor_or_func ||
   5078                          cproto == JS_CFUNC_constructor_or_func_magic);
   5079     if (!name)
   5080         name = "";
   5081     name_atom = JS_NewAtom(ctx, name);
   5082     js_function_set_properties(ctx, func_obj, name_atom, length);
   5083     JS_FreeAtom(ctx, name_atom);
   5084     return func_obj;
   5085 }
   5086 
   5087 /* Note: at least 'length' arguments will be readable in 'argv' */
   5088 JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func,
   5089                          const char *name,
   5090                          int length, JSCFunctionEnum cproto, int magic)
   5091 {
   5092     return JS_NewCFunction3(ctx, func, name, length, cproto, magic,
   5093                             ctx->function_proto);
   5094 }
   5095 
   5096 typedef struct JSCFunctionDataRecord {
   5097     JSCFunctionData *func;
   5098     uint8_t length;
   5099     uint8_t data_len;
   5100     uint16_t magic;
   5101     JSValue data[0];
   5102 } JSCFunctionDataRecord;
   5103 
   5104 static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val)
   5105 {
   5106     JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA);
   5107     int i;
   5108 
   5109     if (s) {
   5110         for(i = 0; i < s->data_len; i++) {
   5111             JS_FreeValueRT(rt, s->data[i]);
   5112         }
   5113         js_free_rt(rt, s);
   5114     }
   5115 }
   5116 
   5117 static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val,
   5118                                     JS_MarkFunc *mark_func)
   5119 {
   5120     JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA);
   5121     int i;
   5122 
   5123     if (s) {
   5124         for(i = 0; i < s->data_len; i++) {
   5125             JS_MarkValue(rt, s->data[i], mark_func);
   5126         }
   5127     }
   5128 }
   5129 
   5130 static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj,
   5131                                        JSValueConst this_val,
   5132                                        int argc, JSValueConst *argv, int flags)
   5133 {
   5134     JSCFunctionDataRecord *s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA);
   5135     JSValueConst *arg_buf;
   5136     int i;
   5137 
   5138     /* XXX: could add the function on the stack for debug */
   5139     if (unlikely(argc < s->length)) {
   5140         arg_buf = alloca(sizeof(arg_buf[0]) * s->length);
   5141         for(i = 0; i < argc; i++)
   5142             arg_buf[i] = argv[i];
   5143         for(i = argc; i < s->length; i++)
   5144             arg_buf[i] = JS_UNDEFINED;
   5145     } else {
   5146         arg_buf = argv;
   5147     }
   5148 
   5149     return s->func(ctx, this_val, argc, arg_buf, s->magic, s->data);
   5150 }
   5151 
   5152 JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func,
   5153                             int length, int magic, int data_len,
   5154                             JSValueConst *data)
   5155 {
   5156     JSCFunctionDataRecord *s;
   5157     JSValue func_obj;
   5158     int i;
   5159 
   5160     func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
   5161                                       JS_CLASS_C_FUNCTION_DATA);
   5162     if (JS_IsException(func_obj))
   5163         return func_obj;
   5164     s = js_malloc(ctx, sizeof(*s) + data_len * sizeof(JSValue));
   5165     if (!s) {
   5166         JS_FreeValue(ctx, func_obj);
   5167         return JS_EXCEPTION;
   5168     }
   5169     s->func = func;
   5170     s->length = length;
   5171     s->data_len = data_len;
   5172     s->magic = magic;
   5173     for(i = 0; i < data_len; i++)
   5174         s->data[i] = JS_DupValue(ctx, data[i]);
   5175     JS_SetOpaque(func_obj, s);
   5176     js_function_set_properties(ctx, func_obj,
   5177                                JS_ATOM_empty_string, length);
   5178     return func_obj;
   5179 }
   5180 
   5181 static JSContext *js_autoinit_get_realm(JSProperty *pr)
   5182 {
   5183     return (JSContext *)(pr->u.init.realm_and_id & ~3);
   5184 }
   5185 
   5186 static JSAutoInitIDEnum js_autoinit_get_id(JSProperty *pr)
   5187 {
   5188     return pr->u.init.realm_and_id & 3;
   5189 }
   5190 
   5191 static void js_autoinit_free(JSRuntime *rt, JSProperty *pr)
   5192 {
   5193     JS_FreeContext(js_autoinit_get_realm(pr));
   5194 }
   5195 
   5196 static void js_autoinit_mark(JSRuntime *rt, JSProperty *pr,
   5197                              JS_MarkFunc *mark_func)
   5198 {
   5199     mark_func(rt, &js_autoinit_get_realm(pr)->header);
   5200 }
   5201 
   5202 static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags)
   5203 {
   5204     if (unlikely(prop_flags & JS_PROP_TMASK)) {
   5205         if ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
   5206             if (pr->u.getset.getter)
   5207                 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
   5208             if (pr->u.getset.setter)
   5209                 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
   5210         } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
   5211             free_var_ref(rt, pr->u.var_ref);
   5212         } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
   5213             js_autoinit_free(rt, pr);
   5214         }
   5215     } else {
   5216         JS_FreeValueRT(rt, pr->u.value);
   5217     }
   5218 }
   5219 
   5220 static force_inline JSShapeProperty *find_own_property1(JSObject *p,
   5221                                                         JSAtom atom)
   5222 {
   5223     JSShape *sh;
   5224     JSShapeProperty *pr, *prop;
   5225     intptr_t h;
   5226     sh = p->shape;
   5227     h = (uintptr_t)atom & sh->prop_hash_mask;
   5228     h = prop_hash_end(sh)[-h - 1];
   5229     prop = get_shape_prop(sh);
   5230     while (h) {
   5231         pr = &prop[h - 1];
   5232         if (likely(pr->atom == atom)) {
   5233             return pr;
   5234         }
   5235         h = pr->hash_next;
   5236     }
   5237     return NULL;
   5238 }
   5239 
   5240 static force_inline JSShapeProperty *find_own_property(JSProperty **ppr,
   5241                                                        JSObject *p,
   5242                                                        JSAtom atom)
   5243 {
   5244     JSShape *sh;
   5245     JSShapeProperty *pr, *prop;
   5246     intptr_t h;
   5247     sh = p->shape;
   5248     h = (uintptr_t)atom & sh->prop_hash_mask;
   5249     h = prop_hash_end(sh)[-h - 1];
   5250     prop = get_shape_prop(sh);
   5251     while (h) {
   5252         pr = &prop[h - 1];
   5253         if (likely(pr->atom == atom)) {
   5254             *ppr = &p->prop[h - 1];
   5255             /* the compiler should be able to assume that pr != NULL here */
   5256             return pr;
   5257         }
   5258         h = pr->hash_next;
   5259     }
   5260     *ppr = NULL;
   5261     return NULL;
   5262 }
   5263 
   5264 /* indicate that the object may be part of a function prototype cycle */
   5265 static void set_cycle_flag(JSContext *ctx, JSValueConst obj)
   5266 {
   5267 }
   5268 
   5269 static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref)
   5270 {
   5271     if (var_ref) {
   5272         assert(var_ref->header.ref_count > 0);
   5273         if (--var_ref->header.ref_count == 0) {
   5274             if (var_ref->is_detached) {
   5275                 JS_FreeValueRT(rt, var_ref->value);
   5276             } else {
   5277                 list_del(&var_ref->var_ref_link); /* still on the stack */
   5278                 if (var_ref->async_func)
   5279                     async_func_free(rt, var_ref->async_func);
   5280             }
   5281             remove_gc_object(&var_ref->header);
   5282             js_free_rt(rt, var_ref);
   5283         }
   5284     }
   5285 }
   5286 
   5287 static void js_array_finalizer(JSRuntime *rt, JSValue val)
   5288 {
   5289     JSObject *p = JS_VALUE_GET_OBJ(val);
   5290     int i;
   5291 
   5292     for(i = 0; i < p->u.array.count; i++) {
   5293         JS_FreeValueRT(rt, p->u.array.u.values[i]);
   5294     }
   5295     js_free_rt(rt, p->u.array.u.values);
   5296 }
   5297 
   5298 static void js_array_mark(JSRuntime *rt, JSValueConst val,
   5299                           JS_MarkFunc *mark_func)
   5300 {
   5301     JSObject *p = JS_VALUE_GET_OBJ(val);
   5302     int i;
   5303 
   5304     for(i = 0; i < p->u.array.count; i++) {
   5305         JS_MarkValue(rt, p->u.array.u.values[i], mark_func);
   5306     }
   5307 }
   5308 
   5309 static void js_object_data_finalizer(JSRuntime *rt, JSValue val)
   5310 {
   5311     JSObject *p = JS_VALUE_GET_OBJ(val);
   5312     JS_FreeValueRT(rt, p->u.object_data);
   5313     p->u.object_data = JS_UNDEFINED;
   5314 }
   5315 
   5316 static void js_object_data_mark(JSRuntime *rt, JSValueConst val,
   5317                                 JS_MarkFunc *mark_func)
   5318 {
   5319     JSObject *p = JS_VALUE_GET_OBJ(val);
   5320     JS_MarkValue(rt, p->u.object_data, mark_func);
   5321 }
   5322 
   5323 static void js_c_function_finalizer(JSRuntime *rt, JSValue val)
   5324 {
   5325     JSObject *p = JS_VALUE_GET_OBJ(val);
   5326 
   5327     if (p->u.cfunc.realm)
   5328         JS_FreeContext(p->u.cfunc.realm);
   5329 }
   5330 
   5331 static void js_c_function_mark(JSRuntime *rt, JSValueConst val,
   5332                                JS_MarkFunc *mark_func)
   5333 {
   5334     JSObject *p = JS_VALUE_GET_OBJ(val);
   5335 
   5336     if (p->u.cfunc.realm)
   5337         mark_func(rt, &p->u.cfunc.realm->header);
   5338 }
   5339 
   5340 static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val)
   5341 {
   5342     JSObject *p1, *p = JS_VALUE_GET_OBJ(val);
   5343     JSFunctionBytecode *b;
   5344     JSVarRef **var_refs;
   5345     int i;
   5346 
   5347     p1 = p->u.func.home_object;
   5348     if (p1) {
   5349         JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, p1));
   5350     }
   5351     b = p->u.func.function_bytecode;
   5352     if (b) {
   5353         var_refs = p->u.func.var_refs;
   5354         if (var_refs) {
   5355             for(i = 0; i < b->closure_var_count; i++)
   5356                 free_var_ref(rt, var_refs[i]);
   5357             js_free_rt(rt, var_refs);
   5358         }
   5359         JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b));
   5360     }
   5361 }
   5362 
   5363 static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val,
   5364                                       JS_MarkFunc *mark_func)
   5365 {
   5366     JSObject *p = JS_VALUE_GET_OBJ(val);
   5367     JSVarRef **var_refs = p->u.func.var_refs;
   5368     JSFunctionBytecode *b = p->u.func.function_bytecode;
   5369     int i;
   5370 
   5371     if (p->u.func.home_object) {
   5372         JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object),
   5373                      mark_func);
   5374     }
   5375     if (b) {
   5376         if (var_refs) {
   5377             for(i = 0; i < b->closure_var_count; i++) {
   5378                 JSVarRef *var_ref = var_refs[i];
   5379                 if (var_ref) {
   5380                     mark_func(rt, &var_ref->header);
   5381                 }
   5382             }
   5383         }
   5384         /* must mark the function bytecode because template objects may be
   5385            part of a cycle */
   5386         JS_MarkValue(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b), mark_func);
   5387     }
   5388 }
   5389 
   5390 static void js_bound_function_finalizer(JSRuntime *rt, JSValue val)
   5391 {
   5392     JSObject *p = JS_VALUE_GET_OBJ(val);
   5393     JSBoundFunction *bf = p->u.bound_function;
   5394     int i;
   5395 
   5396     JS_FreeValueRT(rt, bf->func_obj);
   5397     JS_FreeValueRT(rt, bf->this_val);
   5398     for(i = 0; i < bf->argc; i++) {
   5399         JS_FreeValueRT(rt, bf->argv[i]);
   5400     }
   5401     js_free_rt(rt, bf);
   5402 }
   5403 
   5404 static void js_bound_function_mark(JSRuntime *rt, JSValueConst val,
   5405                                 JS_MarkFunc *mark_func)
   5406 {
   5407     JSObject *p = JS_VALUE_GET_OBJ(val);
   5408     JSBoundFunction *bf = p->u.bound_function;
   5409     int i;
   5410 
   5411     JS_MarkValue(rt, bf->func_obj, mark_func);
   5412     JS_MarkValue(rt, bf->this_val, mark_func);
   5413     for(i = 0; i < bf->argc; i++)
   5414         JS_MarkValue(rt, bf->argv[i], mark_func);
   5415 }
   5416 
   5417 static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val)
   5418 {
   5419     JSObject *p = JS_VALUE_GET_OBJ(val);
   5420     JSForInIterator *it = p->u.for_in_iterator;
   5421     int i;
   5422 
   5423     JS_FreeValueRT(rt, it->obj);
   5424     if (!it->is_array) {
   5425         for(i = 0; i < it->atom_count; i++) {
   5426             JS_FreeAtomRT(rt, it->tab_atom[i].atom);
   5427         }
   5428         js_free_rt(rt, it->tab_atom);
   5429     }
   5430     js_free_rt(rt, it);
   5431 }
   5432 
   5433 static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val,
   5434                                 JS_MarkFunc *mark_func)
   5435 {
   5436     JSObject *p = JS_VALUE_GET_OBJ(val);
   5437     JSForInIterator *it = p->u.for_in_iterator;
   5438     JS_MarkValue(rt, it->obj, mark_func);
   5439 }
   5440 
   5441 static void free_object(JSRuntime *rt, JSObject *p)
   5442 {
   5443     int i;
   5444     JSClassFinalizer *finalizer;
   5445     JSShape *sh;
   5446     JSShapeProperty *pr;
   5447 
   5448     p->free_mark = 1; /* used to tell the object is invalid when
   5449                          freeing cycles */
   5450     /* free all the fields */
   5451     sh = p->shape;
   5452     pr = get_shape_prop(sh);
   5453     for(i = 0; i < sh->prop_count; i++) {
   5454         free_property(rt, &p->prop[i], pr->flags);
   5455         pr++;
   5456     }
   5457     js_free_rt(rt, p->prop);
   5458     /* as an optimization we destroy the shape immediately without
   5459        putting it in gc_zero_ref_count_list */
   5460     js_free_shape(rt, sh);
   5461 
   5462     /* fail safe */
   5463     p->shape = NULL;
   5464     p->prop = NULL;
   5465 
   5466     if (unlikely(p->first_weak_ref)) {
   5467         reset_weak_ref(rt, p);
   5468     }
   5469 
   5470     finalizer = rt->class_array[p->class_id].finalizer;
   5471     if (finalizer)
   5472         (*finalizer)(rt, JS_MKPTR(JS_TAG_OBJECT, p));
   5473 
   5474     /* fail safe */
   5475     p->class_id = 0;
   5476     p->u.opaque = NULL;
   5477     p->u.func.var_refs = NULL;
   5478     p->u.func.home_object = NULL;
   5479 
   5480     remove_gc_object(&p->header);
   5481     if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && p->header.ref_count != 0) {
   5482         list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list);
   5483     } else {
   5484         js_free_rt(rt, p);
   5485     }
   5486 }
   5487 
   5488 static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp)
   5489 {
   5490     switch(gp->gc_obj_type) {
   5491     case JS_GC_OBJ_TYPE_JS_OBJECT:
   5492         free_object(rt, (JSObject *)gp);
   5493         break;
   5494     case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
   5495         free_function_bytecode(rt, (JSFunctionBytecode *)gp);
   5496         break;
   5497     case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
   5498         __async_func_free(rt, (JSAsyncFunctionState *)gp);
   5499         break;
   5500     default:
   5501         abort();
   5502     }
   5503 }
   5504 
   5505 static void free_zero_refcount(JSRuntime *rt)
   5506 {
   5507     struct list_head *el;
   5508     JSGCObjectHeader *p;
   5509 
   5510     rt->gc_phase = JS_GC_PHASE_DECREF;
   5511     for(;;) {
   5512         el = rt->gc_zero_ref_count_list.next;
   5513         if (el == &rt->gc_zero_ref_count_list)
   5514             break;
   5515         p = list_entry(el, JSGCObjectHeader, link);
   5516         assert(p->ref_count == 0);
   5517         free_gc_object(rt, p);
   5518     }
   5519     rt->gc_phase = JS_GC_PHASE_NONE;
   5520 }
   5521 
   5522 /* called with the ref_count of 'v' reaches zero. */
   5523 void __JS_FreeValueRT(JSRuntime *rt, JSValue v)
   5524 {
   5525     uint32_t tag = JS_VALUE_GET_TAG(v);
   5526 
   5527 #ifdef DUMP_FREE
   5528     {
   5529         printf("Freeing ");
   5530         if (tag == JS_TAG_OBJECT) {
   5531             JS_DumpObject(rt, JS_VALUE_GET_OBJ(v));
   5532         } else {
   5533             JS_DumpValueShort(rt, v);
   5534             printf("\n");
   5535         }
   5536     }
   5537 #endif
   5538 
   5539     switch(tag) {
   5540     case JS_TAG_STRING:
   5541         {
   5542             JSString *p = JS_VALUE_GET_STRING(v);
   5543             if (p->atom_type) {
   5544                 JS_FreeAtomStruct(rt, p);
   5545             } else {
   5546 #ifdef DUMP_LEAKS
   5547                 list_del(&p->link);
   5548 #endif
   5549                 js_free_rt(rt, p);
   5550             }
   5551         }
   5552         break;
   5553     case JS_TAG_OBJECT:
   5554     case JS_TAG_FUNCTION_BYTECODE:
   5555         {
   5556             JSGCObjectHeader *p = JS_VALUE_GET_PTR(v);
   5557             if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) {
   5558                 list_del(&p->link);
   5559                 list_add(&p->link, &rt->gc_zero_ref_count_list);
   5560                 if (rt->gc_phase == JS_GC_PHASE_NONE) {
   5561                     free_zero_refcount(rt);
   5562                 }
   5563             }
   5564         }
   5565         break;
   5566     case JS_TAG_MODULE:
   5567         abort(); /* never freed here */
   5568         break;
   5569     case JS_TAG_BIG_INT:
   5570 #ifdef CONFIG_BIGNUM
   5571     case JS_TAG_BIG_FLOAT:
   5572 #endif
   5573         {
   5574             JSBigFloat *bf = JS_VALUE_GET_PTR(v);
   5575             bf_delete(&bf->num);
   5576             js_free_rt(rt, bf);
   5577         }
   5578         break;
   5579 #ifdef CONFIG_BIGNUM
   5580     case JS_TAG_BIG_DECIMAL:
   5581         {
   5582             JSBigDecimal *bf = JS_VALUE_GET_PTR(v);
   5583             bfdec_delete(&bf->num);
   5584             js_free_rt(rt, bf);
   5585         }
   5586         break;
   5587 #endif
   5588     case JS_TAG_SYMBOL:
   5589         {
   5590             JSAtomStruct *p = JS_VALUE_GET_PTR(v);
   5591             JS_FreeAtomStruct(rt, p);
   5592         }
   5593         break;
   5594     default:
   5595         printf("__JS_FreeValue: unknown tag=%d\n", tag);
   5596         abort();
   5597     }
   5598 }
   5599 
   5600 void __JS_FreeValue(JSContext *ctx, JSValue v)
   5601 {
   5602     __JS_FreeValueRT(ctx->rt, v);
   5603 }
   5604 
   5605 /* garbage collection */
   5606 
   5607 static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,
   5608                           JSGCObjectTypeEnum type)
   5609 {
   5610     h->mark = 0;
   5611     h->gc_obj_type = type;
   5612     list_add_tail(&h->link, &rt->gc_obj_list);
   5613 }
   5614 
   5615 static void remove_gc_object(JSGCObjectHeader *h)
   5616 {
   5617     list_del(&h->link);
   5618 }
   5619 
   5620 void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
   5621 {
   5622     if (JS_VALUE_HAS_REF_COUNT(val)) {
   5623         switch(JS_VALUE_GET_TAG(val)) {
   5624         case JS_TAG_OBJECT:
   5625         case JS_TAG_FUNCTION_BYTECODE:
   5626             mark_func(rt, JS_VALUE_GET_PTR(val));
   5627             break;
   5628         default:
   5629             break;
   5630         }
   5631     }
   5632 }
   5633 
   5634 static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp,
   5635                           JS_MarkFunc *mark_func)
   5636 {
   5637     switch(gp->gc_obj_type) {
   5638     case JS_GC_OBJ_TYPE_JS_OBJECT:
   5639         {
   5640             JSObject *p = (JSObject *)gp;
   5641             JSShapeProperty *prs;
   5642             JSShape *sh;
   5643             int i;
   5644             sh = p->shape;
   5645             mark_func(rt, &sh->header);
   5646             /* mark all the fields */
   5647             prs = get_shape_prop(sh);
   5648             for(i = 0; i < sh->prop_count; i++) {
   5649                 JSProperty *pr = &p->prop[i];
   5650                 if (prs->atom != JS_ATOM_NULL) {
   5651                     if (prs->flags & JS_PROP_TMASK) {
   5652                         if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
   5653                             if (pr->u.getset.getter)
   5654                                 mark_func(rt, &pr->u.getset.getter->header);
   5655                             if (pr->u.getset.setter)
   5656                                 mark_func(rt, &pr->u.getset.setter->header);
   5657                         } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
   5658                             /* Note: the tag does not matter
   5659                                provided it is a GC object */
   5660                             mark_func(rt, &pr->u.var_ref->header);
   5661                         } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
   5662                             js_autoinit_mark(rt, pr, mark_func);
   5663                         }
   5664                     } else {
   5665                         JS_MarkValue(rt, pr->u.value, mark_func);
   5666                     }
   5667                 }
   5668                 prs++;
   5669             }
   5670 
   5671             if (p->class_id != JS_CLASS_OBJECT) {
   5672                 JSClassGCMark *gc_mark;
   5673                 gc_mark = rt->class_array[p->class_id].gc_mark;
   5674                 if (gc_mark)
   5675                     gc_mark(rt, JS_MKPTR(JS_TAG_OBJECT, p), mark_func);
   5676             }
   5677         }
   5678         break;
   5679     case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
   5680         /* the template objects can be part of a cycle */
   5681         {
   5682             JSFunctionBytecode *b = (JSFunctionBytecode *)gp;
   5683             int i;
   5684             for(i = 0; i < b->cpool_count; i++) {
   5685                 JS_MarkValue(rt, b->cpool[i], mark_func);
   5686             }
   5687             if (b->realm)
   5688                 mark_func(rt, &b->realm->header);
   5689         }
   5690         break;
   5691     case JS_GC_OBJ_TYPE_VAR_REF:
   5692         {
   5693             JSVarRef *var_ref = (JSVarRef *)gp;
   5694             if (var_ref->is_detached) {
   5695                 JS_MarkValue(rt, *var_ref->pvalue, mark_func);
   5696             } else if (var_ref->async_func) {
   5697                 mark_func(rt, &var_ref->async_func->header);
   5698             }
   5699         }
   5700         break;
   5701     case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
   5702         {
   5703             JSAsyncFunctionState *s = (JSAsyncFunctionState *)gp;
   5704             JSStackFrame *sf = &s->frame;
   5705             JSValue *sp;
   5706 
   5707             if (!s->is_completed) {
   5708                 JS_MarkValue(rt, sf->cur_func, mark_func);
   5709                 JS_MarkValue(rt, s->this_val, mark_func);
   5710                 /* sf->cur_sp = NULL if the function is running */
   5711                 if (sf->cur_sp) {
   5712                     /* if the function is running, cur_sp is not known so we
   5713                        cannot mark the stack. Marking the variables is not needed
   5714                        because a running function cannot be part of a removable
   5715                        cycle */
   5716                     for(sp = sf->arg_buf; sp < sf->cur_sp; sp++)
   5717                         JS_MarkValue(rt, *sp, mark_func);
   5718                 }
   5719             }
   5720             JS_MarkValue(rt, s->resolving_funcs[0], mark_func);
   5721             JS_MarkValue(rt, s->resolving_funcs[1], mark_func);
   5722         }
   5723         break;
   5724     case JS_GC_OBJ_TYPE_SHAPE:
   5725         {
   5726             JSShape *sh = (JSShape *)gp;
   5727             if (sh->proto != NULL) {
   5728                 mark_func(rt, &sh->proto->header);
   5729             }
   5730         }
   5731         break;
   5732     case JS_GC_OBJ_TYPE_JS_CONTEXT:
   5733         {
   5734             JSContext *ctx = (JSContext *)gp;
   5735             JS_MarkContext(rt, ctx, mark_func);
   5736         }
   5737         break;
   5738     default:
   5739         abort();
   5740     }
   5741 }
   5742 
   5743 static void gc_decref_child(JSRuntime *rt, JSGCObjectHeader *p)
   5744 {
   5745     assert(p->ref_count > 0);
   5746     p->ref_count--;
   5747     if (p->ref_count == 0 && p->mark == 1) {
   5748         list_del(&p->link);
   5749         list_add_tail(&p->link, &rt->tmp_obj_list);
   5750     }
   5751 }
   5752 
   5753 static void gc_decref(JSRuntime *rt)
   5754 {
   5755     struct list_head *el, *el1;
   5756     JSGCObjectHeader *p;
   5757 
   5758     init_list_head(&rt->tmp_obj_list);
   5759 
   5760     /* decrement the refcount of all the children of all the GC
   5761        objects and move the GC objects with zero refcount to
   5762        tmp_obj_list */
   5763     list_for_each_safe(el, el1, &rt->gc_obj_list) {
   5764         p = list_entry(el, JSGCObjectHeader, link);
   5765         assert(p->mark == 0);
   5766         mark_children(rt, p, gc_decref_child);
   5767         p->mark = 1;
   5768         if (p->ref_count == 0) {
   5769             list_del(&p->link);
   5770             list_add_tail(&p->link, &rt->tmp_obj_list);
   5771         }
   5772     }
   5773 }
   5774 
   5775 static void gc_scan_incref_child(JSRuntime *rt, JSGCObjectHeader *p)
   5776 {
   5777     p->ref_count++;
   5778     if (p->ref_count == 1) {
   5779         /* ref_count was 0: remove from tmp_obj_list and add at the
   5780            end of gc_obj_list */
   5781         list_del(&p->link);
   5782         list_add_tail(&p->link, &rt->gc_obj_list);
   5783         p->mark = 0; /* reset the mark for the next GC call */
   5784     }
   5785 }
   5786 
   5787 static void gc_scan_incref_child2(JSRuntime *rt, JSGCObjectHeader *p)
   5788 {
   5789     p->ref_count++;
   5790 }
   5791 
   5792 static void gc_scan(JSRuntime *rt)
   5793 {
   5794     struct list_head *el;
   5795     JSGCObjectHeader *p;
   5796 
   5797     /* keep the objects with a refcount > 0 and their children. */
   5798     list_for_each(el, &rt->gc_obj_list) {
   5799         p = list_entry(el, JSGCObjectHeader, link);
   5800         assert(p->ref_count > 0);
   5801         p->mark = 0; /* reset the mark for the next GC call */
   5802         mark_children(rt, p, gc_scan_incref_child);
   5803     }
   5804 
   5805     /* restore the refcount of the objects to be deleted. */
   5806     list_for_each(el, &rt->tmp_obj_list) {
   5807         p = list_entry(el, JSGCObjectHeader, link);
   5808         mark_children(rt, p, gc_scan_incref_child2);
   5809     }
   5810 }
   5811 
   5812 static void gc_free_cycles(JSRuntime *rt)
   5813 {
   5814     struct list_head *el, *el1;
   5815     JSGCObjectHeader *p;
   5816 #ifdef DUMP_GC_FREE
   5817     BOOL header_done = FALSE;
   5818 #endif
   5819 
   5820     rt->gc_phase = JS_GC_PHASE_REMOVE_CYCLES;
   5821 
   5822     for(;;) {
   5823         el = rt->tmp_obj_list.next;
   5824         if (el == &rt->tmp_obj_list)
   5825             break;
   5826         p = list_entry(el, JSGCObjectHeader, link);
   5827         /* Only need to free the GC object associated with JS values
   5828            or async functions. The rest will be automatically removed
   5829            because they must be referenced by them. */
   5830         switch(p->gc_obj_type) {
   5831         case JS_GC_OBJ_TYPE_JS_OBJECT:
   5832         case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
   5833         case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
   5834 #ifdef DUMP_GC_FREE
   5835             if (!header_done) {
   5836                 printf("Freeing cycles:\n");
   5837                 JS_DumpObjectHeader(rt);
   5838                 header_done = TRUE;
   5839             }
   5840             JS_DumpGCObject(rt, p);
   5841 #endif
   5842             free_gc_object(rt, p);
   5843             break;
   5844         default:
   5845             list_del(&p->link);
   5846             list_add_tail(&p->link, &rt->gc_zero_ref_count_list);
   5847             break;
   5848         }
   5849     }
   5850     rt->gc_phase = JS_GC_PHASE_NONE;
   5851 
   5852     list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) {
   5853         p = list_entry(el, JSGCObjectHeader, link);
   5854         assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT ||
   5855                p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE ||
   5856                p->gc_obj_type == JS_GC_OBJ_TYPE_ASYNC_FUNCTION);
   5857         js_free_rt(rt, p);
   5858     }
   5859 
   5860     init_list_head(&rt->gc_zero_ref_count_list);
   5861 }
   5862 
   5863 void JS_RunGC(JSRuntime *rt)
   5864 {
   5865     /* decrement the reference of the children of each object. mark =
   5866        1 after this pass. */
   5867     gc_decref(rt);
   5868 
   5869     /* keep the GC objects with a non zero refcount and their childs */
   5870     gc_scan(rt);
   5871 
   5872     /* free the GC objects in a cycle */
   5873     gc_free_cycles(rt);
   5874 }
   5875 
   5876 /* Return false if not an object or if the object has already been
   5877    freed (zombie objects are visible in finalizers when freeing
   5878    cycles). */
   5879 BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj)
   5880 {
   5881     JSObject *p;
   5882     if (!JS_IsObject(obj))
   5883         return FALSE;
   5884     p = JS_VALUE_GET_OBJ(obj);
   5885     return !p->free_mark;
   5886 }
   5887 
   5888 /* Compute memory used by various object types */
   5889 /* XXX: poor man's approach to handling multiply referenced objects */
   5890 typedef struct JSMemoryUsage_helper {
   5891     double memory_used_count;
   5892     double str_count;
   5893     double str_size;
   5894     int64_t js_func_count;
   5895     double js_func_size;
   5896     int64_t js_func_code_size;
   5897     int64_t js_func_pc2line_count;
   5898     int64_t js_func_pc2line_size;
   5899 } JSMemoryUsage_helper;
   5900 
   5901 static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp);
   5902 
   5903 static void compute_jsstring_size(JSString *str, JSMemoryUsage_helper *hp)
   5904 {
   5905     if (!str->atom_type) {  /* atoms are handled separately */
   5906         double s_ref_count = str->header.ref_count;
   5907         hp->str_count += 1 / s_ref_count;
   5908         hp->str_size += ((sizeof(*str) + (str->len << str->is_wide_char) +
   5909                           1 - str->is_wide_char) / s_ref_count);
   5910     }
   5911 }
   5912 
   5913 static void compute_bytecode_size(JSFunctionBytecode *b, JSMemoryUsage_helper *hp)
   5914 {
   5915     int memory_used_count, js_func_size, i;
   5916 
   5917     memory_used_count = 0;
   5918     js_func_size = offsetof(JSFunctionBytecode, debug);
   5919     if (b->vardefs) {
   5920         js_func_size += (b->arg_count + b->var_count) * sizeof(*b->vardefs);
   5921     }
   5922     if (b->cpool) {
   5923         js_func_size += b->cpool_count * sizeof(*b->cpool);
   5924         for (i = 0; i < b->cpool_count; i++) {
   5925             JSValueConst val = b->cpool[i];
   5926             compute_value_size(val, hp);
   5927         }
   5928     }
   5929     if (b->closure_var) {
   5930         js_func_size += b->closure_var_count * sizeof(*b->closure_var);
   5931     }
   5932     if (!b->read_only_bytecode && b->byte_code_buf) {
   5933         hp->js_func_code_size += b->byte_code_len;
   5934     }
   5935     if (b->has_debug) {
   5936         js_func_size += sizeof(*b) - offsetof(JSFunctionBytecode, debug);
   5937         if (b->debug.source) {
   5938             memory_used_count++;
   5939             js_func_size += b->debug.source_len + 1;
   5940         }
   5941         if (b->debug.pc2line_len) {
   5942             memory_used_count++;
   5943             hp->js_func_pc2line_count += 1;
   5944             hp->js_func_pc2line_size += b->debug.pc2line_len;
   5945         }
   5946     }
   5947     hp->js_func_size += js_func_size;
   5948     hp->js_func_count += 1;
   5949     hp->memory_used_count += memory_used_count;
   5950 }
   5951 
   5952 static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp)
   5953 {
   5954     switch(JS_VALUE_GET_TAG(val)) {
   5955     case JS_TAG_STRING:
   5956         compute_jsstring_size(JS_VALUE_GET_STRING(val), hp);
   5957         break;
   5958     case JS_TAG_BIG_INT:
   5959 #ifdef CONFIG_BIGNUM
   5960     case JS_TAG_BIG_FLOAT:
   5961     case JS_TAG_BIG_DECIMAL:
   5962 #endif
   5963         /* should track JSBigFloat usage */
   5964         break;
   5965     }
   5966 }
   5967 
   5968 void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s)
   5969 {
   5970     struct list_head *el, *el1;
   5971     int i;
   5972     JSMemoryUsage_helper mem = { 0 }, *hp = &mem;
   5973 
   5974     memset(s, 0, sizeof(*s));
   5975     s->malloc_count = rt->malloc_state.malloc_count;
   5976     s->malloc_size = rt->malloc_state.malloc_size;
   5977     s->malloc_limit = rt->malloc_state.malloc_limit;
   5978 
   5979     s->memory_used_count = 2; /* rt + rt->class_array */
   5980     s->memory_used_size = sizeof(JSRuntime) + sizeof(JSValue) * rt->class_count;
   5981 
   5982     list_for_each(el, &rt->context_list) {
   5983         JSContext *ctx = list_entry(el, JSContext, link);
   5984         JSShape *sh = ctx->array_shape;
   5985         s->memory_used_count += 2; /* ctx + ctx->class_proto */
   5986         s->memory_used_size += sizeof(JSContext) +
   5987             sizeof(JSValue) * rt->class_count;
   5988         s->binary_object_count += ctx->binary_object_count;
   5989         s->binary_object_size += ctx->binary_object_size;
   5990 
   5991         /* the hashed shapes are counted separately */
   5992         if (sh && !sh->is_hashed) {
   5993             int hash_size = sh->prop_hash_mask + 1;
   5994             s->shape_count++;
   5995             s->shape_size += get_shape_size(hash_size, sh->prop_size);
   5996         }
   5997         list_for_each(el1, &ctx->loaded_modules) {
   5998             JSModuleDef *m = list_entry(el1, JSModuleDef, link);
   5999             s->memory_used_count += 1;
   6000             s->memory_used_size += sizeof(*m);
   6001             if (m->req_module_entries) {
   6002                 s->memory_used_count += 1;
   6003                 s->memory_used_size += m->req_module_entries_count * sizeof(*m->req_module_entries);
   6004             }
   6005             if (m->export_entries) {
   6006                 s->memory_used_count += 1;
   6007                 s->memory_used_size += m->export_entries_count * sizeof(*m->export_entries);
   6008                 for (i = 0; i < m->export_entries_count; i++) {
   6009                     JSExportEntry *me = &m->export_entries[i];
   6010                     if (me->export_type == JS_EXPORT_TYPE_LOCAL && me->u.local.var_ref) {
   6011                         /* potential multiple count */
   6012                         s->memory_used_count += 1;
   6013                         compute_value_size(me->u.local.var_ref->value, hp);
   6014                     }
   6015                 }
   6016             }
   6017             if (m->star_export_entries) {
   6018                 s->memory_used_count += 1;
   6019                 s->memory_used_size += m->star_export_entries_count * sizeof(*m->star_export_entries);
   6020             }
   6021             if (m->import_entries) {
   6022                 s->memory_used_count += 1;
   6023                 s->memory_used_size += m->import_entries_count * sizeof(*m->import_entries);
   6024             }
   6025             compute_value_size(m->module_ns, hp);
   6026             compute_value_size(m->func_obj, hp);
   6027         }
   6028     }
   6029 
   6030     list_for_each(el, &rt->gc_obj_list) {
   6031         JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link);
   6032         JSObject *p;
   6033         JSShape *sh;
   6034         JSShapeProperty *prs;
   6035 
   6036         /* XXX: could count the other GC object types too */
   6037         if (gp->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) {
   6038             compute_bytecode_size((JSFunctionBytecode *)gp, hp);
   6039             continue;
   6040         } else if (gp->gc_obj_type != JS_GC_OBJ_TYPE_JS_OBJECT) {
   6041             continue;
   6042         }
   6043         p = (JSObject *)gp;
   6044         sh = p->shape;
   6045         s->obj_count++;
   6046         if (p->prop) {
   6047             s->memory_used_count++;
   6048             s->prop_size += sh->prop_size * sizeof(*p->prop);
   6049             s->prop_count += sh->prop_count;
   6050             prs = get_shape_prop(sh);
   6051             for(i = 0; i < sh->prop_count; i++) {
   6052                 JSProperty *pr = &p->prop[i];
   6053                 if (prs->atom != JS_ATOM_NULL && !(prs->flags & JS_PROP_TMASK)) {
   6054                     compute_value_size(pr->u.value, hp);
   6055                 }
   6056                 prs++;
   6057             }
   6058         }
   6059         /* the hashed shapes are counted separately */
   6060         if (!sh->is_hashed) {
   6061             int hash_size = sh->prop_hash_mask + 1;
   6062             s->shape_count++;
   6063             s->shape_size += get_shape_size(hash_size, sh->prop_size);
   6064         }
   6065 
   6066         switch(p->class_id) {
   6067         case JS_CLASS_ARRAY:             /* u.array | length */
   6068         case JS_CLASS_ARGUMENTS:         /* u.array | length */
   6069             s->array_count++;
   6070             if (p->fast_array) {
   6071                 s->fast_array_count++;
   6072                 if (p->u.array.u.values) {
   6073                     s->memory_used_count++;
   6074                     s->memory_used_size += p->u.array.count *
   6075                         sizeof(*p->u.array.u.values);
   6076                     s->fast_array_elements += p->u.array.count;
   6077                     for (i = 0; i < p->u.array.count; i++) {
   6078                         compute_value_size(p->u.array.u.values[i], hp);
   6079                     }
   6080                 }
   6081             }
   6082             break;
   6083         case JS_CLASS_NUMBER:            /* u.object_data */
   6084         case JS_CLASS_STRING:            /* u.object_data */
   6085         case JS_CLASS_BOOLEAN:           /* u.object_data */
   6086         case JS_CLASS_SYMBOL:            /* u.object_data */
   6087         case JS_CLASS_DATE:              /* u.object_data */
   6088         case JS_CLASS_BIG_INT:           /* u.object_data */
   6089 #ifdef CONFIG_BIGNUM
   6090         case JS_CLASS_BIG_FLOAT:         /* u.object_data */
   6091         case JS_CLASS_BIG_DECIMAL:         /* u.object_data */
   6092 #endif
   6093             compute_value_size(p->u.object_data, hp);
   6094             break;
   6095         case JS_CLASS_C_FUNCTION:        /* u.cfunc */
   6096             s->c_func_count++;
   6097             break;
   6098         case JS_CLASS_BYTECODE_FUNCTION: /* u.func */
   6099             {
   6100                 JSFunctionBytecode *b = p->u.func.function_bytecode;
   6101                 JSVarRef **var_refs = p->u.func.var_refs;
   6102                 /* home_object: object will be accounted for in list scan */
   6103                 if (var_refs) {
   6104                     s->memory_used_count++;
   6105                     s->js_func_size += b->closure_var_count * sizeof(*var_refs);
   6106                     for (i = 0; i < b->closure_var_count; i++) {
   6107                         if (var_refs[i]) {
   6108                             double ref_count = var_refs[i]->header.ref_count;
   6109                             s->memory_used_count += 1 / ref_count;
   6110                             s->js_func_size += sizeof(*var_refs[i]) / ref_count;
   6111                             /* handle non object closed values */
   6112                             if (var_refs[i]->pvalue == &var_refs[i]->value) {
   6113                                 /* potential multiple count */
   6114                                 compute_value_size(var_refs[i]->value, hp);
   6115                             }
   6116                         }
   6117                     }
   6118                 }
   6119             }
   6120             break;
   6121         case JS_CLASS_BOUND_FUNCTION:    /* u.bound_function */
   6122             {
   6123                 JSBoundFunction *bf = p->u.bound_function;
   6124                 /* func_obj and this_val are objects */
   6125                 for (i = 0; i < bf->argc; i++) {
   6126                     compute_value_size(bf->argv[i], hp);
   6127                 }
   6128                 s->memory_used_count += 1;
   6129                 s->memory_used_size += sizeof(*bf) + bf->argc * sizeof(*bf->argv);
   6130             }
   6131             break;
   6132         case JS_CLASS_C_FUNCTION_DATA:   /* u.c_function_data_record */
   6133             {
   6134                 JSCFunctionDataRecord *fd = p->u.c_function_data_record;
   6135                 if (fd) {
   6136                     for (i = 0; i < fd->data_len; i++) {
   6137                         compute_value_size(fd->data[i], hp);
   6138                     }
   6139                     s->memory_used_count += 1;
   6140                     s->memory_used_size += sizeof(*fd) + fd->data_len * sizeof(*fd->data);
   6141                 }
   6142             }
   6143             break;
   6144         case JS_CLASS_REGEXP:            /* u.regexp */
   6145             compute_jsstring_size(p->u.regexp.pattern, hp);
   6146             compute_jsstring_size(p->u.regexp.bytecode, hp);
   6147             break;
   6148 
   6149         case JS_CLASS_FOR_IN_ITERATOR:   /* u.for_in_iterator */
   6150             {
   6151                 JSForInIterator *it = p->u.for_in_iterator;
   6152                 if (it) {
   6153                     compute_value_size(it->obj, hp);
   6154                     s->memory_used_count += 1;
   6155                     s->memory_used_size += sizeof(*it);
   6156                 }
   6157             }
   6158             break;
   6159         case JS_CLASS_ARRAY_BUFFER:      /* u.array_buffer */
   6160         case JS_CLASS_SHARED_ARRAY_BUFFER: /* u.array_buffer */
   6161             {
   6162                 JSArrayBuffer *abuf = p->u.array_buffer;
   6163                 if (abuf) {
   6164                     s->memory_used_count += 1;
   6165                     s->memory_used_size += sizeof(*abuf);
   6166                     if (abuf->data) {
   6167                         s->memory_used_count += 1;
   6168                         s->memory_used_size += abuf->byte_length;
   6169                     }
   6170                 }
   6171             }
   6172             break;
   6173         case JS_CLASS_GENERATOR:         /* u.generator_data */
   6174         case JS_CLASS_UINT8C_ARRAY:      /* u.typed_array / u.array */
   6175         case JS_CLASS_INT8_ARRAY:        /* u.typed_array / u.array */
   6176         case JS_CLASS_UINT8_ARRAY:       /* u.typed_array / u.array */
   6177         case JS_CLASS_INT16_ARRAY:       /* u.typed_array / u.array */
   6178         case JS_CLASS_UINT16_ARRAY:      /* u.typed_array / u.array */
   6179         case JS_CLASS_INT32_ARRAY:       /* u.typed_array / u.array */
   6180         case JS_CLASS_UINT32_ARRAY:      /* u.typed_array / u.array */
   6181         case JS_CLASS_BIG_INT64_ARRAY:   /* u.typed_array / u.array */
   6182         case JS_CLASS_BIG_UINT64_ARRAY:  /* u.typed_array / u.array */
   6183         case JS_CLASS_FLOAT32_ARRAY:     /* u.typed_array / u.array */
   6184         case JS_CLASS_FLOAT64_ARRAY:     /* u.typed_array / u.array */
   6185         case JS_CLASS_DATAVIEW:          /* u.typed_array */
   6186 #ifdef CONFIG_BIGNUM
   6187         case JS_CLASS_FLOAT_ENV:         /* u.float_env */
   6188 #endif
   6189         case JS_CLASS_MAP:               /* u.map_state */
   6190         case JS_CLASS_SET:               /* u.map_state */
   6191         case JS_CLASS_WEAKMAP:           /* u.map_state */
   6192         case JS_CLASS_WEAKSET:           /* u.map_state */
   6193         case JS_CLASS_MAP_ITERATOR:      /* u.map_iterator_data */
   6194         case JS_CLASS_SET_ITERATOR:      /* u.map_iterator_data */
   6195         case JS_CLASS_ARRAY_ITERATOR:    /* u.array_iterator_data */
   6196         case JS_CLASS_STRING_ITERATOR:   /* u.array_iterator_data */
   6197         case JS_CLASS_PROXY:             /* u.proxy_data */
   6198         case JS_CLASS_PROMISE:           /* u.promise_data */
   6199         case JS_CLASS_PROMISE_RESOLVE_FUNCTION:  /* u.promise_function_data */
   6200         case JS_CLASS_PROMISE_REJECT_FUNCTION:   /* u.promise_function_data */
   6201         case JS_CLASS_ASYNC_FUNCTION_RESOLVE:    /* u.async_function_data */
   6202         case JS_CLASS_ASYNC_FUNCTION_REJECT:     /* u.async_function_data */
   6203         case JS_CLASS_ASYNC_FROM_SYNC_ITERATOR:  /* u.async_from_sync_iterator_data */
   6204         case JS_CLASS_ASYNC_GENERATOR:   /* u.async_generator_data */
   6205             /* TODO */
   6206         default:
   6207             /* XXX: class definition should have an opaque block size */
   6208             if (p->u.opaque) {
   6209                 s->memory_used_count += 1;
   6210             }
   6211             break;
   6212         }
   6213     }
   6214     s->obj_size += s->obj_count * sizeof(JSObject);
   6215 
   6216     /* hashed shapes */
   6217     s->memory_used_count++; /* rt->shape_hash */
   6218     s->memory_used_size += sizeof(rt->shape_hash[0]) * rt->shape_hash_size;
   6219     for(i = 0; i < rt->shape_hash_size; i++) {
   6220         JSShape *sh;
   6221         for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) {
   6222             int hash_size = sh->prop_hash_mask + 1;
   6223             s->shape_count++;
   6224             s->shape_size += get_shape_size(hash_size, sh->prop_size);
   6225         }
   6226     }
   6227 
   6228     /* atoms */
   6229     s->memory_used_count += 2; /* rt->atom_array, rt->atom_hash */
   6230     s->atom_count = rt->atom_count;
   6231     s->atom_size = sizeof(rt->atom_array[0]) * rt->atom_size +
   6232         sizeof(rt->atom_hash[0]) * rt->atom_hash_size;
   6233     for(i = 0; i < rt->atom_size; i++) {
   6234         JSAtomStruct *p = rt->atom_array[i];
   6235         if (!atom_is_free(p)) {
   6236             s->atom_size += (sizeof(*p) + (p->len << p->is_wide_char) +
   6237                              1 - p->is_wide_char);
   6238         }
   6239     }
   6240     s->str_count = round(mem.str_count);
   6241     s->str_size = round(mem.str_size);
   6242     s->js_func_count = mem.js_func_count;
   6243     s->js_func_size = round(mem.js_func_size);
   6244     s->js_func_code_size = mem.js_func_code_size;
   6245     s->js_func_pc2line_count = mem.js_func_pc2line_count;
   6246     s->js_func_pc2line_size = mem.js_func_pc2line_size;
   6247     s->memory_used_count += round(mem.memory_used_count) +
   6248         s->atom_count + s->str_count +
   6249         s->obj_count + s->shape_count +
   6250         s->js_func_count + s->js_func_pc2line_count;
   6251     s->memory_used_size += s->atom_size + s->str_size +
   6252         s->obj_size + s->prop_size + s->shape_size +
   6253         s->js_func_size + s->js_func_code_size + s->js_func_pc2line_size;
   6254 }
   6255 
   6256 void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt)
   6257 {
   6258     fprintf(fp, "QuickJS memory usage -- "
   6259 #ifdef CONFIG_BIGNUM
   6260             "BigNum "
   6261 #endif
   6262             CONFIG_VERSION " version, %d-bit, malloc limit: %"PRId64"\n\n",
   6263             (int)sizeof(void *) * 8, s->malloc_limit);
   6264 #if 1
   6265     if (rt) {
   6266         static const struct {
   6267             const char *name;
   6268             size_t size;
   6269         } object_types[] = {
   6270             { "JSRuntime", sizeof(JSRuntime) },
   6271             { "JSContext", sizeof(JSContext) },
   6272             { "JSObject", sizeof(JSObject) },
   6273             { "JSString", sizeof(JSString) },
   6274             { "JSFunctionBytecode", sizeof(JSFunctionBytecode) },
   6275         };
   6276         int i, usage_size_ok = 0;
   6277         for(i = 0; i < countof(object_types); i++) {
   6278             unsigned int size = object_types[i].size;
   6279             void *p = js_malloc_rt(rt, size);
   6280             if (p) {
   6281                 unsigned int size1 = js_malloc_usable_size_rt(rt, p);
   6282                 if (size1 >= size) {
   6283                     usage_size_ok = 1;
   6284                     fprintf(fp, "  %3u + %-2u  %s\n",
   6285                             size, size1 - size, object_types[i].name);
   6286                 }
   6287                 js_free_rt(rt, p);
   6288             }
   6289         }
   6290         if (!usage_size_ok) {
   6291             fprintf(fp, "  malloc_usable_size unavailable\n");
   6292         }
   6293         {
   6294             int obj_classes[JS_CLASS_INIT_COUNT + 1] = { 0 };
   6295             int class_id;
   6296             struct list_head *el;
   6297             list_for_each(el, &rt->gc_obj_list) {
   6298                 JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link);
   6299                 JSObject *p;
   6300                 if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
   6301                     p = (JSObject *)gp;
   6302                     obj_classes[min_uint32(p->class_id, JS_CLASS_INIT_COUNT)]++;
   6303                 }
   6304             }
   6305             fprintf(fp, "\n" "JSObject classes\n");
   6306             if (obj_classes[0])
   6307                 fprintf(fp, "  %5d  %2.0d %s\n", obj_classes[0], 0, "none");
   6308             for (class_id = 1; class_id < JS_CLASS_INIT_COUNT; class_id++) {
   6309                 if (obj_classes[class_id] && class_id < rt->class_count) {
   6310                     char buf[ATOM_GET_STR_BUF_SIZE];
   6311                     fprintf(fp, "  %5d  %2.0d %s\n", obj_classes[class_id], class_id,
   6312                             JS_AtomGetStrRT(rt, buf, sizeof(buf), rt->class_array[class_id].class_name));
   6313                 }
   6314             }
   6315             if (obj_classes[JS_CLASS_INIT_COUNT])
   6316                 fprintf(fp, "  %5d  %2.0d %s\n", obj_classes[JS_CLASS_INIT_COUNT], 0, "other");
   6317         }
   6318         fprintf(fp, "\n");
   6319     }
   6320 #endif
   6321     fprintf(fp, "%-20s %8s %8s\n", "NAME", "COUNT", "SIZE");
   6322 
   6323     if (s->malloc_count) {
   6324         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per block)\n",
   6325                 "memory allocated", s->malloc_count, s->malloc_size,
   6326                 (double)s->malloc_size / s->malloc_count);
   6327         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%d overhead, %0.1f average slack)\n",
   6328                 "memory used", s->memory_used_count, s->memory_used_size,
   6329                 MALLOC_OVERHEAD, ((double)(s->malloc_size - s->memory_used_size) /
   6330                                   s->memory_used_count));
   6331     }
   6332     if (s->atom_count) {
   6333         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per atom)\n",
   6334                 "atoms", s->atom_count, s->atom_size,
   6335                 (double)s->atom_size / s->atom_count);
   6336     }
   6337     if (s->str_count) {
   6338         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per string)\n",
   6339                 "strings", s->str_count, s->str_size,
   6340                 (double)s->str_size / s->str_count);
   6341     }
   6342     if (s->obj_count) {
   6343         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per object)\n",
   6344                 "objects", s->obj_count, s->obj_size,
   6345                 (double)s->obj_size / s->obj_count);
   6346         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per object)\n",
   6347                 "  properties", s->prop_count, s->prop_size,
   6348                 (double)s->prop_count / s->obj_count);
   6349         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per shape)\n",
   6350                 "  shapes", s->shape_count, s->shape_size,
   6351                 (double)s->shape_size / s->shape_count);
   6352     }
   6353     if (s->js_func_count) {
   6354         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n",
   6355                 "bytecode functions", s->js_func_count, s->js_func_size);
   6356         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per function)\n",
   6357                 "  bytecode", s->js_func_count, s->js_func_code_size,
   6358                 (double)s->js_func_code_size / s->js_func_count);
   6359         if (s->js_func_pc2line_count) {
   6360             fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per function)\n",
   6361                     "  pc2line", s->js_func_pc2line_count,
   6362                     s->js_func_pc2line_size,
   6363                     (double)s->js_func_pc2line_size / s->js_func_pc2line_count);
   6364         }
   6365     }
   6366     if (s->c_func_count) {
   6367         fprintf(fp, "%-20s %8"PRId64"\n", "C functions", s->c_func_count);
   6368     }
   6369     if (s->array_count) {
   6370         fprintf(fp, "%-20s %8"PRId64"\n", "arrays", s->array_count);
   6371         if (s->fast_array_count) {
   6372             fprintf(fp, "%-20s %8"PRId64"\n", "  fast arrays", s->fast_array_count);
   6373             fprintf(fp, "%-20s %8"PRId64" %8"PRId64"  (%0.1f per fast array)\n",
   6374                     "  elements", s->fast_array_elements,
   6375                     s->fast_array_elements * (int)sizeof(JSValue),
   6376                     (double)s->fast_array_elements / s->fast_array_count);
   6377         }
   6378     }
   6379     if (s->binary_object_count) {
   6380         fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n",
   6381                 "binary objects", s->binary_object_count, s->binary_object_size);
   6382     }
   6383 }
   6384 
   6385 JSValue JS_GetGlobalObject(JSContext *ctx)
   6386 {
   6387     return JS_DupValue(ctx, ctx->global_obj);
   6388 }
   6389 
   6390 /* WARNING: obj is freed */
   6391 JSValue JS_Throw(JSContext *ctx, JSValue obj)
   6392 {
   6393     JSRuntime *rt = ctx->rt;
   6394     JS_FreeValue(ctx, rt->current_exception);
   6395     rt->current_exception = obj;
   6396     return JS_EXCEPTION;
   6397 }
   6398 
   6399 /* return the pending exception (cannot be called twice). */
   6400 JSValue JS_GetException(JSContext *ctx)
   6401 {
   6402     JSValue val;
   6403     JSRuntime *rt = ctx->rt;
   6404     val = rt->current_exception;
   6405     rt->current_exception = JS_UNINITIALIZED;
   6406     return val;
   6407 }
   6408 
   6409 JS_BOOL JS_HasException(JSContext *ctx)
   6410 {
   6411     return !JS_IsUninitialized(ctx->rt->current_exception);
   6412 }
   6413 
   6414 static void dbuf_put_leb128(DynBuf *s, uint32_t v)
   6415 {
   6416     uint32_t a;
   6417     for(;;) {
   6418         a = v & 0x7f;
   6419         v >>= 7;
   6420         if (v != 0) {
   6421             dbuf_putc(s, a | 0x80);
   6422         } else {
   6423             dbuf_putc(s, a);
   6424             break;
   6425         }
   6426     }
   6427 }
   6428 
   6429 static void dbuf_put_sleb128(DynBuf *s, int32_t v1)
   6430 {
   6431     uint32_t v = v1;
   6432     dbuf_put_leb128(s, (2 * v) ^ -(v >> 31));
   6433 }
   6434 
   6435 static int get_leb128(uint32_t *pval, const uint8_t *buf,
   6436                       const uint8_t *buf_end)
   6437 {
   6438     const uint8_t *ptr = buf;
   6439     uint32_t v, a, i;
   6440     v = 0;
   6441     for(i = 0; i < 5; i++) {
   6442         if (unlikely(ptr >= buf_end))
   6443             break;
   6444         a = *ptr++;
   6445         v |= (a & 0x7f) << (i * 7);
   6446         if (!(a & 0x80)) {
   6447             *pval = v;
   6448             return ptr - buf;
   6449         }
   6450     }
   6451     *pval = 0;
   6452     return -1;
   6453 }
   6454 
   6455 static int get_sleb128(int32_t *pval, const uint8_t *buf,
   6456                        const uint8_t *buf_end)
   6457 {
   6458     int ret;
   6459     uint32_t val;
   6460     ret = get_leb128(&val, buf, buf_end);
   6461     if (ret < 0) {
   6462         *pval = 0;
   6463         return -1;
   6464     }
   6465     *pval = (val >> 1) ^ -(val & 1);
   6466     return ret;
   6467 }
   6468 
   6469 static int find_line_num(JSContext *ctx, JSFunctionBytecode *b,
   6470                          uint32_t pc_value)
   6471 {
   6472     const uint8_t *p_end, *p;
   6473     int new_line_num, line_num, pc, v, ret;
   6474     unsigned int op;
   6475 
   6476     if (!b->has_debug || !b->debug.pc2line_buf) {
   6477         /* function was stripped */
   6478         return -1;
   6479     }
   6480 
   6481     p = b->debug.pc2line_buf;
   6482     p_end = p + b->debug.pc2line_len;
   6483     pc = 0;
   6484     line_num = b->debug.line_num;
   6485     while (p < p_end) {
   6486         op = *p++;
   6487         if (op == 0) {
   6488             uint32_t val;
   6489             ret = get_leb128(&val, p, p_end);
   6490             if (ret < 0)
   6491                 goto fail;
   6492             pc += val;
   6493             p += ret;
   6494             ret = get_sleb128(&v, p, p_end);
   6495             if (ret < 0) {
   6496             fail:
   6497                 /* should never happen */
   6498                 return b->debug.line_num;
   6499             }
   6500             p += ret;
   6501             new_line_num = line_num + v;
   6502         } else {
   6503             op -= PC2LINE_OP_FIRST;
   6504             pc += (op / PC2LINE_RANGE);
   6505             new_line_num = line_num + (op % PC2LINE_RANGE) + PC2LINE_BASE;
   6506         }
   6507         if (pc_value < pc)
   6508             return line_num;
   6509         line_num = new_line_num;
   6510     }
   6511     return line_num;
   6512 }
   6513 
   6514 /* in order to avoid executing arbitrary code during the stack trace
   6515    generation, we only look at simple 'name' properties containing a
   6516    string. */
   6517 static const char *get_func_name(JSContext *ctx, JSValueConst func)
   6518 {
   6519     JSProperty *pr;
   6520     JSShapeProperty *prs;
   6521     JSValueConst val;
   6522 
   6523     if (JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)
   6524         return NULL;
   6525     prs = find_own_property(&pr, JS_VALUE_GET_OBJ(func), JS_ATOM_name);
   6526     if (!prs)
   6527         return NULL;
   6528     if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL)
   6529         return NULL;
   6530     val = pr->u.value;
   6531     if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)
   6532         return NULL;
   6533     return JS_ToCString(ctx, val);
   6534 }
   6535 
   6536 #define JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL (1 << 0)
   6537 /* only taken into account if filename is provided */
   6538 #define JS_BACKTRACE_FLAG_SINGLE_LEVEL     (1 << 1)
   6539 
   6540 /* if filename != NULL, an additional level is added with the filename
   6541    and line number information (used for parse error). */
   6542 static void build_backtrace(JSContext *ctx, JSValueConst error_obj,
   6543                             const char *filename, int line_num,
   6544                             int backtrace_flags)
   6545 {
   6546     JSStackFrame *sf;
   6547     JSValue str;
   6548     DynBuf dbuf;
   6549     const char *func_name_str;
   6550     const char *str1;
   6551     JSObject *p;
   6552     BOOL backtrace_barrier;
   6553 
   6554     js_dbuf_init(ctx, &dbuf);
   6555     if (filename) {
   6556         dbuf_printf(&dbuf, "    at %s", filename);
   6557         if (line_num != -1)
   6558             dbuf_printf(&dbuf, ":%d", line_num);
   6559         dbuf_putc(&dbuf, '\n');
   6560         str = JS_NewString(ctx, filename);
   6561         JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_fileName, str,
   6562                                JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
   6563         JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_lineNumber, JS_NewInt32(ctx, line_num),
   6564                                JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
   6565         if (backtrace_flags & JS_BACKTRACE_FLAG_SINGLE_LEVEL)
   6566             goto done;
   6567     }
   6568     for(sf = ctx->rt->current_stack_frame; sf != NULL; sf = sf->prev_frame) {
   6569         if (backtrace_flags & JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL) {
   6570             backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL;
   6571             continue;
   6572         }
   6573         func_name_str = get_func_name(ctx, sf->cur_func);
   6574         if (!func_name_str || func_name_str[0] == '\0')
   6575             str1 = "<anonymous>";
   6576         else
   6577             str1 = func_name_str;
   6578         dbuf_printf(&dbuf, "    at %s", str1);
   6579         JS_FreeCString(ctx, func_name_str);
   6580 
   6581         p = JS_VALUE_GET_OBJ(sf->cur_func);
   6582         backtrace_barrier = FALSE;
   6583         if (js_class_has_bytecode(p->class_id)) {
   6584             JSFunctionBytecode *b;
   6585             const char *atom_str;
   6586             int line_num1;
   6587 
   6588             b = p->u.func.function_bytecode;
   6589             backtrace_barrier = b->backtrace_barrier;
   6590             if (b->has_debug) {
   6591                 line_num1 = find_line_num(ctx, b,
   6592                                           sf->cur_pc - b->byte_code_buf - 1);
   6593                 atom_str = JS_AtomToCString(ctx, b->debug.filename);
   6594                 dbuf_printf(&dbuf, " (%s",
   6595                             atom_str ? atom_str : "<null>");
   6596                 JS_FreeCString(ctx, atom_str);
   6597                 if (line_num1 != -1)
   6598                     dbuf_printf(&dbuf, ":%d", line_num1);
   6599                 dbuf_putc(&dbuf, ')');
   6600             }
   6601         } else {
   6602             dbuf_printf(&dbuf, " (native)");
   6603         }
   6604         dbuf_putc(&dbuf, '\n');
   6605         /* stop backtrace if JS_EVAL_FLAG_BACKTRACE_BARRIER was used */
   6606         if (backtrace_barrier)
   6607             break;
   6608     }
   6609  done:
   6610     dbuf_putc(&dbuf, '\0');
   6611     if (dbuf_error(&dbuf))
   6612         str = JS_NULL;
   6613     else
   6614         str = JS_NewString(ctx, (char *)dbuf.buf);
   6615     dbuf_free(&dbuf);
   6616     JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, str,
   6617                            JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
   6618 }
   6619 
   6620 /* Note: it is important that no exception is returned by this function */
   6621 static BOOL is_backtrace_needed(JSContext *ctx, JSValueConst obj)
   6622 {
   6623     JSObject *p;
   6624     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
   6625         return FALSE;
   6626     p = JS_VALUE_GET_OBJ(obj);
   6627     if (p->class_id != JS_CLASS_ERROR)
   6628         return FALSE;
   6629     if (find_own_property1(p, JS_ATOM_stack))
   6630         return FALSE;
   6631     return TRUE;
   6632 }
   6633 
   6634 JSValue JS_NewError(JSContext *ctx)
   6635 {
   6636     return JS_NewObjectClass(ctx, JS_CLASS_ERROR);
   6637 }
   6638 
   6639 static JSValue JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num,
   6640                               const char *fmt, va_list ap, BOOL add_backtrace)
   6641 {
   6642     char buf[256];
   6643     JSValue obj, ret;
   6644 
   6645     vsnprintf(buf, sizeof(buf), fmt, ap);
   6646     obj = JS_NewObjectProtoClass(ctx, ctx->native_error_proto[error_num],
   6647                                  JS_CLASS_ERROR);
   6648     if (unlikely(JS_IsException(obj))) {
   6649         /* out of memory: throw JS_NULL to avoid recursing */
   6650         obj = JS_NULL;
   6651     } else {
   6652         JS_DefinePropertyValue(ctx, obj, JS_ATOM_message,
   6653                                JS_NewString(ctx, buf),
   6654                                JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
   6655     }
   6656     if (add_backtrace) {
   6657         build_backtrace(ctx, obj, NULL, 0, 0);
   6658     }
   6659     ret = JS_Throw(ctx, obj);
   6660     return ret;
   6661 }
   6662 
   6663 static JSValue JS_ThrowError(JSContext *ctx, JSErrorEnum error_num,
   6664                              const char *fmt, va_list ap)
   6665 {
   6666     JSRuntime *rt = ctx->rt;
   6667     JSStackFrame *sf;
   6668     BOOL add_backtrace;
   6669 
   6670     /* the backtrace is added later if called from a bytecode function */
   6671     sf = rt->current_stack_frame;
   6672     add_backtrace = !rt->in_out_of_memory &&
   6673         (!sf || (JS_GetFunctionBytecode(sf->cur_func) == NULL));
   6674     return JS_ThrowError2(ctx, error_num, fmt, ap, add_backtrace);
   6675 }
   6676 
   6677 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...)
   6678 {
   6679     JSValue val;
   6680     va_list ap;
   6681 
   6682     va_start(ap, fmt);
   6683     val = JS_ThrowError(ctx, JS_SYNTAX_ERROR, fmt, ap);
   6684     va_end(ap);
   6685     return val;
   6686 }
   6687 
   6688 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...)
   6689 {
   6690     JSValue val;
   6691     va_list ap;
   6692 
   6693     va_start(ap, fmt);
   6694     val = JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap);
   6695     va_end(ap);
   6696     return val;
   6697 }
   6698 
   6699 static int __attribute__((format(printf, 3, 4))) JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, const char *fmt, ...)
   6700 {
   6701     va_list ap;
   6702 
   6703     if ((flags & JS_PROP_THROW) ||
   6704         ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
   6705         va_start(ap, fmt);
   6706         JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap);
   6707         va_end(ap);
   6708         return -1;
   6709     } else {
   6710         return FALSE;
   6711     }
   6712 }
   6713 
   6714 /* never use it directly */
   6715 static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowTypeErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...)
   6716 {
   6717     char buf[ATOM_GET_STR_BUF_SIZE];
   6718     return JS_ThrowTypeError(ctx, fmt,
   6719                              JS_AtomGetStr(ctx, buf, sizeof(buf), atom));
   6720 }
   6721 
   6722 /* never use it directly */
   6723 static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowSyntaxErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...)
   6724 {
   6725     char buf[ATOM_GET_STR_BUF_SIZE];
   6726     return JS_ThrowSyntaxError(ctx, fmt,
   6727                              JS_AtomGetStr(ctx, buf, sizeof(buf), atom));
   6728 }
   6729 
   6730 /* %s is replaced by 'atom'. The macro is used so that gcc can check
   6731     the format string. */
   6732 #define JS_ThrowTypeErrorAtom(ctx, fmt, atom) __JS_ThrowTypeErrorAtom(ctx, atom, fmt, "")
   6733 #define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) __JS_ThrowSyntaxErrorAtom(ctx, atom, fmt, "")
   6734 
   6735 static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom)
   6736 {
   6737     if ((flags & JS_PROP_THROW) ||
   6738         ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
   6739         JS_ThrowTypeErrorAtom(ctx, "'%s' is read-only", atom);
   6740         return -1;
   6741     } else {
   6742         return FALSE;
   6743     }
   6744 }
   6745 
   6746 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...)
   6747 {
   6748     JSValue val;
   6749     va_list ap;
   6750 
   6751     va_start(ap, fmt);
   6752     val = JS_ThrowError(ctx, JS_REFERENCE_ERROR, fmt, ap);
   6753     va_end(ap);
   6754     return val;
   6755 }
   6756 
   6757 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...)
   6758 {
   6759     JSValue val;
   6760     va_list ap;
   6761 
   6762     va_start(ap, fmt);
   6763     val = JS_ThrowError(ctx, JS_RANGE_ERROR, fmt, ap);
   6764     va_end(ap);
   6765     return val;
   6766 }
   6767 
   6768 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...)
   6769 {
   6770     JSValue val;
   6771     va_list ap;
   6772 
   6773     va_start(ap, fmt);
   6774     val = JS_ThrowError(ctx, JS_INTERNAL_ERROR, fmt, ap);
   6775     va_end(ap);
   6776     return val;
   6777 }
   6778 
   6779 JSValue JS_ThrowOutOfMemory(JSContext *ctx)
   6780 {
   6781     JSRuntime *rt = ctx->rt;
   6782     if (!rt->in_out_of_memory) {
   6783         rt->in_out_of_memory = TRUE;
   6784         JS_ThrowInternalError(ctx, "out of memory");
   6785         rt->in_out_of_memory = FALSE;
   6786     }
   6787     return JS_EXCEPTION;
   6788 }
   6789 
   6790 static JSValue JS_ThrowStackOverflow(JSContext *ctx)
   6791 {
   6792     return JS_ThrowInternalError(ctx, "stack overflow");
   6793 }
   6794 
   6795 static JSValue JS_ThrowTypeErrorNotAnObject(JSContext *ctx)
   6796 {
   6797     return JS_ThrowTypeError(ctx, "not an object");
   6798 }
   6799 
   6800 static JSValue JS_ThrowTypeErrorNotASymbol(JSContext *ctx)
   6801 {
   6802     return JS_ThrowTypeError(ctx, "not a symbol");
   6803 }
   6804 
   6805 static JSValue JS_ThrowReferenceErrorNotDefined(JSContext *ctx, JSAtom name)
   6806 {
   6807     char buf[ATOM_GET_STR_BUF_SIZE];
   6808     return JS_ThrowReferenceError(ctx, "'%s' is not defined",
   6809                                   JS_AtomGetStr(ctx, buf, sizeof(buf), name));
   6810 }
   6811 
   6812 static JSValue JS_ThrowReferenceErrorUninitialized(JSContext *ctx, JSAtom name)
   6813 {
   6814     char buf[ATOM_GET_STR_BUF_SIZE];
   6815     return JS_ThrowReferenceError(ctx, "%s is not initialized",
   6816                                   name == JS_ATOM_NULL ? "lexical variable" :
   6817                                   JS_AtomGetStr(ctx, buf, sizeof(buf), name));
   6818 }
   6819 
   6820 static JSValue JS_ThrowReferenceErrorUninitialized2(JSContext *ctx,
   6821                                                     JSFunctionBytecode *b,
   6822                                                     int idx, BOOL is_ref)
   6823 {
   6824     JSAtom atom = JS_ATOM_NULL;
   6825     if (is_ref) {
   6826         atom = b->closure_var[idx].var_name;
   6827     } else {
   6828         /* not present if the function is stripped and contains no eval() */
   6829         if (b->vardefs)
   6830             atom = b->vardefs[b->arg_count + idx].var_name;
   6831     }
   6832     return JS_ThrowReferenceErrorUninitialized(ctx, atom);
   6833 }
   6834 
   6835 static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id)
   6836 {
   6837     JSRuntime *rt = ctx->rt;
   6838     JSAtom name;
   6839     name = rt->class_array[class_id].class_name;
   6840     return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name);
   6841 }
   6842 
   6843 static no_inline __exception int __js_poll_interrupts(JSContext *ctx)
   6844 {
   6845     JSRuntime *rt = ctx->rt;
   6846     ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT;
   6847     if (rt->interrupt_handler) {
   6848         if (rt->interrupt_handler(rt, rt->interrupt_opaque)) {
   6849             /* XXX: should set a specific flag to avoid catching */
   6850             JS_ThrowInternalError(ctx, "interrupted");
   6851             JS_SetUncatchableError(ctx, ctx->rt->current_exception, TRUE);
   6852             return -1;
   6853         }
   6854     }
   6855     return 0;
   6856 }
   6857 
   6858 static inline __exception int js_poll_interrupts(JSContext *ctx)
   6859 {
   6860     if (unlikely(--ctx->interrupt_counter <= 0)) {
   6861         return __js_poll_interrupts(ctx);
   6862     } else {
   6863         return 0;
   6864     }
   6865 }
   6866 
   6867 /* return -1 (exception) or TRUE/FALSE */
   6868 static int JS_SetPrototypeInternal(JSContext *ctx, JSValueConst obj,
   6869                                    JSValueConst proto_val,
   6870                                    BOOL throw_flag)
   6871 {
   6872     JSObject *proto, *p, *p1;
   6873     JSShape *sh;
   6874 
   6875     if (throw_flag) {
   6876         if (JS_VALUE_GET_TAG(obj) == JS_TAG_NULL ||
   6877             JS_VALUE_GET_TAG(obj) == JS_TAG_UNDEFINED)
   6878             goto not_obj;
   6879     } else {
   6880         if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
   6881             goto not_obj;
   6882     }
   6883     p = JS_VALUE_GET_OBJ(obj);
   6884     if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) {
   6885         if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_NULL) {
   6886         not_obj:
   6887             JS_ThrowTypeErrorNotAnObject(ctx);
   6888             return -1;
   6889         }
   6890         proto = NULL;
   6891     } else {
   6892         proto = JS_VALUE_GET_OBJ(proto_val);
   6893     }
   6894 
   6895     if (throw_flag && JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
   6896         return TRUE;
   6897 
   6898     if (unlikely(p->class_id == JS_CLASS_PROXY))
   6899         return js_proxy_setPrototypeOf(ctx, obj, proto_val, throw_flag);
   6900     sh = p->shape;
   6901     if (sh->proto == proto)
   6902         return TRUE;
   6903     if (!p->extensible) {
   6904         if (throw_flag) {
   6905             JS_ThrowTypeError(ctx, "object is not extensible");
   6906             return -1;
   6907         } else {
   6908             return FALSE;
   6909         }
   6910     }
   6911     if (proto) {
   6912         /* check if there is a cycle */
   6913         p1 = proto;
   6914         do {
   6915             if (p1 == p) {
   6916                 if (throw_flag) {
   6917                     JS_ThrowTypeError(ctx, "circular prototype chain");
   6918                     return -1;
   6919                 } else {
   6920                     return FALSE;
   6921                 }
   6922             }
   6923             /* Note: for Proxy objects, proto is NULL */
   6924             p1 = p1->shape->proto;
   6925         } while (p1 != NULL);
   6926         JS_DupValue(ctx, proto_val);
   6927     }
   6928 
   6929     if (js_shape_prepare_update(ctx, p, NULL))
   6930         return -1;
   6931     sh = p->shape;
   6932     if (sh->proto)
   6933         JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
   6934     sh->proto = proto;
   6935     return TRUE;
   6936 }
   6937 
   6938 /* return -1 (exception) or TRUE/FALSE */
   6939 int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val)
   6940 {
   6941     return JS_SetPrototypeInternal(ctx, obj, proto_val, TRUE);
   6942 }
   6943 
   6944 /* Only works for primitive types, otherwise return JS_NULL. */
   6945 static JSValueConst JS_GetPrototypePrimitive(JSContext *ctx, JSValueConst val)
   6946 {
   6947     switch(JS_VALUE_GET_NORM_TAG(val)) {
   6948     case JS_TAG_BIG_INT:
   6949         val = ctx->class_proto[JS_CLASS_BIG_INT];
   6950         break;
   6951 #ifdef CONFIG_BIGNUM
   6952     case JS_TAG_BIG_FLOAT:
   6953         val = ctx->class_proto[JS_CLASS_BIG_FLOAT];
   6954         break;
   6955     case JS_TAG_BIG_DECIMAL:
   6956         val = ctx->class_proto[JS_CLASS_BIG_DECIMAL];
   6957         break;
   6958 #endif
   6959     case JS_TAG_INT:
   6960     case JS_TAG_FLOAT64:
   6961         val = ctx->class_proto[JS_CLASS_NUMBER];
   6962         break;
   6963     case JS_TAG_BOOL:
   6964         val = ctx->class_proto[JS_CLASS_BOOLEAN];
   6965         break;
   6966     case JS_TAG_STRING:
   6967         val = ctx->class_proto[JS_CLASS_STRING];
   6968         break;
   6969     case JS_TAG_SYMBOL:
   6970         val = ctx->class_proto[JS_CLASS_SYMBOL];
   6971         break;
   6972     case JS_TAG_OBJECT:
   6973     case JS_TAG_NULL:
   6974     case JS_TAG_UNDEFINED:
   6975     default:
   6976         val = JS_NULL;
   6977         break;
   6978     }
   6979     return val;
   6980 }
   6981 
   6982 /* Return an Object, JS_NULL or JS_EXCEPTION in case of Proxy object. */
   6983 JSValue JS_GetPrototype(JSContext *ctx, JSValueConst obj)
   6984 {
   6985     JSValue val;
   6986     if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
   6987         JSObject *p;
   6988         p = JS_VALUE_GET_OBJ(obj);
   6989         if (unlikely(p->class_id == JS_CLASS_PROXY)) {
   6990             val = js_proxy_getPrototypeOf(ctx, obj);
   6991         } else {
   6992             p = p->shape->proto;
   6993             if (!p)
   6994                 val = JS_NULL;
   6995             else
   6996                 val = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
   6997         }
   6998     } else {
   6999         val = JS_DupValue(ctx, JS_GetPrototypePrimitive(ctx, obj));
   7000     }
   7001     return val;
   7002 }
   7003 
   7004 static JSValue JS_GetPrototypeFree(JSContext *ctx, JSValue obj)
   7005 {
   7006     JSValue obj1;
   7007     obj1 = JS_GetPrototype(ctx, obj);
   7008     JS_FreeValue(ctx, obj);
   7009     return obj1;
   7010 }
   7011 
   7012 /* return TRUE, FALSE or (-1) in case of exception */
   7013 static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val,
   7014                                    JSValueConst obj)
   7015 {
   7016     JSValue obj_proto;
   7017     JSObject *proto;
   7018     const JSObject *p, *proto1;
   7019     BOOL ret;
   7020 
   7021     if (!JS_IsFunction(ctx, obj))
   7022         return FALSE;
   7023     p = JS_VALUE_GET_OBJ(obj);
   7024     if (p->class_id == JS_CLASS_BOUND_FUNCTION) {
   7025         JSBoundFunction *s = p->u.bound_function;
   7026         return JS_IsInstanceOf(ctx, val, s->func_obj);
   7027     }
   7028 
   7029     /* Only explicitly boxed values are instances of constructors */
   7030     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
   7031         return FALSE;
   7032     obj_proto = JS_GetProperty(ctx, obj, JS_ATOM_prototype);
   7033     if (JS_VALUE_GET_TAG(obj_proto) != JS_TAG_OBJECT) {
   7034         if (!JS_IsException(obj_proto))
   7035             JS_ThrowTypeError(ctx, "operand 'prototype' property is not an object");
   7036         ret = -1;
   7037         goto done;
   7038     }
   7039     proto = JS_VALUE_GET_OBJ(obj_proto);
   7040     p = JS_VALUE_GET_OBJ(val);
   7041     for(;;) {
   7042         proto1 = p->shape->proto;
   7043         if (!proto1) {
   7044             /* slow case if proxy in the prototype chain */
   7045             if (unlikely(p->class_id == JS_CLASS_PROXY)) {
   7046                 JSValue obj1;
   7047                 obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, (JSObject *)p));
   7048                 for(;;) {
   7049                     obj1 = JS_GetPrototypeFree(ctx, obj1);
   7050                     if (JS_IsException(obj1)) {
   7051                         ret = -1;
   7052                         break;
   7053                     }
   7054                     if (JS_IsNull(obj1)) {
   7055                         ret = FALSE;
   7056                         break;
   7057                     }
   7058                     if (proto == JS_VALUE_GET_OBJ(obj1)) {
   7059                         JS_FreeValue(ctx, obj1);
   7060                         ret = TRUE;
   7061                         break;
   7062                     }
   7063                     /* must check for timeout to avoid infinite loop */
   7064                     if (js_poll_interrupts(ctx)) {
   7065                         JS_FreeValue(ctx, obj1);
   7066                         ret = -1;
   7067                         break;
   7068                     }
   7069                 }
   7070             } else {
   7071                 ret = FALSE;
   7072             }
   7073             break;
   7074         }
   7075         p = proto1;
   7076         if (proto == p) {
   7077             ret = TRUE;
   7078             break;
   7079         }
   7080     }
   7081 done:
   7082     JS_FreeValue(ctx, obj_proto);
   7083     return ret;
   7084 }
   7085 
   7086 /* return TRUE, FALSE or (-1) in case of exception */
   7087 int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj)
   7088 {
   7089     JSValue method;
   7090 
   7091     if (!JS_IsObject(obj))
   7092         goto fail;
   7093     method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_hasInstance);
   7094     if (JS_IsException(method))
   7095         return -1;
   7096     if (!JS_IsNull(method) && !JS_IsUndefined(method)) {
   7097         JSValue ret;
   7098         ret = JS_CallFree(ctx, method, obj, 1, &val);
   7099         return JS_ToBoolFree(ctx, ret);
   7100     }
   7101 
   7102     /* legacy case */
   7103     if (!JS_IsFunction(ctx, obj)) {
   7104     fail:
   7105         JS_ThrowTypeError(ctx, "invalid 'instanceof' right operand");
   7106         return -1;
   7107     }
   7108     return JS_OrdinaryIsInstanceOf(ctx, val, obj);
   7109 }
   7110 
   7111 /* return the value associated to the autoinit property or an exception */
   7112 typedef JSValue JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque);
   7113 
   7114 static JSAutoInitFunc *js_autoinit_func_table[] = {
   7115     js_instantiate_prototype, /* JS_AUTOINIT_ID_PROTOTYPE */
   7116     js_module_ns_autoinit, /* JS_AUTOINIT_ID_MODULE_NS */
   7117     JS_InstantiateFunctionListItem2, /* JS_AUTOINIT_ID_PROP */
   7118 };
   7119 
   7120 /* warning: 'prs' is reallocated after it */
   7121 static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop,
   7122                                JSProperty *pr, JSShapeProperty *prs)
   7123 {
   7124     JSValue val;
   7125     JSContext *realm;
   7126     JSAutoInitFunc *func;
   7127 
   7128     if (js_shape_prepare_update(ctx, p, &prs))
   7129         return -1;
   7130 
   7131     realm = js_autoinit_get_realm(pr);
   7132     func = js_autoinit_func_table[js_autoinit_get_id(pr)];
   7133     /* 'func' shall not modify the object properties 'pr' */
   7134     val = func(realm, p, prop, pr->u.init.opaque);
   7135     js_autoinit_free(ctx->rt, pr);
   7136     prs->flags &= ~JS_PROP_TMASK;
   7137     pr->u.value = JS_UNDEFINED;
   7138     if (JS_IsException(val))
   7139         return -1;
   7140     pr->u.value = val;
   7141     return 0;
   7142 }
   7143 
   7144 JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
   7145                                JSAtom prop, JSValueConst this_obj,
   7146                                BOOL throw_ref_error)
   7147 {
   7148     JSObject *p;
   7149     JSProperty *pr;
   7150     JSShapeProperty *prs;
   7151     uint32_t tag;
   7152 
   7153     tag = JS_VALUE_GET_TAG(obj);
   7154     if (unlikely(tag != JS_TAG_OBJECT)) {
   7155         switch(tag) {
   7156         case JS_TAG_NULL:
   7157             return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", prop);
   7158         case JS_TAG_UNDEFINED:
   7159             return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of undefined", prop);
   7160         case JS_TAG_EXCEPTION:
   7161             return JS_EXCEPTION;
   7162         case JS_TAG_STRING:
   7163             {
   7164                 JSString *p1 = JS_VALUE_GET_STRING(obj);
   7165                 if (__JS_AtomIsTaggedInt(prop)) {
   7166                     uint32_t idx, ch;
   7167                     idx = __JS_AtomToUInt32(prop);
   7168                     if (idx < p1->len) {
   7169                         if (p1->is_wide_char)
   7170                             ch = p1->u.str16[idx];
   7171                         else
   7172                             ch = p1->u.str8[idx];
   7173                         return js_new_string_char(ctx, ch);
   7174                     }
   7175                 } else if (prop == JS_ATOM_length) {
   7176                     return JS_NewInt32(ctx, p1->len);
   7177                 }
   7178             }
   7179             break;
   7180         default:
   7181             break;
   7182         }
   7183         /* cannot raise an exception */
   7184         p = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj));
   7185         if (!p)
   7186             return JS_UNDEFINED;
   7187     } else {
   7188         p = JS_VALUE_GET_OBJ(obj);
   7189     }
   7190 
   7191     for(;;) {
   7192         prs = find_own_property(&pr, p, prop);
   7193         if (prs) {
   7194             /* found */
   7195             if (unlikely(prs->flags & JS_PROP_TMASK)) {
   7196                 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
   7197                     if (unlikely(!pr->u.getset.getter)) {
   7198                         return JS_UNDEFINED;
   7199                     } else {
   7200                         JSValue func = JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter);
   7201                         /* Note: the field could be removed in the getter */
   7202                         func = JS_DupValue(ctx, func);
   7203                         return JS_CallFree(ctx, func, this_obj, 0, NULL);
   7204                     }
   7205                 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
   7206                     JSValue val = *pr->u.var_ref->pvalue;
   7207                     if (unlikely(JS_IsUninitialized(val)))
   7208                         return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
   7209                     return JS_DupValue(ctx, val);
   7210                 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
   7211                     /* Instantiate property and retry */
   7212                     if (JS_AutoInitProperty(ctx, p, prop, pr, prs))
   7213                         return JS_EXCEPTION;
   7214                     continue;
   7215                 }
   7216             } else {
   7217                 return JS_DupValue(ctx, pr->u.value);
   7218             }
   7219         }
   7220         if (unlikely(p->is_exotic)) {
   7221             /* exotic behaviors */
   7222             if (p->fast_array) {
   7223                 if (__JS_AtomIsTaggedInt(prop)) {
   7224                     uint32_t idx = __JS_AtomToUInt32(prop);
   7225                     if (idx < p->u.array.count) {
   7226                         /* we avoid duplicating the code */
   7227                         return JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx);
   7228                     } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
   7229                                p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
   7230                         return JS_UNDEFINED;
   7231                     }
   7232                 } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
   7233                            p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
   7234                     int ret;
   7235                     ret = JS_AtomIsNumericIndex(ctx, prop);
   7236                     if (ret != 0) {
   7237                         if (ret < 0)
   7238                             return JS_EXCEPTION;
   7239                         return JS_UNDEFINED;
   7240                     }
   7241                 }
   7242             } else {
   7243                 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
   7244                 if (em) {
   7245                     if (em->get_property) {
   7246                         JSValue obj1, retval;
   7247                         /* XXX: should pass throw_ref_error */
   7248                         /* Note: if 'p' is a prototype, it can be
   7249                            freed in the called function */
   7250                         obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
   7251                         retval = em->get_property(ctx, obj1, prop, this_obj);
   7252                         JS_FreeValue(ctx, obj1);
   7253                         return retval;
   7254                     }
   7255                     if (em->get_own_property) {
   7256                         JSPropertyDescriptor desc;
   7257                         int ret;
   7258                         JSValue obj1;
   7259 
   7260                         /* Note: if 'p' is a prototype, it can be
   7261                            freed in the called function */
   7262                         obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
   7263                         ret = em->get_own_property(ctx, &desc, obj1, prop);
   7264                         JS_FreeValue(ctx, obj1);
   7265                         if (ret < 0)
   7266                             return JS_EXCEPTION;
   7267                         if (ret) {
   7268                             if (desc.flags & JS_PROP_GETSET) {
   7269                                 JS_FreeValue(ctx, desc.setter);
   7270                                 return JS_CallFree(ctx, desc.getter, this_obj, 0, NULL);
   7271                             } else {
   7272                                 return desc.value;
   7273                             }
   7274                         }
   7275                     }
   7276                 }
   7277             }
   7278         }
   7279         p = p->shape->proto;
   7280         if (!p)
   7281             break;
   7282     }
   7283     if (unlikely(throw_ref_error)) {
   7284         return JS_ThrowReferenceErrorNotDefined(ctx, prop);
   7285     } else {
   7286         return JS_UNDEFINED;
   7287     }
   7288 }
   7289 
   7290 static JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *ctx, JSAtom atom)
   7291 {
   7292     return JS_ThrowTypeErrorAtom(ctx, "private class field '%s' does not exist",
   7293                                  atom);
   7294 }
   7295 
   7296 /* Private fields can be added even on non extensible objects or
   7297    Proxies */
   7298 static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj,
   7299                                  JSValueConst name, JSValue val)
   7300 {
   7301     JSObject *p;
   7302     JSShapeProperty *prs;
   7303     JSProperty *pr;
   7304     JSAtom prop;
   7305 
   7306     if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
   7307         JS_ThrowTypeErrorNotAnObject(ctx);
   7308         goto fail;
   7309     }
   7310     /* safety check */
   7311     if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) {
   7312         JS_ThrowTypeErrorNotASymbol(ctx);
   7313         goto fail;
   7314     }
   7315     prop = js_symbol_to_atom(ctx, (JSValue)name);
   7316     p = JS_VALUE_GET_OBJ(obj);
   7317     prs = find_own_property(&pr, p, prop);
   7318     if (prs) {
   7319         JS_ThrowTypeErrorAtom(ctx, "private class field '%s' already exists",
   7320                               prop);
   7321         goto fail;
   7322     }
   7323     pr = add_property(ctx, p, prop, JS_PROP_C_W_E);
   7324     if (unlikely(!pr)) {
   7325     fail:
   7326         JS_FreeValue(ctx, val);
   7327         return -1;
   7328     }
   7329     pr->u.value = val;
   7330     return 0;
   7331 }
   7332 
   7333 static JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj,
   7334                                   JSValueConst name)
   7335 {
   7336     JSObject *p;
   7337     JSShapeProperty *prs;
   7338     JSProperty *pr;
   7339     JSAtom prop;
   7340 
   7341     if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
   7342         return JS_ThrowTypeErrorNotAnObject(ctx);
   7343     /* safety check */
   7344     if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL))
   7345         return JS_ThrowTypeErrorNotASymbol(ctx);
   7346     prop = js_symbol_to_atom(ctx, (JSValue)name);
   7347     p = JS_VALUE_GET_OBJ(obj);
   7348     prs = find_own_property(&pr, p, prop);
   7349     if (!prs) {
   7350         JS_ThrowTypeErrorPrivateNotFound(ctx, prop);
   7351         return JS_EXCEPTION;
   7352     }
   7353     return JS_DupValue(ctx, pr->u.value);
   7354 }
   7355 
   7356 static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj,
   7357                               JSValueConst name, JSValue val)
   7358 {
   7359     JSObject *p;
   7360     JSShapeProperty *prs;
   7361     JSProperty *pr;
   7362     JSAtom prop;
   7363 
   7364     if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
   7365         JS_ThrowTypeErrorNotAnObject(ctx);
   7366         goto fail;
   7367     }
   7368     /* safety check */
   7369     if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) {
   7370         JS_ThrowTypeErrorNotASymbol(ctx);
   7371         goto fail;
   7372     }
   7373     prop = js_symbol_to_atom(ctx, (JSValue)name);
   7374     p = JS_VALUE_GET_OBJ(obj);
   7375     prs = find_own_property(&pr, p, prop);
   7376     if (!prs) {
   7377         JS_ThrowTypeErrorPrivateNotFound(ctx, prop);
   7378     fail:
   7379         JS_FreeValue(ctx, val);
   7380         return -1;
   7381     }
   7382     set_value(ctx, &pr->u.value, val);
   7383     return 0;
   7384 }
   7385 
   7386 /* add a private brand field to 'home_obj' if not already present and
   7387    if obj is != null add a private brand to it */
   7388 static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj)
   7389 {
   7390     JSObject *p, *p1;
   7391     JSShapeProperty *prs;
   7392     JSProperty *pr;
   7393     JSValue brand;
   7394     JSAtom brand_atom;
   7395 
   7396     if (unlikely(JS_VALUE_GET_TAG(home_obj) != JS_TAG_OBJECT)) {
   7397         JS_ThrowTypeErrorNotAnObject(ctx);
   7398         return -1;
   7399     }
   7400     p = JS_VALUE_GET_OBJ(home_obj);
   7401     prs = find_own_property(&pr, p, JS_ATOM_Private_brand);
   7402     if (!prs) {
   7403         /* if the brand is not present, add it */
   7404         brand = JS_NewSymbolFromAtom(ctx, JS_ATOM_brand, JS_ATOM_TYPE_PRIVATE);
   7405         if (JS_IsException(brand))
   7406             return -1;
   7407         pr = add_property(ctx, p, JS_ATOM_Private_brand, JS_PROP_C_W_E);
   7408         if (!pr) {
   7409             JS_FreeValue(ctx, brand);
   7410             return -1;
   7411         }
   7412         pr->u.value = JS_DupValue(ctx, brand);
   7413     } else {
   7414         brand = JS_DupValue(ctx, pr->u.value);
   7415     }
   7416     brand_atom = js_symbol_to_atom(ctx, brand);
   7417 
   7418     if (JS_IsObject(obj)) {
   7419         p1 = JS_VALUE_GET_OBJ(obj);
   7420         prs = find_own_property(&pr, p1, brand_atom);
   7421         if (unlikely(prs)) {
   7422             JS_FreeAtom(ctx, brand_atom);
   7423             JS_ThrowTypeError(ctx, "private method is already present");
   7424             return -1;
   7425         }
   7426         pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E);
   7427         JS_FreeAtom(ctx, brand_atom);
   7428         if (!pr)
   7429             return -1;
   7430         pr->u.value = JS_UNDEFINED;
   7431     } else {
   7432         JS_FreeAtom(ctx, brand_atom);
   7433     }
   7434     return 0;
   7435 }
   7436 
   7437 /* return a boolean telling if the brand of the home object of 'func'
   7438    is present on 'obj' or -1 in case of exception */
   7439 static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func)
   7440 {
   7441     JSObject *p, *p1, *home_obj;
   7442     JSShapeProperty *prs;
   7443     JSProperty *pr;
   7444     JSValueConst brand;
   7445 
   7446     /* get the home object of 'func' */
   7447     if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT))
   7448         goto not_obj;
   7449     p1 = JS_VALUE_GET_OBJ(func);
   7450     if (!js_class_has_bytecode(p1->class_id))
   7451         goto not_obj;
   7452     home_obj = p1->u.func.home_object;
   7453     if (!home_obj)
   7454         goto not_obj;
   7455     prs = find_own_property(&pr, home_obj, JS_ATOM_Private_brand);
   7456     if (!prs) {
   7457         JS_ThrowTypeError(ctx, "expecting <brand> private field");
   7458         return -1;
   7459     }
   7460     brand = pr->u.value;
   7461     /* safety check */
   7462     if (unlikely(JS_VALUE_GET_TAG(brand) != JS_TAG_SYMBOL))
   7463         goto not_obj;
   7464 
   7465     /* get the brand array of 'obj' */
   7466     if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
   7467     not_obj:
   7468         JS_ThrowTypeErrorNotAnObject(ctx);
   7469         return -1;
   7470     }
   7471     p = JS_VALUE_GET_OBJ(obj);
   7472     prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, (JSValue)brand));
   7473     return (prs != NULL);
   7474 }
   7475 
   7476 static uint32_t js_string_obj_get_length(JSContext *ctx,
   7477                                          JSValueConst obj)
   7478 {
   7479     JSObject *p;
   7480     JSString *p1;
   7481     uint32_t len = 0;
   7482 
   7483     /* This is a class exotic method: obj class_id is JS_CLASS_STRING */
   7484     p = JS_VALUE_GET_OBJ(obj);
   7485     if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) {
   7486         p1 = JS_VALUE_GET_STRING(p->u.object_data);
   7487         len = p1->len;
   7488     }
   7489     return len;
   7490 }
   7491 
   7492 static int num_keys_cmp(const void *p1, const void *p2, void *opaque)
   7493 {
   7494     JSContext *ctx = opaque;
   7495     JSAtom atom1 = ((const JSPropertyEnum *)p1)->atom;
   7496     JSAtom atom2 = ((const JSPropertyEnum *)p2)->atom;
   7497     uint32_t v1, v2;
   7498     BOOL atom1_is_integer, atom2_is_integer;
   7499 
   7500     atom1_is_integer = JS_AtomIsArrayIndex(ctx, &v1, atom1);
   7501     atom2_is_integer = JS_AtomIsArrayIndex(ctx, &v2, atom2);
   7502     assert(atom1_is_integer && atom2_is_integer);
   7503     if (v1 < v2)
   7504         return -1;
   7505     else if (v1 == v2)
   7506         return 0;
   7507     else
   7508         return 1;
   7509 }
   7510 
   7511 static void js_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len)
   7512 {
   7513     uint32_t i;
   7514     if (tab) {
   7515         for(i = 0; i < len; i++)
   7516             JS_FreeAtom(ctx, tab[i].atom);
   7517         js_free(ctx, tab);
   7518     }
   7519 }
   7520 
   7521 /* return < 0 in case if exception, 0 if OK. ptab and its atoms must
   7522    be freed by the user. */
   7523 static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx,
   7524                                                       JSPropertyEnum **ptab,
   7525                                                       uint32_t *plen,
   7526                                                       JSObject *p, int flags)
   7527 {
   7528     int i, j;
   7529     JSShape *sh;
   7530     JSShapeProperty *prs;
   7531     JSPropertyEnum *tab_atom, *tab_exotic;
   7532     JSAtom atom;
   7533     uint32_t num_keys_count, str_keys_count, sym_keys_count, atom_count;
   7534     uint32_t num_index, str_index, sym_index, exotic_count, exotic_keys_count;
   7535     BOOL is_enumerable, num_sorted;
   7536     uint32_t num_key;
   7537     JSAtomKindEnum kind;
   7538 
   7539     /* clear pointer for consistency in case of failure */
   7540     *ptab = NULL;
   7541     *plen = 0;
   7542 
   7543     /* compute the number of returned properties */
   7544     num_keys_count = 0;
   7545     str_keys_count = 0;
   7546     sym_keys_count = 0;
   7547     exotic_keys_count = 0;
   7548     exotic_count = 0;
   7549     tab_exotic = NULL;
   7550     sh = p->shape;
   7551     for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
   7552         atom = prs->atom;
   7553         if (atom != JS_ATOM_NULL) {
   7554             is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0);
   7555             kind = JS_AtomGetKind(ctx, atom);
   7556             if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&
   7557                 ((flags >> kind) & 1) != 0) {
   7558                 /* need to raise an exception in case of the module
   7559                    name space (implicit GetOwnProperty) */
   7560                 if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) &&
   7561                     (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY))) {
   7562                     JSVarRef *var_ref = p->prop[i].u.var_ref;
   7563                     if (unlikely(JS_IsUninitialized(*var_ref->pvalue))) {
   7564                         JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
   7565                         return -1;
   7566                     }
   7567                 }
   7568                 if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) {
   7569                     num_keys_count++;
   7570                 } else if (kind == JS_ATOM_KIND_STRING) {
   7571                     str_keys_count++;
   7572                 } else {
   7573                     sym_keys_count++;
   7574                 }
   7575             }
   7576         }
   7577     }
   7578 
   7579     if (p->is_exotic) {
   7580         if (p->fast_array) {
   7581             if (flags & JS_GPN_STRING_MASK) {
   7582                 num_keys_count += p->u.array.count;
   7583             }
   7584         } else if (p->class_id == JS_CLASS_STRING) {
   7585             if (flags & JS_GPN_STRING_MASK) {
   7586                 num_keys_count += js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
   7587             }
   7588         } else {
   7589             const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
   7590             if (em && em->get_own_property_names) {
   7591                 if (em->get_own_property_names(ctx, &tab_exotic, &exotic_count,
   7592                                                JS_MKPTR(JS_TAG_OBJECT, p)))
   7593                     return -1;
   7594                 for(i = 0; i < exotic_count; i++) {
   7595                     atom = tab_exotic[i].atom;
   7596                     kind = JS_AtomGetKind(ctx, atom);
   7597                     if (((flags >> kind) & 1) != 0) {
   7598                         is_enumerable = FALSE;
   7599                         if (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY)) {
   7600                             JSPropertyDescriptor desc;
   7601                             int res;
   7602                             /* set the "is_enumerable" field if necessary */
   7603                             res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);
   7604                             if (res < 0) {
   7605                                 js_free_prop_enum(ctx, tab_exotic, exotic_count);
   7606                                 return -1;
   7607                             }
   7608                             if (res) {
   7609                                 is_enumerable =
   7610                                     ((desc.flags & JS_PROP_ENUMERABLE) != 0);
   7611                                 js_free_desc(ctx, &desc);
   7612                             }
   7613                             tab_exotic[i].is_enumerable = is_enumerable;
   7614                         }
   7615                         if (!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) {
   7616                             exotic_keys_count++;
   7617                         }
   7618                     }
   7619                 }
   7620             }
   7621         }
   7622     }
   7623 
   7624     /* fill them */
   7625 
   7626     atom_count = num_keys_count + str_keys_count + sym_keys_count + exotic_keys_count;
   7627     /* avoid allocating 0 bytes */
   7628     tab_atom = js_malloc(ctx, sizeof(tab_atom[0]) * max_int(atom_count, 1));
   7629     if (!tab_atom) {
   7630         js_free_prop_enum(ctx, tab_exotic, exotic_count);
   7631         return -1;
   7632     }
   7633 
   7634     num_index = 0;
   7635     str_index = num_keys_count;
   7636     sym_index = str_index + str_keys_count;
   7637 
   7638     num_sorted = TRUE;
   7639     sh = p->shape;
   7640     for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
   7641         atom = prs->atom;
   7642         if (atom != JS_ATOM_NULL) {
   7643             is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0);
   7644             kind = JS_AtomGetKind(ctx, atom);
   7645             if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&
   7646                 ((flags >> kind) & 1) != 0) {
   7647                 if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) {
   7648                     j = num_index++;
   7649                     num_sorted = FALSE;
   7650                 } else if (kind == JS_ATOM_KIND_STRING) {
   7651                     j = str_index++;
   7652                 } else {
   7653                     j = sym_index++;
   7654                 }
   7655                 tab_atom[j].atom = JS_DupAtom(ctx, atom);
   7656                 tab_atom[j].is_enumerable = is_enumerable;
   7657             }
   7658         }
   7659     }
   7660 
   7661     if (p->is_exotic) {
   7662         int len;
   7663         if (p->fast_array) {
   7664             if (flags & JS_GPN_STRING_MASK) {
   7665                 len = p->u.array.count;
   7666                 goto add_array_keys;
   7667             }
   7668         } else if (p->class_id == JS_CLASS_STRING) {
   7669             if (flags & JS_GPN_STRING_MASK) {
   7670                 len = js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
   7671             add_array_keys:
   7672                 for(i = 0; i < len; i++) {
   7673                     tab_atom[num_index].atom = __JS_AtomFromUInt32(i);
   7674                     if (tab_atom[num_index].atom == JS_ATOM_NULL) {
   7675                         js_free_prop_enum(ctx, tab_atom, num_index);
   7676                         return -1;
   7677                     }
   7678                     tab_atom[num_index].is_enumerable = TRUE;
   7679                     num_index++;
   7680                 }
   7681             }
   7682         } else {
   7683             /* Note: exotic keys are not reordered and comes after the object own properties. */
   7684             for(i = 0; i < exotic_count; i++) {
   7685                 atom = tab_exotic[i].atom;
   7686                 is_enumerable = tab_exotic[i].is_enumerable;
   7687                 kind = JS_AtomGetKind(ctx, atom);
   7688                 if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&
   7689                     ((flags >> kind) & 1) != 0) {
   7690                     tab_atom[sym_index].atom = atom;
   7691                     tab_atom[sym_index].is_enumerable = is_enumerable;
   7692                     sym_index++;
   7693                 } else {
   7694                     JS_FreeAtom(ctx, atom);
   7695                 }
   7696             }
   7697             js_free(ctx, tab_exotic);
   7698         }
   7699     }
   7700 
   7701     assert(num_index == num_keys_count);
   7702     assert(str_index == num_keys_count + str_keys_count);
   7703     assert(sym_index == atom_count);
   7704 
   7705     if (num_keys_count != 0 && !num_sorted) {
   7706         rqsort(tab_atom, num_keys_count, sizeof(tab_atom[0]), num_keys_cmp,
   7707                ctx);
   7708     }
   7709     *ptab = tab_atom;
   7710     *plen = atom_count;
   7711     return 0;
   7712 }
   7713 
   7714 int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab,
   7715                            uint32_t *plen, JSValueConst obj, int flags)
   7716 {
   7717     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
   7718         JS_ThrowTypeErrorNotAnObject(ctx);
   7719         return -1;
   7720     }
   7721     return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen,
   7722                                           JS_VALUE_GET_OBJ(obj), flags);
   7723 }
   7724 
   7725 /* Return -1 if exception,
   7726    FALSE if the property does not exist, TRUE if it exists. If TRUE is
   7727    returned, the property descriptor 'desc' is filled present. */
   7728 static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,
   7729                                      JSObject *p, JSAtom prop)
   7730 {
   7731     JSShapeProperty *prs;
   7732     JSProperty *pr;
   7733 
   7734 retry:
   7735     prs = find_own_property(&pr, p, prop);
   7736     if (prs) {
   7737         if (desc) {
   7738             desc->flags = prs->flags & JS_PROP_C_W_E;
   7739             desc->getter = JS_UNDEFINED;
   7740             desc->setter = JS_UNDEFINED;
   7741             desc->value = JS_UNDEFINED;
   7742             if (unlikely(prs->flags & JS_PROP_TMASK)) {
   7743                 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
   7744                     desc->flags |= JS_PROP_GETSET;
   7745                     if (pr->u.getset.getter)
   7746                         desc->getter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
   7747                     if (pr->u.getset.setter)
   7748                         desc->setter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
   7749                 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
   7750                     JSValue val = *pr->u.var_ref->pvalue;
   7751                     if (unlikely(JS_IsUninitialized(val))) {
   7752                         JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
   7753                         return -1;
   7754                     }
   7755                     desc->value = JS_DupValue(ctx, val);
   7756                 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
   7757                     /* Instantiate property and retry */
   7758                     if (JS_AutoInitProperty(ctx, p, prop, pr, prs))
   7759                         return -1;
   7760                     goto retry;
   7761                 }
   7762             } else {
   7763                 desc->value = JS_DupValue(ctx, pr->u.value);
   7764             }
   7765         } else {
   7766             /* for consistency, send the exception even if desc is NULL */
   7767             if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF)) {
   7768                 if (unlikely(JS_IsUninitialized(*pr->u.var_ref->pvalue))) {
   7769                     JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
   7770                     return -1;
   7771                 }
   7772             } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
   7773                 /* nothing to do: delay instantiation until actual value and/or attributes are read */
   7774             }
   7775         }
   7776         return TRUE;
   7777     }
   7778     if (p->is_exotic) {
   7779         if (p->fast_array) {
   7780             /* specific case for fast arrays */
   7781             if (__JS_AtomIsTaggedInt(prop)) {
   7782                 uint32_t idx;
   7783                 idx = __JS_AtomToUInt32(prop);
   7784                 if (idx < p->u.array.count) {
   7785                     if (desc) {
   7786                         desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE |
   7787                             JS_PROP_CONFIGURABLE;
   7788                         desc->getter = JS_UNDEFINED;
   7789                         desc->setter = JS_UNDEFINED;
   7790                         desc->value = JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx);
   7791                     }
   7792                     return TRUE;
   7793                 }
   7794             }
   7795         } else {
   7796             const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
   7797             if (em && em->get_own_property) {
   7798                 return em->get_own_property(ctx, desc,
   7799                                             JS_MKPTR(JS_TAG_OBJECT, p), prop);
   7800             }
   7801         }
   7802     }
   7803     return FALSE;
   7804 }
   7805 
   7806 int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc,
   7807                       JSValueConst obj, JSAtom prop)
   7808 {
   7809     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
   7810         JS_ThrowTypeErrorNotAnObject(ctx);
   7811         return -1;
   7812     }
   7813     return JS_GetOwnPropertyInternal(ctx, desc, JS_VALUE_GET_OBJ(obj), prop);
   7814 }
   7815 
   7816 /* return -1 if exception (Proxy object only) or TRUE/FALSE */
   7817 int JS_IsExtensible(JSContext *ctx, JSValueConst obj)
   7818 {
   7819     JSObject *p;
   7820 
   7821     if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
   7822         return FALSE;
   7823     p = JS_VALUE_GET_OBJ(obj);
   7824     if (unlikely(p->class_id == JS_CLASS_PROXY))
   7825         return js_proxy_isExtensible(ctx, obj);
   7826     else
   7827         return p->extensible;
   7828 }
   7829 
   7830 /* return -1 if exception (Proxy object only) or TRUE/FALSE */
   7831 int JS_PreventExtensions(JSContext *ctx, JSValueConst obj)
   7832 {
   7833     JSObject *p;
   7834 
   7835     if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
   7836         return FALSE;
   7837     p = JS_VALUE_GET_OBJ(obj);
   7838     if (unlikely(p->class_id == JS_CLASS_PROXY))
   7839         return js_proxy_preventExtensions(ctx, obj);
   7840     p->extensible = FALSE;
   7841     return TRUE;
   7842 }
   7843 
   7844 /* return -1 if exception otherwise TRUE or FALSE */
   7845 int JS_HasPropertyStr(JSContext *ctx, JSValueConst obj, const char *propname)
   7846 {
   7847     JSAtom atom;
   7848     int ret;
   7849     atom = JS_NewAtom(ctx, propname);
   7850     ret = JS_HasProperty(ctx, obj, atom);
   7851     JS_FreeAtom(ctx, atom);
   7852     return ret;
   7853 }
   7854 
   7855 /* return -1 if exception otherwise TRUE or FALSE */
   7856 int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop)
   7857 {
   7858     JSObject *p;
   7859     int ret;
   7860     JSValue obj1;
   7861 
   7862     if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
   7863         return FALSE;
   7864     p = JS_VALUE_GET_OBJ(obj);
   7865     for(;;) {
   7866         if (p->is_exotic) {
   7867             const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
   7868             if (em && em->has_property) {
   7869                 /* has_property can free the prototype */
   7870                 obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
   7871                 ret = em->has_property(ctx, obj1, prop);
   7872                 JS_FreeValue(ctx, obj1);
   7873                 return ret;
   7874             }
   7875         }
   7876         /* JS_GetOwnPropertyInternal can free the prototype */
   7877         JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
   7878         ret = JS_GetOwnPropertyInternal(ctx, NULL, p, prop);
   7879         JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
   7880         if (ret != 0)
   7881             return ret;
   7882         if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
   7883             p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
   7884             ret = JS_AtomIsNumericIndex(ctx, prop);
   7885             if (ret != 0) {
   7886                 if (ret < 0)
   7887                     return -1;
   7888                 return FALSE;
   7889             }
   7890         }
   7891         p = p->shape->proto;
   7892         if (!p)
   7893             break;
   7894     }
   7895     return FALSE;
   7896 }
   7897 
   7898 /* val must be a symbol */
   7899 static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val)
   7900 {
   7901     JSAtomStruct *p = JS_VALUE_GET_PTR(val);
   7902     return js_get_atom_index(ctx->rt, p);
   7903 }
   7904 
   7905 /* return JS_ATOM_NULL in case of exception */
   7906 JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val)
   7907 {
   7908     JSAtom atom;
   7909     uint32_t tag;
   7910     tag = JS_VALUE_GET_TAG(val);
   7911     if (tag == JS_TAG_INT &&
   7912         (uint32_t)JS_VALUE_GET_INT(val) <= JS_ATOM_MAX_INT) {
   7913         /* fast path for integer values */
   7914         atom = __JS_AtomFromUInt32(JS_VALUE_GET_INT(val));
   7915     } else if (tag == JS_TAG_SYMBOL) {
   7916         JSAtomStruct *p = JS_VALUE_GET_PTR(val);
   7917         atom = JS_DupAtom(ctx, js_get_atom_index(ctx->rt, p));
   7918     } else {
   7919         JSValue str;
   7920         str = JS_ToPropertyKey(ctx, val);
   7921         if (JS_IsException(str))
   7922             return JS_ATOM_NULL;
   7923         if (JS_VALUE_GET_TAG(str) == JS_TAG_SYMBOL) {
   7924             atom = js_symbol_to_atom(ctx, str);
   7925         } else {
   7926             atom = JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(str));
   7927         }
   7928     }
   7929     return atom;
   7930 }
   7931 
   7932 static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj,
   7933                                    JSValue prop)
   7934 {
   7935     JSAtom atom;
   7936     JSValue ret;
   7937 
   7938     if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT &&
   7939                JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) {
   7940         JSObject *p;
   7941         uint32_t idx;
   7942         /* fast path for array access */
   7943         p = JS_VALUE_GET_OBJ(this_obj);
   7944         idx = JS_VALUE_GET_INT(prop);
   7945         switch(p->class_id) {
   7946         case JS_CLASS_ARRAY:
   7947         case JS_CLASS_ARGUMENTS:
   7948             if (unlikely(idx >= p->u.array.count)) goto slow_path;
   7949             return JS_DupValue(ctx, p->u.array.u.values[idx]);
   7950         case JS_CLASS_INT8_ARRAY:
   7951             if (unlikely(idx >= p->u.array.count)) goto slow_path;
   7952             return JS_NewInt32(ctx, p->u.array.u.int8_ptr[idx]);
   7953         case JS_CLASS_UINT8C_ARRAY:
   7954         case JS_CLASS_UINT8_ARRAY:
   7955             if (unlikely(idx >= p->u.array.count)) goto slow_path;
   7956             return JS_NewInt32(ctx, p->u.array.u.uint8_ptr[idx]);
   7957         case JS_CLASS_INT16_ARRAY:
   7958             if (unlikely(idx >= p->u.array.count)) goto slow_path;
   7959             return JS_NewInt32(ctx, p->u.array.u.int16_ptr[idx]);
   7960         case JS_CLASS_UINT16_ARRAY:
   7961             if (unlikely(idx >= p->u.array.count)) goto slow_path;
   7962             return JS_NewInt32(ctx, p->u.array.u.uint16_ptr[idx]);
   7963         case JS_CLASS_INT32_ARRAY:
   7964             if (unlikely(idx >= p->u.array.count)) goto slow_path;
   7965             return JS_NewInt32(ctx, p->u.array.u.int32_ptr[idx]);
   7966         case JS_CLASS_UINT32_ARRAY:
   7967             if (unlikely(idx >= p->u.array.count)) goto slow_path;
   7968             return JS_NewUint32(ctx, p->u.array.u.uint32_ptr[idx]);
   7969         case JS_CLASS_BIG_INT64_ARRAY:
   7970             if (unlikely(idx >= p->u.array.count)) goto slow_path;
   7971             return JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]);
   7972         case JS_CLASS_BIG_UINT64_ARRAY:
   7973             if (unlikely(idx >= p->u.array.count)) goto slow_path;
   7974             return JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]);
   7975         case JS_CLASS_FLOAT32_ARRAY:
   7976             if (unlikely(idx >= p->u.array.count)) goto slow_path;
   7977             return __JS_NewFloat64(ctx, p->u.array.u.float_ptr[idx]);
   7978         case JS_CLASS_FLOAT64_ARRAY:
   7979             if (unlikely(idx >= p->u.array.count)) goto slow_path;
   7980             return __JS_NewFloat64(ctx, p->u.array.u.double_ptr[idx]);
   7981         default:
   7982             goto slow_path;
   7983         }
   7984     } else {
   7985     slow_path:
   7986         atom = JS_ValueToAtom(ctx, prop);
   7987         JS_FreeValue(ctx, prop);
   7988         if (unlikely(atom == JS_ATOM_NULL))
   7989             return JS_EXCEPTION;
   7990         ret = JS_GetProperty(ctx, this_obj, atom);
   7991         JS_FreeAtom(ctx, atom);
   7992         return ret;
   7993     }
   7994 }
   7995 
   7996 JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
   7997                              uint32_t idx)
   7998 {
   7999     return JS_GetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx));
   8000 }
   8001 
   8002 /* Check if an object has a generalized numeric property. Return value:
   8003    -1 for exception,
   8004    TRUE if property exists, stored into *pval,
   8005    FALSE if proprty does not exist.
   8006  */
   8007 static int JS_TryGetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, JSValue *pval)
   8008 {
   8009     JSValue val = JS_UNDEFINED;
   8010     JSAtom prop;
   8011     int present;
   8012 
   8013     if (likely((uint64_t)idx <= JS_ATOM_MAX_INT)) {
   8014         /* fast path */
   8015         present = JS_HasProperty(ctx, obj, __JS_AtomFromUInt32(idx));
   8016         if (present > 0) {
   8017             val = JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx));
   8018             if (unlikely(JS_IsException(val)))
   8019                 present = -1;
   8020         }
   8021     } else {
   8022         prop = JS_NewAtomInt64(ctx, idx);
   8023         present = -1;
   8024         if (likely(prop != JS_ATOM_NULL)) {
   8025             present = JS_HasProperty(ctx, obj, prop);
   8026             if (present > 0) {
   8027                 val = JS_GetProperty(ctx, obj, prop);
   8028                 if (unlikely(JS_IsException(val)))
   8029                     present = -1;
   8030             }
   8031             JS_FreeAtom(ctx, prop);
   8032         }
   8033     }
   8034     *pval = val;
   8035     return present;
   8036 }
   8037 
   8038 static JSValue JS_GetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx)
   8039 {
   8040     JSAtom prop;
   8041     JSValue val;
   8042 
   8043     if ((uint64_t)idx <= INT32_MAX) {
   8044         /* fast path for fast arrays */
   8045         return JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx));
   8046     }
   8047     prop = JS_NewAtomInt64(ctx, idx);
   8048     if (prop == JS_ATOM_NULL)
   8049         return JS_EXCEPTION;
   8050 
   8051     val = JS_GetProperty(ctx, obj, prop);
   8052     JS_FreeAtom(ctx, prop);
   8053     return val;
   8054 }
   8055 
   8056 JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj,
   8057                           const char *prop)
   8058 {
   8059     JSAtom atom;
   8060     JSValue ret;
   8061     atom = JS_NewAtom(ctx, prop);
   8062     ret = JS_GetProperty(ctx, this_obj, atom);
   8063     JS_FreeAtom(ctx, atom);
   8064     return ret;
   8065 }
   8066 
   8067 /* Note: the property value is not initialized. Return NULL if memory
   8068    error. */
   8069 static JSProperty *add_property(JSContext *ctx,
   8070                                 JSObject *p, JSAtom prop, int prop_flags)
   8071 {
   8072     JSShape *sh, *new_sh;
   8073 
   8074     sh = p->shape;
   8075     if (sh->is_hashed) {
   8076         /* try to find an existing shape */
   8077         new_sh = find_hashed_shape_prop(ctx->rt, sh, prop, prop_flags);
   8078         if (new_sh) {
   8079             /* matching shape found: use it */
   8080             /*  the property array may need to be resized */
   8081             if (new_sh->prop_size != sh->prop_size) {
   8082                 JSProperty *new_prop;
   8083                 new_prop = js_realloc(ctx, p->prop, sizeof(p->prop[0]) *
   8084                                       new_sh->prop_size);
   8085                 if (!new_prop)
   8086                     return NULL;
   8087                 p->prop = new_prop;
   8088             }
   8089             p->shape = js_dup_shape(new_sh);
   8090             js_free_shape(ctx->rt, sh);
   8091             return &p->prop[new_sh->prop_count - 1];
   8092         } else if (sh->header.ref_count != 1) {
   8093             /* if the shape is shared, clone it */
   8094             new_sh = js_clone_shape(ctx, sh);
   8095             if (!new_sh)
   8096                 return NULL;
   8097             /* hash the cloned shape */
   8098             new_sh->is_hashed = TRUE;
   8099             js_shape_hash_link(ctx->rt, new_sh);
   8100             js_free_shape(ctx->rt, p->shape);
   8101             p->shape = new_sh;
   8102         }
   8103     }
   8104     assert(p->shape->header.ref_count == 1);
   8105     if (add_shape_property(ctx, &p->shape, p, prop, prop_flags))
   8106         return NULL;
   8107     return &p->prop[p->shape->prop_count - 1];
   8108 }
   8109 
   8110 /* can be called on Array or Arguments objects. return < 0 if
   8111    memory alloc error. */
   8112 static no_inline __exception int convert_fast_array_to_array(JSContext *ctx,
   8113                                                              JSObject *p)
   8114 {
   8115     JSProperty *pr;
   8116     JSShape *sh;
   8117     JSValue *tab;
   8118     uint32_t i, len, new_count;
   8119 
   8120     if (js_shape_prepare_update(ctx, p, NULL))
   8121         return -1;
   8122     len = p->u.array.count;
   8123     /* resize the properties once to simplify the error handling */
   8124     sh = p->shape;
   8125     new_count = sh->prop_count + len;
   8126     if (new_count > sh->prop_size) {
   8127         if (resize_properties(ctx, &p->shape, p, new_count))
   8128             return -1;
   8129     }
   8130 
   8131     tab = p->u.array.u.values;
   8132     for(i = 0; i < len; i++) {
   8133         /* add_property cannot fail here but
   8134            __JS_AtomFromUInt32(i) fails for i > INT32_MAX */
   8135         pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E);
   8136         pr->u.value = *tab++;
   8137     }
   8138     js_free(ctx, p->u.array.u.values);
   8139     p->u.array.count = 0;
   8140     p->u.array.u.values = NULL; /* fail safe */
   8141     p->u.array.u1.size = 0;
   8142     p->fast_array = 0;
   8143     return 0;
   8144 }
   8145 
   8146 static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom)
   8147 {
   8148     JSShape *sh;
   8149     JSShapeProperty *pr, *lpr, *prop;
   8150     JSProperty *pr1;
   8151     uint32_t lpr_idx;
   8152     intptr_t h, h1;
   8153 
   8154  redo:
   8155     sh = p->shape;
   8156     h1 = atom & sh->prop_hash_mask;
   8157     h = prop_hash_end(sh)[-h1 - 1];
   8158     prop = get_shape_prop(sh);
   8159     lpr = NULL;
   8160     lpr_idx = 0;   /* prevent warning */
   8161     while (h != 0) {
   8162         pr = &prop[h - 1];
   8163         if (likely(pr->atom == atom)) {
   8164             /* found ! */
   8165             if (!(pr->flags & JS_PROP_CONFIGURABLE))
   8166                 return FALSE;
   8167             /* realloc the shape if needed */
   8168             if (lpr)
   8169                 lpr_idx = lpr - get_shape_prop(sh);
   8170             if (js_shape_prepare_update(ctx, p, &pr))
   8171                 return -1;
   8172             sh = p->shape;
   8173             /* remove property */
   8174             if (lpr) {
   8175                 lpr = get_shape_prop(sh) + lpr_idx;
   8176                 lpr->hash_next = pr->hash_next;
   8177             } else {
   8178                 prop_hash_end(sh)[-h1 - 1] = pr->hash_next;
   8179             }
   8180             sh->deleted_prop_count++;
   8181             /* free the entry */
   8182             pr1 = &p->prop[h - 1];
   8183             free_property(ctx->rt, pr1, pr->flags);
   8184             JS_FreeAtom(ctx, pr->atom);
   8185             /* put default values */
   8186             pr->flags = 0;
   8187             pr->atom = JS_ATOM_NULL;
   8188             pr1->u.value = JS_UNDEFINED;
   8189 
   8190             /* compact the properties if too many deleted properties */
   8191             if (sh->deleted_prop_count >= 8 &&
   8192                 sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) {
   8193                 compact_properties(ctx, p);
   8194             }
   8195             return TRUE;
   8196         }
   8197         lpr = pr;
   8198         h = pr->hash_next;
   8199     }
   8200 
   8201     if (p->is_exotic) {
   8202         if (p->fast_array) {
   8203             uint32_t idx;
   8204             if (JS_AtomIsArrayIndex(ctx, &idx, atom) &&
   8205                 idx < p->u.array.count) {
   8206                 if (p->class_id == JS_CLASS_ARRAY ||
   8207                     p->class_id == JS_CLASS_ARGUMENTS) {
   8208                     /* Special case deleting the last element of a fast Array */
   8209                     if (idx == p->u.array.count - 1) {
   8210                         JS_FreeValue(ctx, p->u.array.u.values[idx]);
   8211                         p->u.array.count = idx;
   8212                         return TRUE;
   8213                     }
   8214                     if (convert_fast_array_to_array(ctx, p))
   8215                         return -1;
   8216                     goto redo;
   8217                 } else {
   8218                     return FALSE;
   8219                 }
   8220             }
   8221         } else {
   8222             const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
   8223             if (em && em->delete_property) {
   8224                 return em->delete_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), atom);
   8225             }
   8226         }
   8227     }
   8228     /* not found */
   8229     return TRUE;
   8230 }
   8231 
   8232 static int call_setter(JSContext *ctx, JSObject *setter,
   8233                        JSValueConst this_obj, JSValue val, int flags)
   8234 {
   8235     JSValue ret, func;
   8236     if (likely(setter)) {
   8237         func = JS_MKPTR(JS_TAG_OBJECT, setter);
   8238         /* Note: the field could be removed in the setter */
   8239         func = JS_DupValue(ctx, func);
   8240         ret = JS_CallFree(ctx, func, this_obj, 1, (JSValueConst *)&val);
   8241         JS_FreeValue(ctx, val);
   8242         if (JS_IsException(ret))
   8243             return -1;
   8244         JS_FreeValue(ctx, ret);
   8245         return TRUE;
   8246     } else {
   8247         JS_FreeValue(ctx, val);
   8248         if ((flags & JS_PROP_THROW) ||
   8249             ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
   8250             JS_ThrowTypeError(ctx, "no setter for property");
   8251             return -1;
   8252         }
   8253         return FALSE;
   8254     }
   8255 }
   8256 
   8257 /* set the array length and remove the array elements if necessary. */
   8258 static int set_array_length(JSContext *ctx, JSObject *p, JSValue val,
   8259                             int flags)
   8260 {
   8261     uint32_t len, idx, cur_len;
   8262     int i, ret;
   8263 
   8264     /* Note: this call can reallocate the properties of 'p' */
   8265     ret = JS_ToArrayLengthFree(ctx, &len, val, FALSE);
   8266     if (ret)
   8267         return -1;
   8268     /* JS_ToArrayLengthFree() must be done before the read-only test */
   8269     if (unlikely(!(p->shape->prop[0].flags & JS_PROP_WRITABLE)))
   8270         return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);
   8271 
   8272     if (likely(p->fast_array)) {
   8273         uint32_t old_len = p->u.array.count;
   8274         if (len < old_len) {
   8275             for(i = len; i < old_len; i++) {
   8276                 JS_FreeValue(ctx, p->u.array.u.values[i]);
   8277             }
   8278             p->u.array.count = len;
   8279         }
   8280         p->prop[0].u.value = JS_NewUint32(ctx, len);
   8281     } else {
   8282         /* Note: length is always a uint32 because the object is an
   8283            array */
   8284         JS_ToUint32(ctx, &cur_len, p->prop[0].u.value);
   8285         if (len < cur_len) {
   8286             uint32_t d;
   8287             JSShape *sh;
   8288             JSShapeProperty *pr;
   8289 
   8290             d = cur_len - len;
   8291             sh = p->shape;
   8292             if (d <= sh->prop_count) {
   8293                 JSAtom atom;
   8294 
   8295                 /* faster to iterate */
   8296                 while (cur_len > len) {
   8297                     atom = JS_NewAtomUInt32(ctx, cur_len - 1);
   8298                     ret = delete_property(ctx, p, atom);
   8299                     JS_FreeAtom(ctx, atom);
   8300                     if (unlikely(!ret)) {
   8301                         /* unlikely case: property is not
   8302                            configurable */
   8303                         break;
   8304                     }
   8305                     cur_len--;
   8306                 }
   8307             } else {
   8308                 /* faster to iterate thru all the properties. Need two
   8309                    passes in case one of the property is not
   8310                    configurable */
   8311                 cur_len = len;
   8312                 for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count;
   8313                     i++, pr++) {
   8314                     if (pr->atom != JS_ATOM_NULL &&
   8315                         JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) {
   8316                         if (idx >= cur_len &&
   8317                             !(pr->flags & JS_PROP_CONFIGURABLE)) {
   8318                             cur_len = idx + 1;
   8319                         }
   8320                     }
   8321                 }
   8322 
   8323                 for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count;
   8324                     i++, pr++) {
   8325                     if (pr->atom != JS_ATOM_NULL &&
   8326                         JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) {
   8327                         if (idx >= cur_len) {
   8328                             /* remove the property */
   8329                             delete_property(ctx, p, pr->atom);
   8330                             /* WARNING: the shape may have been modified */
   8331                             sh = p->shape;
   8332                             pr = get_shape_prop(sh) + i;
   8333                         }
   8334                     }
   8335                 }
   8336             }
   8337         } else {
   8338             cur_len = len;
   8339         }
   8340         set_value(ctx, &p->prop[0].u.value, JS_NewUint32(ctx, cur_len));
   8341         if (unlikely(cur_len > len)) {
   8342             return JS_ThrowTypeErrorOrFalse(ctx, flags, "not configurable");
   8343         }
   8344     }
   8345     return TRUE;
   8346 }
   8347 
   8348 /* return -1 if exception */
   8349 static int expand_fast_array(JSContext *ctx, JSObject *p, uint32_t new_len)
   8350 {
   8351     uint32_t new_size;
   8352     size_t slack;
   8353     JSValue *new_array_prop;
   8354     /* XXX: potential arithmetic overflow */
   8355     new_size = max_int(new_len, p->u.array.u1.size * 3 / 2);
   8356     new_array_prop = js_realloc2(ctx, p->u.array.u.values, sizeof(JSValue) * new_size, &slack);
   8357     if (!new_array_prop)
   8358         return -1;
   8359     new_size += slack / sizeof(*new_array_prop);
   8360     p->u.array.u.values = new_array_prop;
   8361     p->u.array.u1.size = new_size;
   8362     return 0;
   8363 }
   8364 
   8365 /* Preconditions: 'p' must be of class JS_CLASS_ARRAY, p->fast_array =
   8366    TRUE and p->extensible = TRUE */
   8367 static int add_fast_array_element(JSContext *ctx, JSObject *p,
   8368                                   JSValue val, int flags)
   8369 {
   8370     uint32_t new_len, array_len;
   8371     /* extend the array by one */
   8372     /* XXX: convert to slow array if new_len > 2^31-1 elements */
   8373     new_len = p->u.array.count + 1;
   8374     /* update the length if necessary. We assume that if the length is
   8375        not an integer, then if it >= 2^31.  */
   8376     if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT)) {
   8377         array_len = JS_VALUE_GET_INT(p->prop[0].u.value);
   8378         if (new_len > array_len) {
   8379             if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) {
   8380                 JS_FreeValue(ctx, val);
   8381                 return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);
   8382             }
   8383             p->prop[0].u.value = JS_NewInt32(ctx, new_len);
   8384         }
   8385     }
   8386     if (unlikely(new_len > p->u.array.u1.size)) {
   8387         if (expand_fast_array(ctx, p, new_len)) {
   8388             JS_FreeValue(ctx, val);
   8389             return -1;
   8390         }
   8391     }
   8392     p->u.array.u.values[new_len - 1] = val;
   8393     p->u.array.count = new_len;
   8394     return TRUE;
   8395 }
   8396 
   8397 /* Allocate a new fast array. Its 'length' property is set to zero. It
   8398    maximum size is 2^31-1 elements. For convenience, 'len' is a 64 bit
   8399    integer. WARNING: the content of the array is not initialized. */
   8400 static JSValue js_allocate_fast_array(JSContext *ctx, int64_t len)
   8401 {
   8402     JSValue arr;
   8403     JSObject *p;
   8404 
   8405     if (len > INT32_MAX)
   8406         return JS_ThrowRangeError(ctx, "invalid array length");
   8407     arr = JS_NewArray(ctx);
   8408     if (JS_IsException(arr))
   8409         return arr;
   8410     if (len > 0) {
   8411         p = JS_VALUE_GET_OBJ(arr);
   8412         if (expand_fast_array(ctx, p, len) < 0) {
   8413             JS_FreeValue(ctx, arr);
   8414             return JS_EXCEPTION;
   8415         }
   8416         p->u.array.count = len;
   8417     }
   8418     return arr;
   8419 }
   8420 
   8421 static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc)
   8422 {
   8423     JS_FreeValue(ctx, desc->getter);
   8424     JS_FreeValue(ctx, desc->setter);
   8425     JS_FreeValue(ctx, desc->value);
   8426 }
   8427 
   8428 /* return -1 in case of exception or TRUE or FALSE. Warning: 'val' is
   8429    freed by the function. 'flags' is a bitmask of JS_PROP_NO_ADD,
   8430    JS_PROP_THROW or JS_PROP_THROW_STRICT. If JS_PROP_NO_ADD is set,
   8431    the new property is not added and an error is raised. 'this_obj' is
   8432    the receiver. If obj != this_obj, then obj must be an object
   8433    (Reflect.set case). */
   8434 int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj,
   8435                            JSAtom prop, JSValue val, JSValueConst this_obj, int flags)
   8436 {
   8437     JSObject *p, *p1;
   8438     JSShapeProperty *prs;
   8439     JSProperty *pr;
   8440     uint32_t tag;
   8441     JSPropertyDescriptor desc;
   8442     int ret;
   8443 #if 0
   8444     printf("JS_SetPropertyInternal: "); print_atom(ctx, prop); printf("\n");
   8445 #endif
   8446     tag = JS_VALUE_GET_TAG(this_obj);
   8447     if (unlikely(tag != JS_TAG_OBJECT)) {
   8448         if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
   8449             p = NULL;
   8450             p1 = JS_VALUE_GET_OBJ(obj);
   8451             goto prototype_lookup;
   8452         } else {
   8453             switch(tag) {
   8454             case JS_TAG_NULL:
   8455                 JS_FreeValue(ctx, val);
   8456                 JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of null", prop);
   8457                 return -1;
   8458             case JS_TAG_UNDEFINED:
   8459                 JS_FreeValue(ctx, val);
   8460                 JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of undefined", prop);
   8461                 return -1;
   8462             default:
   8463                 /* even on a primitive type we can have setters on the prototype */
   8464                 p = NULL;
   8465                 p1 = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj));
   8466                 goto prototype_lookup;
   8467             }
   8468         }
   8469     } else {
   8470         p = JS_VALUE_GET_OBJ(this_obj);
   8471         p1 = JS_VALUE_GET_OBJ(obj);
   8472         if (unlikely(p != p1))
   8473             goto retry2;
   8474     }
   8475 
   8476     /* fast path if obj == this_obj */
   8477  retry:
   8478     prs = find_own_property(&pr, p1, prop);
   8479     if (prs) {
   8480         if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE |
   8481                                   JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) {
   8482             /* fast case */
   8483             set_value(ctx, &pr->u.value, val);
   8484             return TRUE;
   8485         } else if (prs->flags & JS_PROP_LENGTH) {
   8486             assert(p->class_id == JS_CLASS_ARRAY);
   8487             assert(prop == JS_ATOM_length);
   8488             return set_array_length(ctx, p, val, flags);
   8489         } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
   8490             return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags);
   8491         } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
   8492             /* JS_PROP_WRITABLE is always true for variable
   8493                references, but they are write protected in module name
   8494                spaces. */
   8495             if (p->class_id == JS_CLASS_MODULE_NS)
   8496                 goto read_only_prop;
   8497             set_value(ctx, pr->u.var_ref->pvalue, val);
   8498             return TRUE;
   8499         } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
   8500             /* Instantiate property and retry (potentially useless) */
   8501             if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) {
   8502                 JS_FreeValue(ctx, val);
   8503                 return -1;
   8504             }
   8505             goto retry;
   8506         } else {
   8507             goto read_only_prop;
   8508         }
   8509     }
   8510 
   8511     for(;;) {
   8512         if (p1->is_exotic) {
   8513             if (p1->fast_array) {
   8514                 if (__JS_AtomIsTaggedInt(prop)) {
   8515                     uint32_t idx = __JS_AtomToUInt32(prop);
   8516                     if (idx < p1->u.array.count) {
   8517                         if (unlikely(p == p1))
   8518                             return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, flags);
   8519                         else
   8520                             break;
   8521                     } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY &&
   8522                                p1->class_id <= JS_CLASS_FLOAT64_ARRAY) {
   8523                         goto typed_array_oob;
   8524                     }
   8525                 } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY &&
   8526                            p1->class_id <= JS_CLASS_FLOAT64_ARRAY) {
   8527                     ret = JS_AtomIsNumericIndex(ctx, prop);
   8528                     if (ret != 0) {
   8529                         if (ret < 0) {
   8530                             JS_FreeValue(ctx, val);
   8531                             return -1;
   8532                         }
   8533                     typed_array_oob:
   8534                         /* must convert the argument even if out of bound access */
   8535                         if (p1->class_id == JS_CLASS_BIG_INT64_ARRAY ||
   8536                             p1->class_id == JS_CLASS_BIG_UINT64_ARRAY) {
   8537                             int64_t v;
   8538                             if (JS_ToBigInt64Free(ctx, &v, val))
   8539                                 return -1;
   8540                         } else {
   8541                             val = JS_ToNumberFree(ctx, val);
   8542                             JS_FreeValue(ctx, val);
   8543                             if (JS_IsException(val))
   8544                                 return -1;
   8545                         }
   8546                         return TRUE;
   8547                     }
   8548                 }
   8549             } else {
   8550                 const JSClassExoticMethods *em = ctx->rt->class_array[p1->class_id].exotic;
   8551                 if (em) {
   8552                     JSValue obj1;
   8553                     if (em->set_property) {
   8554                         /* set_property can free the prototype */
   8555                         obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
   8556                         ret = em->set_property(ctx, obj1, prop,
   8557                                                val, this_obj, flags);
   8558                         JS_FreeValue(ctx, obj1);
   8559                         JS_FreeValue(ctx, val);
   8560                         return ret;
   8561                     }
   8562                     if (em->get_own_property) {
   8563                         /* get_own_property can free the prototype */
   8564                         obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
   8565                         ret = em->get_own_property(ctx, &desc,
   8566                                                    obj1, prop);
   8567                         JS_FreeValue(ctx, obj1);
   8568                         if (ret < 0) {
   8569                             JS_FreeValue(ctx, val);
   8570                             return ret;
   8571                         }
   8572                         if (ret) {
   8573                             if (desc.flags & JS_PROP_GETSET) {
   8574                                 JSObject *setter;
   8575                                 if (JS_IsUndefined(desc.setter))
   8576                                     setter = NULL;
   8577                                 else
   8578                                     setter = JS_VALUE_GET_OBJ(desc.setter);
   8579                                 ret = call_setter(ctx, setter, this_obj, val, flags);
   8580                                 JS_FreeValue(ctx, desc.getter);
   8581                                 JS_FreeValue(ctx, desc.setter);
   8582                                 return ret;
   8583                             } else {
   8584                                 JS_FreeValue(ctx, desc.value);
   8585                                 if (!(desc.flags & JS_PROP_WRITABLE))
   8586                                     goto read_only_prop;
   8587                                 if (likely(p == p1)) {
   8588                                     ret = JS_DefineProperty(ctx, this_obj, prop, val,
   8589                                                             JS_UNDEFINED, JS_UNDEFINED,
   8590                                                             JS_PROP_HAS_VALUE);
   8591                                     JS_FreeValue(ctx, val);
   8592                                     return ret;
   8593                                 } else {
   8594                                     break;
   8595                                 }
   8596                             }
   8597                         }
   8598                     }
   8599                 }
   8600             }
   8601         }
   8602         p1 = p1->shape->proto;
   8603     prototype_lookup:
   8604         if (!p1)
   8605             break;
   8606 
   8607     retry2:
   8608         prs = find_own_property(&pr, p1, prop);
   8609         if (prs) {
   8610             if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
   8611                 return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags);
   8612             } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
   8613                 /* Instantiate property and retry (potentially useless) */
   8614                 if (JS_AutoInitProperty(ctx, p1, prop, pr, prs))
   8615                     return -1;
   8616                 goto retry2;
   8617             } else if (!(prs->flags & JS_PROP_WRITABLE)) {
   8618                 goto read_only_prop;
   8619             }
   8620         }
   8621     }
   8622 
   8623     if (unlikely(flags & JS_PROP_NO_ADD)) {
   8624         JS_FreeValue(ctx, val);
   8625         JS_ThrowReferenceErrorNotDefined(ctx, prop);
   8626         return -1;
   8627     }
   8628 
   8629     if (unlikely(!p)) {
   8630         JS_FreeValue(ctx, val);
   8631         return JS_ThrowTypeErrorOrFalse(ctx, flags, "not an object");
   8632     }
   8633 
   8634     if (unlikely(!p->extensible)) {
   8635         JS_FreeValue(ctx, val);
   8636         return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible");
   8637     }
   8638 
   8639     if (likely(p == JS_VALUE_GET_OBJ(obj))) {
   8640         if (p->is_exotic) {
   8641             if (p->class_id == JS_CLASS_ARRAY && p->fast_array &&
   8642                 __JS_AtomIsTaggedInt(prop)) {
   8643                 uint32_t idx = __JS_AtomToUInt32(prop);
   8644                 if (idx == p->u.array.count) {
   8645                     /* fast case */
   8646                     return add_fast_array_element(ctx, p, val, flags);
   8647                 } else {
   8648                     goto generic_create_prop;
   8649                 }
   8650             } else {
   8651                 goto generic_create_prop;
   8652             }
   8653         } else {
   8654             pr = add_property(ctx, p, prop, JS_PROP_C_W_E);
   8655             if (unlikely(!pr)) {
   8656                 JS_FreeValue(ctx, val);
   8657                 return -1;
   8658             }
   8659             pr->u.value = val;
   8660             return TRUE;
   8661         }
   8662     } else {
   8663         /* generic case: modify the property in this_obj if it already exists */
   8664         ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
   8665         if (ret < 0) {
   8666             JS_FreeValue(ctx, val);
   8667             return ret;
   8668         }
   8669         if (ret) {
   8670             if (desc.flags & JS_PROP_GETSET) {
   8671                 JS_FreeValue(ctx, desc.getter);
   8672                 JS_FreeValue(ctx, desc.setter);
   8673                 JS_FreeValue(ctx, val);
   8674                 return JS_ThrowTypeErrorOrFalse(ctx, flags, "setter is forbidden");
   8675             } else {
   8676                 JS_FreeValue(ctx, desc.value);
   8677                 if (!(desc.flags & JS_PROP_WRITABLE) ||
   8678                     p->class_id == JS_CLASS_MODULE_NS) {
   8679                 read_only_prop:
   8680                     JS_FreeValue(ctx, val);
   8681                     return JS_ThrowTypeErrorReadOnly(ctx, flags, prop);
   8682                 }
   8683             }
   8684             ret = JS_DefineProperty(ctx, this_obj, prop, val,
   8685                                     JS_UNDEFINED, JS_UNDEFINED,
   8686                                     JS_PROP_HAS_VALUE);
   8687             JS_FreeValue(ctx, val);
   8688             return ret;
   8689         } else {
   8690         generic_create_prop:
   8691             ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED,
   8692                                     flags |
   8693                                     JS_PROP_HAS_VALUE |
   8694                                     JS_PROP_HAS_ENUMERABLE |
   8695                                     JS_PROP_HAS_WRITABLE |
   8696                                     JS_PROP_HAS_CONFIGURABLE |
   8697                                     JS_PROP_C_W_E);
   8698             JS_FreeValue(ctx, val);
   8699             return ret;
   8700         }
   8701     }
   8702 }
   8703 
   8704 /* flags can be JS_PROP_THROW or JS_PROP_THROW_STRICT */
   8705 static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
   8706                                JSValue prop, JSValue val, int flags)
   8707 {
   8708     if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT &&
   8709                JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) {
   8710         JSObject *p;
   8711         uint32_t idx;
   8712         double d;
   8713         int32_t v;
   8714 
   8715         /* fast path for array access */
   8716         p = JS_VALUE_GET_OBJ(this_obj);
   8717         idx = JS_VALUE_GET_INT(prop);
   8718         switch(p->class_id) {
   8719         case JS_CLASS_ARRAY:
   8720             if (unlikely(idx >= (uint32_t)p->u.array.count)) {
   8721                 JSObject *p1;
   8722                 JSShape *sh1;
   8723 
   8724                 /* fast path to add an element to the array */
   8725                 if (idx != (uint32_t)p->u.array.count ||
   8726                     !p->fast_array || !p->extensible)
   8727                     goto slow_path;
   8728                 /* check if prototype chain has a numeric property */
   8729                 p1 = p->shape->proto;
   8730                 while (p1 != NULL) {
   8731                     sh1 = p1->shape;
   8732                     if (p1->class_id == JS_CLASS_ARRAY) {
   8733                         if (unlikely(!p1->fast_array))
   8734                             goto slow_path;
   8735                     } else if (p1->class_id == JS_CLASS_OBJECT) {
   8736                         if (unlikely(sh1->has_small_array_index))
   8737                             goto slow_path;
   8738                     } else {
   8739                         goto slow_path;
   8740                     }
   8741                     p1 = sh1->proto;
   8742                 }
   8743                 /* add element */
   8744                 return add_fast_array_element(ctx, p, val, flags);
   8745             }
   8746             set_value(ctx, &p->u.array.u.values[idx], val);
   8747             break;
   8748         case JS_CLASS_ARGUMENTS:
   8749             if (unlikely(idx >= (uint32_t)p->u.array.count))
   8750                 goto slow_path;
   8751             set_value(ctx, &p->u.array.u.values[idx], val);
   8752             break;
   8753         case JS_CLASS_UINT8C_ARRAY:
   8754             if (JS_ToUint8ClampFree(ctx, &v, val))
   8755                 return -1;
   8756             /* Note: the conversion can detach the typed array, so the
   8757                array bound check must be done after */
   8758             if (unlikely(idx >= (uint32_t)p->u.array.count))
   8759                 goto ta_out_of_bound;
   8760             p->u.array.u.uint8_ptr[idx] = v;
   8761             break;
   8762         case JS_CLASS_INT8_ARRAY:
   8763         case JS_CLASS_UINT8_ARRAY:
   8764             if (JS_ToInt32Free(ctx, &v, val))
   8765                 return -1;
   8766             if (unlikely(idx >= (uint32_t)p->u.array.count))
   8767                 goto ta_out_of_bound;
   8768             p->u.array.u.uint8_ptr[idx] = v;
   8769             break;
   8770         case JS_CLASS_INT16_ARRAY:
   8771         case JS_CLASS_UINT16_ARRAY:
   8772             if (JS_ToInt32Free(ctx, &v, val))
   8773                 return -1;
   8774             if (unlikely(idx >= (uint32_t)p->u.array.count))
   8775                 goto ta_out_of_bound;
   8776             p->u.array.u.uint16_ptr[idx] = v;
   8777             break;
   8778         case JS_CLASS_INT32_ARRAY:
   8779         case JS_CLASS_UINT32_ARRAY:
   8780             if (JS_ToInt32Free(ctx, &v, val))
   8781                 return -1;
   8782             if (unlikely(idx >= (uint32_t)p->u.array.count))
   8783                 goto ta_out_of_bound;
   8784             p->u.array.u.uint32_ptr[idx] = v;
   8785             break;
   8786         case JS_CLASS_BIG_INT64_ARRAY:
   8787         case JS_CLASS_BIG_UINT64_ARRAY:
   8788             /* XXX: need specific conversion function */
   8789             {
   8790                 int64_t v;
   8791                 if (JS_ToBigInt64Free(ctx, &v, val))
   8792                     return -1;
   8793                 if (unlikely(idx >= (uint32_t)p->u.array.count))
   8794                     goto ta_out_of_bound;
   8795                 p->u.array.u.uint64_ptr[idx] = v;
   8796             }
   8797             break;
   8798         case JS_CLASS_FLOAT32_ARRAY:
   8799             if (JS_ToFloat64Free(ctx, &d, val))
   8800                 return -1;
   8801             if (unlikely(idx >= (uint32_t)p->u.array.count))
   8802                 goto ta_out_of_bound;
   8803             p->u.array.u.float_ptr[idx] = d;
   8804             break;
   8805         case JS_CLASS_FLOAT64_ARRAY:
   8806             if (JS_ToFloat64Free(ctx, &d, val))
   8807                 return -1;
   8808             if (unlikely(idx >= (uint32_t)p->u.array.count)) {
   8809             ta_out_of_bound:
   8810                 return TRUE;
   8811             }
   8812             p->u.array.u.double_ptr[idx] = d;
   8813             break;
   8814         default:
   8815             goto slow_path;
   8816         }
   8817         return TRUE;
   8818     } else {
   8819         JSAtom atom;
   8820         int ret;
   8821     slow_path:
   8822         atom = JS_ValueToAtom(ctx, prop);
   8823         JS_FreeValue(ctx, prop);
   8824         if (unlikely(atom == JS_ATOM_NULL)) {
   8825             JS_FreeValue(ctx, val);
   8826             return -1;
   8827         }
   8828         ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, this_obj, flags);
   8829         JS_FreeAtom(ctx, atom);
   8830         return ret;
   8831     }
   8832 }
   8833 
   8834 int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
   8835                          uint32_t idx, JSValue val)
   8836 {
   8837     return JS_SetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx), val,
   8838                                JS_PROP_THROW);
   8839 }
   8840 
   8841 int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj,
   8842                         int64_t idx, JSValue val)
   8843 {
   8844     JSAtom prop;
   8845     int res;
   8846 
   8847     if ((uint64_t)idx <= INT32_MAX) {
   8848         /* fast path for fast arrays */
   8849         return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val,
   8850                                    JS_PROP_THROW);
   8851     }
   8852     prop = JS_NewAtomInt64(ctx, idx);
   8853     if (prop == JS_ATOM_NULL) {
   8854         JS_FreeValue(ctx, val);
   8855         return -1;
   8856     }
   8857     res = JS_SetProperty(ctx, this_obj, prop, val);
   8858     JS_FreeAtom(ctx, prop);
   8859     return res;
   8860 }
   8861 
   8862 int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj,
   8863                       const char *prop, JSValue val)
   8864 {
   8865     JSAtom atom;
   8866     int ret;
   8867     atom = JS_NewAtom(ctx, prop);
   8868     ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, this_obj, JS_PROP_THROW);
   8869     JS_FreeAtom(ctx, atom);
   8870     return ret;
   8871 }
   8872 
   8873 /* compute the property flags. For each flag: (JS_PROP_HAS_x forces
   8874    it, otherwise def_flags is used)
   8875    Note: makes assumption about the bit pattern of the flags
   8876 */
   8877 static int get_prop_flags(int flags, int def_flags)
   8878 {
   8879     int mask;
   8880     mask = (flags >> JS_PROP_HAS_SHIFT) & JS_PROP_C_W_E;
   8881     return (flags & mask) | (def_flags & ~mask);
   8882 }
   8883 
   8884 static int JS_CreateProperty(JSContext *ctx, JSObject *p,
   8885                              JSAtom prop, JSValueConst val,
   8886                              JSValueConst getter, JSValueConst setter,
   8887                              int flags)
   8888 {
   8889     JSProperty *pr;
   8890     int ret, prop_flags;
   8891 
   8892     /* add a new property or modify an existing exotic one */
   8893     if (p->is_exotic) {
   8894         if (p->class_id == JS_CLASS_ARRAY) {
   8895             uint32_t idx, len;
   8896 
   8897             if (p->fast_array) {
   8898                 if (__JS_AtomIsTaggedInt(prop)) {
   8899                     idx = __JS_AtomToUInt32(prop);
   8900                     if (idx == p->u.array.count) {
   8901                         if (!p->extensible)
   8902                             goto not_extensible;
   8903                         if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET))
   8904                             goto convert_to_array;
   8905                         prop_flags = get_prop_flags(flags, 0);
   8906                         if (prop_flags != JS_PROP_C_W_E)
   8907                             goto convert_to_array;
   8908                         return add_fast_array_element(ctx, p,
   8909                                                       JS_DupValue(ctx, val), flags);
   8910                     } else {
   8911                         goto convert_to_array;
   8912                     }
   8913                 } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) {
   8914                     /* convert the fast array to normal array */
   8915                 convert_to_array:
   8916                     if (convert_fast_array_to_array(ctx, p))
   8917                         return -1;
   8918                     goto generic_array;
   8919                 }
   8920             } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) {
   8921                 JSProperty *plen;
   8922                 JSShapeProperty *pslen;
   8923             generic_array:
   8924                 /* update the length field */
   8925                 plen = &p->prop[0];
   8926                 JS_ToUint32(ctx, &len, plen->u.value);
   8927                 if ((idx + 1) > len) {
   8928                     pslen = get_shape_prop(p->shape);
   8929                     if (unlikely(!(pslen->flags & JS_PROP_WRITABLE)))
   8930                         return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);
   8931                     /* XXX: should update the length after defining
   8932                        the property */
   8933                     len = idx + 1;
   8934                     set_value(ctx, &plen->u.value, JS_NewUint32(ctx, len));
   8935                 }
   8936             }
   8937         } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
   8938                    p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
   8939             ret = JS_AtomIsNumericIndex(ctx, prop);
   8940             if (ret != 0) {
   8941                 if (ret < 0)
   8942                     return -1;
   8943                 return JS_ThrowTypeErrorOrFalse(ctx, flags, "cannot create numeric index in typed array");
   8944             }
   8945         } else if (!(flags & JS_PROP_NO_EXOTIC)) {
   8946             const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
   8947             if (em) {
   8948                 if (em->define_own_property) {
   8949                     return em->define_own_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p),
   8950                                                    prop, val, getter, setter, flags);
   8951                 }
   8952                 ret = JS_IsExtensible(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
   8953                 if (ret < 0)
   8954                     return -1;
   8955                 if (!ret)
   8956                     goto not_extensible;
   8957             }
   8958         }
   8959     }
   8960 
   8961     if (!p->extensible) {
   8962     not_extensible:
   8963         return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible");
   8964     }
   8965 
   8966     if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
   8967         prop_flags = (flags & (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) |
   8968             JS_PROP_GETSET;
   8969     } else {
   8970         prop_flags = flags & JS_PROP_C_W_E;
   8971     }
   8972     pr = add_property(ctx, p, prop, prop_flags);
   8973     if (unlikely(!pr))
   8974         return -1;
   8975     if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
   8976         pr->u.getset.getter = NULL;
   8977         if ((flags & JS_PROP_HAS_GET) && JS_IsFunction(ctx, getter)) {
   8978             pr->u.getset.getter =
   8979                 JS_VALUE_GET_OBJ(JS_DupValue(ctx, getter));
   8980         }
   8981         pr->u.getset.setter = NULL;
   8982         if ((flags & JS_PROP_HAS_SET) && JS_IsFunction(ctx, setter)) {
   8983             pr->u.getset.setter =
   8984                 JS_VALUE_GET_OBJ(JS_DupValue(ctx, setter));
   8985         }
   8986     } else {
   8987         if (flags & JS_PROP_HAS_VALUE) {
   8988             pr->u.value = JS_DupValue(ctx, val);
   8989         } else {
   8990             pr->u.value = JS_UNDEFINED;
   8991         }
   8992     }
   8993     return TRUE;
   8994 }
   8995 
   8996 /* return FALSE if not OK */
   8997 static BOOL check_define_prop_flags(int prop_flags, int flags)
   8998 {
   8999     BOOL has_accessor, is_getset;
   9000 
   9001     if (!(prop_flags & JS_PROP_CONFIGURABLE)) {
   9002         if ((flags & (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) ==
   9003             (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) {
   9004             return FALSE;
   9005         }
   9006         if ((flags & JS_PROP_HAS_ENUMERABLE) &&
   9007             (flags & JS_PROP_ENUMERABLE) != (prop_flags & JS_PROP_ENUMERABLE))
   9008             return FALSE;
   9009     }
   9010     if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE |
   9011                  JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
   9012         if (!(prop_flags & JS_PROP_CONFIGURABLE)) {
   9013             has_accessor = ((flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) != 0);
   9014             is_getset = ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET);
   9015             if (has_accessor != is_getset)
   9016                 return FALSE;
   9017             if (!has_accessor && !is_getset && !(prop_flags & JS_PROP_WRITABLE)) {
   9018                 /* not writable: cannot set the writable bit */
   9019                 if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) ==
   9020                     (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE))
   9021                     return FALSE;
   9022             }
   9023         }
   9024     }
   9025     return TRUE;
   9026 }
   9027 
   9028 /* ensure that the shape can be safely modified */
   9029 static int js_shape_prepare_update(JSContext *ctx, JSObject *p,
   9030                                    JSShapeProperty **pprs)
   9031 {
   9032     JSShape *sh;
   9033     uint32_t idx = 0;    /* prevent warning */
   9034 
   9035     sh = p->shape;
   9036     if (sh->is_hashed) {
   9037         if (sh->header.ref_count != 1) {
   9038             if (pprs)
   9039                 idx = *pprs - get_shape_prop(sh);
   9040             /* clone the shape (the resulting one is no longer hashed) */
   9041             sh = js_clone_shape(ctx, sh);
   9042             if (!sh)
   9043                 return -1;
   9044             js_free_shape(ctx->rt, p->shape);
   9045             p->shape = sh;
   9046             if (pprs)
   9047                 *pprs = get_shape_prop(sh) + idx;
   9048         } else {
   9049             js_shape_hash_unlink(ctx->rt, sh);
   9050             sh->is_hashed = FALSE;
   9051         }
   9052     }
   9053     return 0;
   9054 }
   9055 
   9056 static int js_update_property_flags(JSContext *ctx, JSObject *p,
   9057                                     JSShapeProperty **pprs, int flags)
   9058 {
   9059     if (flags != (*pprs)->flags) {
   9060         if (js_shape_prepare_update(ctx, p, pprs))
   9061             return -1;
   9062         (*pprs)->flags = flags;
   9063     }
   9064     return 0;
   9065 }
   9066 
   9067 /* allowed flags:
   9068    JS_PROP_CONFIGURABLE, JS_PROP_WRITABLE, JS_PROP_ENUMERABLE
   9069    JS_PROP_HAS_GET, JS_PROP_HAS_SET, JS_PROP_HAS_VALUE,
   9070    JS_PROP_HAS_CONFIGURABLE, JS_PROP_HAS_WRITABLE, JS_PROP_HAS_ENUMERABLE,
   9071    JS_PROP_THROW, JS_PROP_NO_EXOTIC.
   9072    If JS_PROP_THROW is set, return an exception instead of FALSE.
   9073    if JS_PROP_NO_EXOTIC is set, do not call the exotic
   9074    define_own_property callback.
   9075    return -1 (exception), FALSE or TRUE.
   9076 */
   9077 int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
   9078                       JSAtom prop, JSValueConst val,
   9079                       JSValueConst getter, JSValueConst setter, int flags)
   9080 {
   9081     JSObject *p;
   9082     JSShapeProperty *prs;
   9083     JSProperty *pr;
   9084     int mask, res;
   9085 
   9086     if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) {
   9087         JS_ThrowTypeErrorNotAnObject(ctx);
   9088         return -1;
   9089     }
   9090     p = JS_VALUE_GET_OBJ(this_obj);
   9091 
   9092  redo_prop_update:
   9093     prs = find_own_property(&pr, p, prop);
   9094     if (prs) {
   9095         /* the range of the Array length property is always tested before */
   9096         if ((prs->flags & JS_PROP_LENGTH) && (flags & JS_PROP_HAS_VALUE)) {
   9097             uint32_t array_length;
   9098             if (JS_ToArrayLengthFree(ctx, &array_length,
   9099                                      JS_DupValue(ctx, val), FALSE)) {
   9100                 return -1;
   9101             }
   9102             /* this code relies on the fact that Uint32 are never allocated */
   9103             val = (JSValueConst)JS_NewUint32(ctx, array_length);
   9104             /* prs may have been modified */
   9105             prs = find_own_property(&pr, p, prop);
   9106             assert(prs != NULL);
   9107         }
   9108         /* property already exists */
   9109         if (!check_define_prop_flags(prs->flags, flags)) {
   9110         not_configurable:
   9111             return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable");
   9112         }
   9113 
   9114         if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
   9115             /* Instantiate property and retry */
   9116             if (JS_AutoInitProperty(ctx, p, prop, pr, prs))
   9117                 return -1;
   9118             goto redo_prop_update;
   9119         }
   9120 
   9121         if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE |
   9122                      JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
   9123             if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
   9124                 JSObject *new_getter, *new_setter;
   9125 
   9126                 if (JS_IsFunction(ctx, getter)) {
   9127                     new_getter = JS_VALUE_GET_OBJ(getter);
   9128                 } else {
   9129                     new_getter = NULL;
   9130                 }
   9131                 if (JS_IsFunction(ctx, setter)) {
   9132                     new_setter = JS_VALUE_GET_OBJ(setter);
   9133                 } else {
   9134                     new_setter = NULL;
   9135                 }
   9136 
   9137                 if ((prs->flags & JS_PROP_TMASK) != JS_PROP_GETSET) {
   9138                     if (js_shape_prepare_update(ctx, p, &prs))
   9139                         return -1;
   9140                     /* convert to getset */
   9141                     if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
   9142                         free_var_ref(ctx->rt, pr->u.var_ref);
   9143                     } else {
   9144                         JS_FreeValue(ctx, pr->u.value);
   9145                     }
   9146                     prs->flags = (prs->flags &
   9147                                   (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) |
   9148                         JS_PROP_GETSET;
   9149                     pr->u.getset.getter = NULL;
   9150                     pr->u.getset.setter = NULL;
   9151                 } else {
   9152                     if (!(prs->flags & JS_PROP_CONFIGURABLE)) {
   9153                         if ((flags & JS_PROP_HAS_GET) &&
   9154                             new_getter != pr->u.getset.getter) {
   9155                             goto not_configurable;
   9156                         }
   9157                         if ((flags & JS_PROP_HAS_SET) &&
   9158                             new_setter != pr->u.getset.setter) {
   9159                             goto not_configurable;
   9160                         }
   9161                     }
   9162                 }
   9163                 if (flags & JS_PROP_HAS_GET) {
   9164                     if (pr->u.getset.getter)
   9165                         JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
   9166                     if (new_getter)
   9167                         JS_DupValue(ctx, getter);
   9168                     pr->u.getset.getter = new_getter;
   9169                 }
   9170                 if (flags & JS_PROP_HAS_SET) {
   9171                     if (pr->u.getset.setter)
   9172                         JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
   9173                     if (new_setter)
   9174                         JS_DupValue(ctx, setter);
   9175                     pr->u.getset.setter = new_setter;
   9176                 }
   9177             } else {
   9178                 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
   9179                     /* convert to data descriptor */
   9180                     if (js_shape_prepare_update(ctx, p, &prs))
   9181                         return -1;
   9182                     if (pr->u.getset.getter)
   9183                         JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
   9184                     if (pr->u.getset.setter)
   9185                         JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
   9186                     prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE);
   9187                     pr->u.value = JS_UNDEFINED;
   9188                 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
   9189                     /* Note: JS_PROP_VARREF is always writable */
   9190                 } else {
   9191                     if ((prs->flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 &&
   9192                         (flags & JS_PROP_HAS_VALUE)) {
   9193                         if (!js_same_value(ctx, val, pr->u.value)) {
   9194                             goto not_configurable;
   9195                         } else {
   9196                             return TRUE;
   9197                         }
   9198                     }
   9199                 }
   9200                 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
   9201                     if (flags & JS_PROP_HAS_VALUE) {
   9202                         if (p->class_id == JS_CLASS_MODULE_NS) {
   9203                             /* JS_PROP_WRITABLE is always true for variable
   9204                                references, but they are write protected in module name
   9205                                spaces. */
   9206                             if (!js_same_value(ctx, val, *pr->u.var_ref->pvalue))
   9207                                 goto not_configurable;
   9208                         } else {
   9209                             /* update the reference */
   9210                             set_value(ctx, pr->u.var_ref->pvalue,
   9211                                       JS_DupValue(ctx, val));
   9212                         }
   9213                     }
   9214                     /* if writable is set to false, no longer a
   9215                        reference (for mapped arguments) */
   9216                     if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) {
   9217                         JSValue val1;
   9218                         if (p->class_id == JS_CLASS_MODULE_NS) {
   9219                             return JS_ThrowTypeErrorOrFalse(ctx, flags, "module namespace properties have writable = false");
   9220                         }
   9221                         if (js_shape_prepare_update(ctx, p, &prs))
   9222                             return -1;
   9223                         val1 = JS_DupValue(ctx, *pr->u.var_ref->pvalue);
   9224                         free_var_ref(ctx->rt, pr->u.var_ref);
   9225                         pr->u.value = val1;
   9226                         prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE);
   9227                     }
   9228                 } else if (prs->flags & JS_PROP_LENGTH) {
   9229                     if (flags & JS_PROP_HAS_VALUE) {
   9230                         /* Note: no JS code is executable because
   9231                            'val' is guaranted to be a Uint32 */
   9232                         res = set_array_length(ctx, p, JS_DupValue(ctx, val),
   9233                                                flags);
   9234                     } else {
   9235                         res = TRUE;
   9236                     }
   9237                     /* still need to reset the writable flag if
   9238                        needed.  The JS_PROP_LENGTH is kept because the
   9239                        Uint32 test is still done if the length
   9240                        property is read-only. */
   9241                     if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) ==
   9242                         JS_PROP_HAS_WRITABLE) {
   9243                         prs = get_shape_prop(p->shape);
   9244                         if (js_update_property_flags(ctx, p, &prs,
   9245                                                      prs->flags & ~JS_PROP_WRITABLE))
   9246                             return -1;
   9247                     }
   9248                     return res;
   9249                 } else {
   9250                     if (flags & JS_PROP_HAS_VALUE) {
   9251                         JS_FreeValue(ctx, pr->u.value);
   9252                         pr->u.value = JS_DupValue(ctx, val);
   9253                     }
   9254                     if (flags & JS_PROP_HAS_WRITABLE) {
   9255                         if (js_update_property_flags(ctx, p, &prs,
   9256                                                      (prs->flags & ~JS_PROP_WRITABLE) |
   9257                                                      (flags & JS_PROP_WRITABLE)))
   9258                             return -1;
   9259                     }
   9260                 }
   9261             }
   9262         }
   9263         mask = 0;
   9264         if (flags & JS_PROP_HAS_CONFIGURABLE)
   9265             mask |= JS_PROP_CONFIGURABLE;
   9266         if (flags & JS_PROP_HAS_ENUMERABLE)
   9267             mask |= JS_PROP_ENUMERABLE;
   9268         if (js_update_property_flags(ctx, p, &prs,
   9269                                      (prs->flags & ~mask) | (flags & mask)))
   9270             return -1;
   9271         return TRUE;
   9272     }
   9273 
   9274     /* handle modification of fast array elements */
   9275     if (p->fast_array) {
   9276         uint32_t idx;
   9277         uint32_t prop_flags;
   9278         if (p->class_id == JS_CLASS_ARRAY) {
   9279             if (__JS_AtomIsTaggedInt(prop)) {
   9280                 idx = __JS_AtomToUInt32(prop);
   9281                 if (idx < p->u.array.count) {
   9282                     prop_flags = get_prop_flags(flags, JS_PROP_C_W_E);
   9283                     if (prop_flags != JS_PROP_C_W_E)
   9284                         goto convert_to_slow_array;
   9285                     if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
   9286                     convert_to_slow_array:
   9287                         if (convert_fast_array_to_array(ctx, p))
   9288                             return -1;
   9289                         else
   9290                             goto redo_prop_update;
   9291                     }
   9292                     if (flags & JS_PROP_HAS_VALUE) {
   9293                         set_value(ctx, &p->u.array.u.values[idx], JS_DupValue(ctx, val));
   9294                     }
   9295                     return TRUE;
   9296                 }
   9297             }
   9298         } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
   9299                    p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
   9300             JSValue num;
   9301             int ret;
   9302 
   9303             if (!__JS_AtomIsTaggedInt(prop)) {
   9304                 /* slow path with to handle all numeric indexes */
   9305                 num = JS_AtomIsNumericIndex1(ctx, prop);
   9306                 if (JS_IsUndefined(num))
   9307                     goto typed_array_done;
   9308                 if (JS_IsException(num))
   9309                     return -1;
   9310                 ret = JS_NumberIsInteger(ctx, num);
   9311                 if (ret < 0) {
   9312                     JS_FreeValue(ctx, num);
   9313                     return -1;
   9314                 }
   9315                 if (!ret) {
   9316                     JS_FreeValue(ctx, num);
   9317                     return JS_ThrowTypeErrorOrFalse(ctx, flags, "non integer index in typed array");
   9318                 }
   9319                 ret = JS_NumberIsNegativeOrMinusZero(ctx, num);
   9320                 JS_FreeValue(ctx, num);
   9321                 if (ret) {
   9322                     return JS_ThrowTypeErrorOrFalse(ctx, flags, "negative index in typed array");
   9323                 }
   9324                 if (!__JS_AtomIsTaggedInt(prop))
   9325                     goto typed_array_oob;
   9326             }
   9327             idx = __JS_AtomToUInt32(prop);
   9328             /* if the typed array is detached, p->u.array.count = 0 */
   9329             if (idx >= p->u.array.count) {
   9330             typed_array_oob:
   9331                 return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound index in typed array");
   9332             }
   9333             prop_flags = get_prop_flags(flags, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
   9334             if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET) ||
   9335                 prop_flags != (JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE)) {
   9336                 return JS_ThrowTypeErrorOrFalse(ctx, flags, "invalid descriptor flags");
   9337             }
   9338             if (flags & JS_PROP_HAS_VALUE) {
   9339                 return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), JS_DupValue(ctx, val), flags);
   9340             }
   9341             return TRUE;
   9342         typed_array_done: ;
   9343         }
   9344     }
   9345 
   9346     return JS_CreateProperty(ctx, p, prop, val, getter, setter, flags);
   9347 }
   9348 
   9349 static int JS_DefineAutoInitProperty(JSContext *ctx, JSValueConst this_obj,
   9350                                      JSAtom prop, JSAutoInitIDEnum id,
   9351                                      void *opaque, int flags)
   9352 {
   9353     JSObject *p;
   9354     JSProperty *pr;
   9355 
   9356     if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT)
   9357         return FALSE;
   9358 
   9359     p = JS_VALUE_GET_OBJ(this_obj);
   9360 
   9361     if (find_own_property(&pr, p, prop)) {
   9362         /* property already exists */
   9363         abort();
   9364         return FALSE;
   9365     }
   9366 
   9367     /* Specialized CreateProperty */
   9368     pr = add_property(ctx, p, prop, (flags & JS_PROP_C_W_E) | JS_PROP_AUTOINIT);
   9369     if (unlikely(!pr))
   9370         return -1;
   9371     pr->u.init.realm_and_id = (uintptr_t)JS_DupContext(ctx);
   9372     assert((pr->u.init.realm_and_id & 3) == 0);
   9373     assert(id <= 3);
   9374     pr->u.init.realm_and_id |= id;
   9375     pr->u.init.opaque = opaque;
   9376     return TRUE;
   9377 }
   9378 
   9379 /* shortcut to add or redefine a new property value */
   9380 int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj,
   9381                            JSAtom prop, JSValue val, int flags)
   9382 {
   9383     int ret;
   9384     ret = JS_DefineProperty(ctx, this_obj, prop, val, JS_UNDEFINED, JS_UNDEFINED,
   9385                             flags | JS_PROP_HAS_VALUE | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE);
   9386     JS_FreeValue(ctx, val);
   9387     return ret;
   9388 }
   9389 
   9390 int JS_DefinePropertyValueValue(JSContext *ctx, JSValueConst this_obj,
   9391                                 JSValue prop, JSValue val, int flags)
   9392 {
   9393     JSAtom atom;
   9394     int ret;
   9395     atom = JS_ValueToAtom(ctx, prop);
   9396     JS_FreeValue(ctx, prop);
   9397     if (unlikely(atom == JS_ATOM_NULL)) {
   9398         JS_FreeValue(ctx, val);
   9399         return -1;
   9400     }
   9401     ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags);
   9402     JS_FreeAtom(ctx, atom);
   9403     return ret;
   9404 }
   9405 
   9406 int JS_DefinePropertyValueUint32(JSContext *ctx, JSValueConst this_obj,
   9407                                  uint32_t idx, JSValue val, int flags)
   9408 {
   9409     return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewUint32(ctx, idx),
   9410                                        val, flags);
   9411 }
   9412 
   9413 int JS_DefinePropertyValueInt64(JSContext *ctx, JSValueConst this_obj,
   9414                                 int64_t idx, JSValue val, int flags)
   9415 {
   9416     return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx),
   9417                                        val, flags);
   9418 }
   9419 
   9420 int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj,
   9421                               const char *prop, JSValue val, int flags)
   9422 {
   9423     JSAtom atom;
   9424     int ret;
   9425     atom = JS_NewAtom(ctx, prop);
   9426     ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags);
   9427     JS_FreeAtom(ctx, atom);
   9428     return ret;
   9429 }
   9430 
   9431 /* shortcut to add getter & setter */
   9432 int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj,
   9433                             JSAtom prop, JSValue getter, JSValue setter,
   9434                             int flags)
   9435 {
   9436     int ret;
   9437     ret = JS_DefineProperty(ctx, this_obj, prop, JS_UNDEFINED, getter, setter,
   9438                             flags | JS_PROP_HAS_GET | JS_PROP_HAS_SET |
   9439                             JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE);
   9440     JS_FreeValue(ctx, getter);
   9441     JS_FreeValue(ctx, setter);
   9442     return ret;
   9443 }
   9444 
   9445 static int JS_CreateDataPropertyUint32(JSContext *ctx, JSValueConst this_obj,
   9446                                        int64_t idx, JSValue val, int flags)
   9447 {
   9448     return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx),
   9449                                        val, flags | JS_PROP_CONFIGURABLE |
   9450                                        JS_PROP_ENUMERABLE | JS_PROP_WRITABLE);
   9451 }
   9452 
   9453 
   9454 /* return TRUE if 'obj' has a non empty 'name' string */
   9455 static BOOL js_object_has_name(JSContext *ctx, JSValueConst obj)
   9456 {
   9457     JSProperty *pr;
   9458     JSShapeProperty *prs;
   9459     JSValueConst val;
   9460     JSString *p;
   9461 
   9462     prs = find_own_property(&pr, JS_VALUE_GET_OBJ(obj), JS_ATOM_name);
   9463     if (!prs)
   9464         return FALSE;
   9465     if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL)
   9466         return TRUE;
   9467     val = pr->u.value;
   9468     if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)
   9469         return TRUE;
   9470     p = JS_VALUE_GET_STRING(val);
   9471     return (p->len != 0);
   9472 }
   9473 
   9474 static int JS_DefineObjectName(JSContext *ctx, JSValueConst obj,
   9475                                JSAtom name, int flags)
   9476 {
   9477     if (name != JS_ATOM_NULL
   9478     &&  JS_IsObject(obj)
   9479     &&  !js_object_has_name(ctx, obj)
   9480     &&  JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, JS_AtomToString(ctx, name), flags) < 0) {
   9481         return -1;
   9482     }
   9483     return 0;
   9484 }
   9485 
   9486 static int JS_DefineObjectNameComputed(JSContext *ctx, JSValueConst obj,
   9487                                        JSValueConst str, int flags)
   9488 {
   9489     if (JS_IsObject(obj) &&
   9490         !js_object_has_name(ctx, obj)) {
   9491         JSAtom prop;
   9492         JSValue name_str;
   9493         prop = JS_ValueToAtom(ctx, str);
   9494         if (prop == JS_ATOM_NULL)
   9495             return -1;
   9496         name_str = js_get_function_name(ctx, prop);
   9497         JS_FreeAtom(ctx, prop);
   9498         if (JS_IsException(name_str))
   9499             return -1;
   9500         if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, name_str, flags) < 0)
   9501             return -1;
   9502     }
   9503     return 0;
   9504 }
   9505 
   9506 #define DEFINE_GLOBAL_LEX_VAR (1 << 7)
   9507 #define DEFINE_GLOBAL_FUNC_VAR (1 << 6)
   9508 
   9509 static JSValue JS_ThrowSyntaxErrorVarRedeclaration(JSContext *ctx, JSAtom prop)
   9510 {
   9511     return JS_ThrowSyntaxErrorAtom(ctx, "redeclaration of '%s'", prop);
   9512 }
   9513 
   9514 /* flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */
   9515 /* XXX: could support exotic global object. */
   9516 static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags)
   9517 {
   9518     JSObject *p;
   9519     JSShapeProperty *prs;
   9520 
   9521     p = JS_VALUE_GET_OBJ(ctx->global_obj);
   9522     prs = find_own_property1(p, prop);
   9523     /* XXX: should handle JS_PROP_AUTOINIT */
   9524     if (flags & DEFINE_GLOBAL_LEX_VAR) {
   9525         if (prs && !(prs->flags & JS_PROP_CONFIGURABLE))
   9526             goto fail_redeclaration;
   9527     } else {
   9528         if (!prs && !p->extensible)
   9529             goto define_error;
   9530         if (flags & DEFINE_GLOBAL_FUNC_VAR) {
   9531             if (prs) {
   9532                 if (!(prs->flags & JS_PROP_CONFIGURABLE) &&
   9533                     ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET ||
   9534                      ((prs->flags & (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)) !=
   9535                       (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)))) {
   9536                 define_error:
   9537                     JS_ThrowTypeErrorAtom(ctx, "cannot define variable '%s'",
   9538                                           prop);
   9539                     return -1;
   9540                 }
   9541             }
   9542         }
   9543     }
   9544     /* check if there already is a lexical declaration */
   9545     p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
   9546     prs = find_own_property1(p, prop);
   9547     if (prs) {
   9548     fail_redeclaration:
   9549         JS_ThrowSyntaxErrorVarRedeclaration(ctx, prop);
   9550         return -1;
   9551     }
   9552     return 0;
   9553 }
   9554 
   9555 /* def_flags is (0, DEFINE_GLOBAL_LEX_VAR) |
   9556    JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE */
   9557 /* XXX: could support exotic global object. */
   9558 static int JS_DefineGlobalVar(JSContext *ctx, JSAtom prop, int def_flags)
   9559 {
   9560     JSObject *p;
   9561     JSShapeProperty *prs;
   9562     JSProperty *pr;
   9563     JSValue val;
   9564     int flags;
   9565 
   9566     if (def_flags & DEFINE_GLOBAL_LEX_VAR) {
   9567         p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
   9568         flags = JS_PROP_ENUMERABLE | (def_flags & JS_PROP_WRITABLE) |
   9569             JS_PROP_CONFIGURABLE;
   9570         val = JS_UNINITIALIZED;
   9571     } else {
   9572         p = JS_VALUE_GET_OBJ(ctx->global_obj);
   9573         flags = JS_PROP_ENUMERABLE | JS_PROP_WRITABLE |
   9574             (def_flags & JS_PROP_CONFIGURABLE);
   9575         val = JS_UNDEFINED;
   9576     }
   9577     prs = find_own_property1(p, prop);
   9578     if (prs)
   9579         return 0;
   9580     if (!p->extensible)
   9581         return 0;
   9582     pr = add_property(ctx, p, prop, flags);
   9583     if (unlikely(!pr))
   9584         return -1;
   9585     pr->u.value = val;
   9586     return 0;
   9587 }
   9588 
   9589 /* 'def_flags' is 0 or JS_PROP_CONFIGURABLE. */
   9590 /* XXX: could support exotic global object. */
   9591 static int JS_DefineGlobalFunction(JSContext *ctx, JSAtom prop,
   9592                                    JSValueConst func, int def_flags)
   9593 {
   9594 
   9595     JSObject *p;
   9596     JSShapeProperty *prs;
   9597     int flags;
   9598 
   9599     p = JS_VALUE_GET_OBJ(ctx->global_obj);
   9600     prs = find_own_property1(p, prop);
   9601     flags = JS_PROP_HAS_VALUE | JS_PROP_THROW;
   9602     if (!prs || (prs->flags & JS_PROP_CONFIGURABLE)) {
   9603         flags |= JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | def_flags |
   9604             JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE;
   9605     }
   9606     if (JS_DefineProperty(ctx, ctx->global_obj, prop, func,
   9607                           JS_UNDEFINED, JS_UNDEFINED, flags) < 0)
   9608         return -1;
   9609     return 0;
   9610 }
   9611 
   9612 static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop,
   9613                                BOOL throw_ref_error)
   9614 {
   9615     JSObject *p;
   9616     JSShapeProperty *prs;
   9617     JSProperty *pr;
   9618 
   9619     /* no exotic behavior is possible in global_var_obj */
   9620     p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
   9621     prs = find_own_property(&pr, p, prop);
   9622     if (prs) {
   9623         /* XXX: should handle JS_PROP_TMASK properties */
   9624         if (unlikely(JS_IsUninitialized(pr->u.value)))
   9625             return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
   9626         return JS_DupValue(ctx, pr->u.value);
   9627     }
   9628     return JS_GetPropertyInternal(ctx, ctx->global_obj, prop,
   9629                                  ctx->global_obj, throw_ref_error);
   9630 }
   9631 
   9632 /* construct a reference to a global variable */
   9633 static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom prop, JSValue *sp)
   9634 {
   9635     JSObject *p;
   9636     JSShapeProperty *prs;
   9637     JSProperty *pr;
   9638 
   9639     /* no exotic behavior is possible in global_var_obj */
   9640     p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
   9641     prs = find_own_property(&pr, p, prop);
   9642     if (prs) {
   9643         /* XXX: should handle JS_PROP_AUTOINIT properties? */
   9644         /* XXX: conformance: do these tests in
   9645            OP_put_var_ref/OP_get_var_ref ? */
   9646         if (unlikely(JS_IsUninitialized(pr->u.value))) {
   9647             JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
   9648             return -1;
   9649         }
   9650         if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) {
   9651             return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop);
   9652         }
   9653         sp[0] = JS_DupValue(ctx, ctx->global_var_obj);
   9654     } else {
   9655         int ret;
   9656         ret = JS_HasProperty(ctx, ctx->global_obj, prop);
   9657         if (ret < 0)
   9658             return -1;
   9659         if (ret) {
   9660             sp[0] = JS_DupValue(ctx, ctx->global_obj);
   9661         } else {
   9662             sp[0] = JS_UNDEFINED;
   9663         }
   9664     }
   9665     sp[1] = JS_AtomToValue(ctx, prop);
   9666     return 0;
   9667 }
   9668 
   9669 /* use for strict variable access: test if the variable exists */
   9670 static int JS_CheckGlobalVar(JSContext *ctx, JSAtom prop)
   9671 {
   9672     JSObject *p;
   9673     JSShapeProperty *prs;
   9674     int ret;
   9675 
   9676     /* no exotic behavior is possible in global_var_obj */
   9677     p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
   9678     prs = find_own_property1(p, prop);
   9679     if (prs) {
   9680         ret = TRUE;
   9681     } else {
   9682         ret = JS_HasProperty(ctx, ctx->global_obj, prop);
   9683         if (ret < 0)
   9684             return -1;
   9685     }
   9686     return ret;
   9687 }
   9688 
   9689 /* flag = 0: normal variable write
   9690    flag = 1: initialize lexical variable
   9691    flag = 2: normal variable write, strict check was done before
   9692 */
   9693 static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val,
   9694                            int flag)
   9695 {
   9696     JSObject *p;
   9697     JSShapeProperty *prs;
   9698     JSProperty *pr;
   9699     int flags;
   9700 
   9701     /* no exotic behavior is possible in global_var_obj */
   9702     p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
   9703     prs = find_own_property(&pr, p, prop);
   9704     if (prs) {
   9705         /* XXX: should handle JS_PROP_AUTOINIT properties? */
   9706         if (flag != 1) {
   9707             if (unlikely(JS_IsUninitialized(pr->u.value))) {
   9708                 JS_FreeValue(ctx, val);
   9709                 JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
   9710                 return -1;
   9711             }
   9712             if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) {
   9713                 JS_FreeValue(ctx, val);
   9714                 return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop);
   9715             }
   9716         }
   9717         set_value(ctx, &pr->u.value, val);
   9718         return 0;
   9719     }
   9720     flags = JS_PROP_THROW_STRICT;
   9721     if (is_strict_mode(ctx))
   9722         flags |= JS_PROP_NO_ADD;
   9723     return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, ctx->global_obj, flags);
   9724 }
   9725 
   9726 /* return -1, FALSE or TRUE. return FALSE if not configurable or
   9727    invalid object. return -1 in case of exception.
   9728    flags can be 0, JS_PROP_THROW or JS_PROP_THROW_STRICT */
   9729 int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags)
   9730 {
   9731     JSValue obj1;
   9732     JSObject *p;
   9733     int res;
   9734 
   9735     obj1 = JS_ToObject(ctx, obj);
   9736     if (JS_IsException(obj1))
   9737         return -1;
   9738     p = JS_VALUE_GET_OBJ(obj1);
   9739     res = delete_property(ctx, p, prop);
   9740     JS_FreeValue(ctx, obj1);
   9741     if (res != FALSE)
   9742         return res;
   9743     if ((flags & JS_PROP_THROW) ||
   9744         ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
   9745         JS_ThrowTypeError(ctx, "could not delete property");
   9746         return -1;
   9747     }
   9748     return FALSE;
   9749 }
   9750 
   9751 int JS_DeletePropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, int flags)
   9752 {
   9753     JSAtom prop;
   9754     int res;
   9755 
   9756     if ((uint64_t)idx <= JS_ATOM_MAX_INT) {
   9757         /* fast path for fast arrays */
   9758         return JS_DeleteProperty(ctx, obj, __JS_AtomFromUInt32(idx), flags);
   9759     }
   9760     prop = JS_NewAtomInt64(ctx, idx);
   9761     if (prop == JS_ATOM_NULL)
   9762         return -1;
   9763     res = JS_DeleteProperty(ctx, obj, prop, flags);
   9764     JS_FreeAtom(ctx, prop);
   9765     return res;
   9766 }
   9767 
   9768 BOOL JS_IsFunction(JSContext *ctx, JSValueConst val)
   9769 {
   9770     JSObject *p;
   9771     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
   9772         return FALSE;
   9773     p = JS_VALUE_GET_OBJ(val);
   9774     switch(p->class_id) {
   9775     case JS_CLASS_BYTECODE_FUNCTION:
   9776         return TRUE;
   9777     case JS_CLASS_PROXY:
   9778         return p->u.proxy_data->is_func;
   9779     default:
   9780         return (ctx->rt->class_array[p->class_id].call != NULL);
   9781     }
   9782 }
   9783 
   9784 BOOL JS_IsCFunction(JSContext *ctx, JSValueConst val, JSCFunction *func, int magic)
   9785 {
   9786     JSObject *p;
   9787     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
   9788         return FALSE;
   9789     p = JS_VALUE_GET_OBJ(val);
   9790     if (p->class_id == JS_CLASS_C_FUNCTION)
   9791         return (p->u.cfunc.c_function.generic == func && p->u.cfunc.magic == magic);
   9792     else
   9793         return FALSE;
   9794 }
   9795 
   9796 BOOL JS_IsConstructor(JSContext *ctx, JSValueConst val)
   9797 {
   9798     JSObject *p;
   9799     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
   9800         return FALSE;
   9801     p = JS_VALUE_GET_OBJ(val);
   9802     return p->is_constructor;
   9803 }
   9804 
   9805 BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, BOOL val)
   9806 {
   9807     JSObject *p;
   9808     if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)
   9809         return FALSE;
   9810     p = JS_VALUE_GET_OBJ(func_obj);
   9811     p->is_constructor = val;
   9812     return TRUE;
   9813 }
   9814 
   9815 BOOL JS_IsError(JSContext *ctx, JSValueConst val)
   9816 {
   9817     JSObject *p;
   9818     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
   9819         return FALSE;
   9820     p = JS_VALUE_GET_OBJ(val);
   9821     return (p->class_id == JS_CLASS_ERROR);
   9822 }
   9823 
   9824 /* used to avoid catching interrupt exceptions */
   9825 BOOL JS_IsUncatchableError(JSContext *ctx, JSValueConst val)
   9826 {
   9827     JSObject *p;
   9828     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
   9829         return FALSE;
   9830     p = JS_VALUE_GET_OBJ(val);
   9831     return p->class_id == JS_CLASS_ERROR && p->is_uncatchable_error;
   9832 }
   9833 
   9834 void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag)
   9835 {
   9836     JSObject *p;
   9837     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
   9838         return;
   9839     p = JS_VALUE_GET_OBJ(val);
   9840     if (p->class_id == JS_CLASS_ERROR)
   9841         p->is_uncatchable_error = flag;
   9842 }
   9843 
   9844 void JS_ResetUncatchableError(JSContext *ctx)
   9845 {
   9846     JS_SetUncatchableError(ctx, ctx->rt->current_exception, FALSE);
   9847 }
   9848 
   9849 void JS_SetOpaque(JSValue obj, void *opaque)
   9850 {
   9851    JSObject *p;
   9852     if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
   9853         p = JS_VALUE_GET_OBJ(obj);
   9854         p->u.opaque = opaque;
   9855     }
   9856 }
   9857 
   9858 /* return NULL if not an object of class class_id */
   9859 void *JS_GetOpaque(JSValueConst obj, JSClassID class_id)
   9860 {
   9861     JSObject *p;
   9862     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
   9863         return NULL;
   9864     p = JS_VALUE_GET_OBJ(obj);
   9865     if (p->class_id != class_id)
   9866         return NULL;
   9867     return p->u.opaque;
   9868 }
   9869 
   9870 void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id)
   9871 {
   9872     void *p = JS_GetOpaque(obj, class_id);
   9873     if (unlikely(!p)) {
   9874         JS_ThrowTypeErrorInvalidClass(ctx, class_id);
   9875     }
   9876     return p;
   9877 }
   9878 
   9879 static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint)
   9880 {
   9881     int i;
   9882     BOOL force_ordinary;
   9883 
   9884     JSAtom method_name;
   9885     JSValue method, ret;
   9886     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
   9887         return val;
   9888     force_ordinary = hint & HINT_FORCE_ORDINARY;
   9889     hint &= ~HINT_FORCE_ORDINARY;
   9890     if (!force_ordinary) {
   9891         method = JS_GetProperty(ctx, val, JS_ATOM_Symbol_toPrimitive);
   9892         if (JS_IsException(method))
   9893             goto exception;
   9894         /* ECMA says *If exoticToPrim is not undefined* but tests in
   9895            test262 use null as a non callable converter */
   9896         if (!JS_IsUndefined(method) && !JS_IsNull(method)) {
   9897             JSAtom atom;
   9898             JSValue arg;
   9899             switch(hint) {
   9900             case HINT_STRING:
   9901                 atom = JS_ATOM_string;
   9902                 break;
   9903             case HINT_NUMBER:
   9904                 atom = JS_ATOM_number;
   9905                 break;
   9906             default:
   9907             case HINT_NONE:
   9908                 atom = JS_ATOM_default;
   9909                 break;
   9910             }
   9911             arg = JS_AtomToString(ctx, atom);
   9912             ret = JS_CallFree(ctx, method, val, 1, (JSValueConst *)&arg);
   9913             JS_FreeValue(ctx, arg);
   9914             if (JS_IsException(ret))
   9915                 goto exception;
   9916             JS_FreeValue(ctx, val);
   9917             if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT)
   9918                 return ret;
   9919             JS_FreeValue(ctx, ret);
   9920             return JS_ThrowTypeError(ctx, "toPrimitive");
   9921         }
   9922     }
   9923     if (hint != HINT_STRING)
   9924         hint = HINT_NUMBER;
   9925     for(i = 0; i < 2; i++) {
   9926         if ((i ^ hint) == 0) {
   9927             method_name = JS_ATOM_toString;
   9928         } else {
   9929             method_name = JS_ATOM_valueOf;
   9930         }
   9931         method = JS_GetProperty(ctx, val, method_name);
   9932         if (JS_IsException(method))
   9933             goto exception;
   9934         if (JS_IsFunction(ctx, method)) {
   9935             ret = JS_CallFree(ctx, method, val, 0, NULL);
   9936             if (JS_IsException(ret))
   9937                 goto exception;
   9938             if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {
   9939                 JS_FreeValue(ctx, val);
   9940                 return ret;
   9941             }
   9942             JS_FreeValue(ctx, ret);
   9943         } else {
   9944             JS_FreeValue(ctx, method);
   9945         }
   9946     }
   9947     JS_ThrowTypeError(ctx, "toPrimitive");
   9948 exception:
   9949     JS_FreeValue(ctx, val);
   9950     return JS_EXCEPTION;
   9951 }
   9952 
   9953 static JSValue JS_ToPrimitive(JSContext *ctx, JSValueConst val, int hint)
   9954 {
   9955     return JS_ToPrimitiveFree(ctx, JS_DupValue(ctx, val), hint);
   9956 }
   9957 
   9958 void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj)
   9959 {
   9960     JSObject *p;
   9961     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
   9962         return;
   9963     p = JS_VALUE_GET_OBJ(obj);
   9964     p->is_HTMLDDA = TRUE;
   9965 }
   9966 
   9967 static inline BOOL JS_IsHTMLDDA(JSContext *ctx, JSValueConst obj)
   9968 {
   9969     JSObject *p;
   9970     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
   9971         return FALSE;
   9972     p = JS_VALUE_GET_OBJ(obj);
   9973     return p->is_HTMLDDA;
   9974 }
   9975 
   9976 static int JS_ToBoolFree(JSContext *ctx, JSValue val)
   9977 {
   9978     uint32_t tag = JS_VALUE_GET_TAG(val);
   9979     switch(tag) {
   9980     case JS_TAG_INT:
   9981         return JS_VALUE_GET_INT(val) != 0;
   9982     case JS_TAG_BOOL:
   9983     case JS_TAG_NULL:
   9984     case JS_TAG_UNDEFINED:
   9985         return JS_VALUE_GET_INT(val);
   9986     case JS_TAG_EXCEPTION:
   9987         return -1;
   9988     case JS_TAG_STRING:
   9989         {
   9990             BOOL ret = JS_VALUE_GET_STRING(val)->len != 0;
   9991             JS_FreeValue(ctx, val);
   9992             return ret;
   9993         }
   9994     case JS_TAG_BIG_INT:
   9995 #ifdef CONFIG_BIGNUM
   9996     case JS_TAG_BIG_FLOAT:
   9997 #endif
   9998         {
   9999             JSBigFloat *p = JS_VALUE_GET_PTR(val);
  10000             BOOL ret;
  10001             ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN;
  10002             JS_FreeValue(ctx, val);
  10003             return ret;
  10004         }
  10005 #ifdef CONFIG_BIGNUM
  10006     case JS_TAG_BIG_DECIMAL:
  10007         {
  10008             JSBigDecimal *p = JS_VALUE_GET_PTR(val);
  10009             BOOL ret;
  10010             ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN;
  10011             JS_FreeValue(ctx, val);
  10012             return ret;
  10013         }
  10014 #endif
  10015     case JS_TAG_OBJECT:
  10016         {
  10017             JSObject *p = JS_VALUE_GET_OBJ(val);
  10018             BOOL ret;
  10019             ret = !p->is_HTMLDDA;
  10020             JS_FreeValue(ctx, val);
  10021             return ret;
  10022         }
  10023         break;
  10024     default:
  10025         if (JS_TAG_IS_FLOAT64(tag)) {
  10026             double d = JS_VALUE_GET_FLOAT64(val);
  10027             return !isnan(d) && d != 0;
  10028         } else {
  10029             JS_FreeValue(ctx, val);
  10030             return TRUE;
  10031         }
  10032     }
  10033 }
  10034 
  10035 int JS_ToBool(JSContext *ctx, JSValueConst val)
  10036 {
  10037     return JS_ToBoolFree(ctx, JS_DupValue(ctx, val));
  10038 }
  10039 
  10040 static int skip_spaces(const char *pc)
  10041 {
  10042     const uint8_t *p, *p_next, *p_start;
  10043     uint32_t c;
  10044 
  10045     p = p_start = (const uint8_t *)pc;
  10046     for (;;) {
  10047         c = *p;
  10048         if (c < 128) {
  10049             if (!((c >= 0x09 && c <= 0x0d) || (c == 0x20)))
  10050                 break;
  10051             p++;
  10052         } else {
  10053             c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);
  10054             if (!lre_is_space(c))
  10055                 break;
  10056             p = p_next;
  10057         }
  10058     }
  10059     return p - p_start;
  10060 }
  10061 
  10062 static inline int to_digit(int c)
  10063 {
  10064     if (c >= '0' && c <= '9')
  10065         return c - '0';
  10066     else if (c >= 'A' && c <= 'Z')
  10067         return c - 'A' + 10;
  10068     else if (c >= 'a' && c <= 'z')
  10069         return c - 'a' + 10;
  10070     else
  10071         return 36;
  10072 }
  10073 
  10074 /* XXX: remove */
  10075 static double js_strtod(const char *str, int radix, BOOL is_float)
  10076 {
  10077     double d;
  10078     int c;
  10079 
  10080     if (!is_float || radix != 10) {
  10081         const char *p = str;
  10082         uint64_t n_max, n;
  10083         int int_exp, is_neg;
  10084 
  10085         is_neg = 0;
  10086         if (*p == '-') {
  10087             is_neg = 1;
  10088             p++;
  10089         }
  10090 
  10091         /* skip leading zeros */
  10092         while (*p == '0')
  10093             p++;
  10094         n = 0;
  10095         if (radix == 10)
  10096             n_max = ((uint64_t)-1 - 9) / 10; /* most common case */
  10097         else
  10098             n_max = ((uint64_t)-1 - (radix - 1)) / radix;
  10099         /* XXX: could be more precise */
  10100         int_exp = 0;
  10101         while (*p != '\0') {
  10102             c = to_digit((uint8_t)*p);
  10103             if (c >= radix)
  10104                 break;
  10105             if (n <= n_max) {
  10106                 n = n * radix + c;
  10107             } else {
  10108                 if (radix == 10)
  10109                     goto strtod_case;
  10110                 int_exp++;
  10111             }
  10112             p++;
  10113         }
  10114         d = n;
  10115         if (int_exp != 0) {
  10116             d *= pow(radix, int_exp);
  10117         }
  10118         if (is_neg)
  10119             d = -d;
  10120     } else {
  10121     strtod_case:
  10122         d = strtod(str, NULL);
  10123     }
  10124     return d;
  10125 }
  10126 
  10127 #define ATOD_INT_ONLY        (1 << 0)
  10128 /* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */
  10129 #define ATOD_ACCEPT_BIN_OCT  (1 << 2)
  10130 /* accept O prefix as octal if radix == 0 and properly formed (Annex B) */
  10131 #define ATOD_ACCEPT_LEGACY_OCTAL  (1 << 4)
  10132 /* accept _ between digits as a digit separator */
  10133 #define ATOD_ACCEPT_UNDERSCORES  (1 << 5)
  10134 /* allow a suffix to override the type */
  10135 #define ATOD_ACCEPT_SUFFIX    (1 << 6)
  10136 /* default type */
  10137 #define ATOD_TYPE_MASK        (3 << 7)
  10138 #define ATOD_TYPE_FLOAT64     (0 << 7)
  10139 #define ATOD_TYPE_BIG_INT     (1 << 7)
  10140 #ifdef CONFIG_BIGNUM
  10141 #define ATOD_TYPE_BIG_FLOAT   (2 << 7)
  10142 #define ATOD_TYPE_BIG_DECIMAL (3 << 7)
  10143 /* assume bigint mode: floats are parsed as integers if no decimal
  10144    point nor exponent */
  10145 #define ATOD_MODE_BIGINT      (1 << 9)
  10146 #endif
  10147 /* accept -0x1 */
  10148 #define ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 10)
  10149 
  10150 static JSValue js_string_to_bigint(JSContext *ctx, const char *buf,
  10151                                    int radix, int flags, slimb_t *pexponent)
  10152 {
  10153     bf_t a_s, *a = &a_s;
  10154     int ret;
  10155     JSValue val;
  10156     val = JS_NewBigInt(ctx);
  10157     if (JS_IsException(val))
  10158         return val;
  10159     a = JS_GetBigInt(val);
  10160     ret = bf_atof(a, buf, NULL, radix, BF_PREC_INF, BF_RNDZ);
  10161     if (ret & BF_ST_MEM_ERROR) {
  10162         JS_FreeValue(ctx, val);
  10163         return JS_ThrowOutOfMemory(ctx);
  10164     }
  10165 #ifdef CONFIG_BIGNUM
  10166     val = JS_CompactBigInt1(ctx, val, (flags & ATOD_MODE_BIGINT) != 0);
  10167 #else
  10168     val = JS_CompactBigInt1(ctx, val, FALSE);
  10169 #endif
  10170     return val;
  10171 }
  10172 
  10173 #ifdef CONFIG_BIGNUM
  10174 static JSValue js_string_to_bigfloat(JSContext *ctx, const char *buf,
  10175                                      int radix, int flags, slimb_t *pexponent)
  10176 {
  10177     bf_t *a;
  10178     int ret;
  10179     JSValue val;
  10180 
  10181     val = JS_NewBigFloat(ctx);
  10182     if (JS_IsException(val))
  10183         return val;
  10184     a = JS_GetBigFloat(val);
  10185     if (flags & ATOD_ACCEPT_SUFFIX) {
  10186         /* return the exponent to get infinite precision */
  10187         ret = bf_atof2(a, pexponent, buf, NULL, radix, BF_PREC_INF,
  10188                        BF_RNDZ | BF_ATOF_EXPONENT);
  10189     } else {
  10190         ret = bf_atof(a, buf, NULL, radix, ctx->fp_env.prec,
  10191                       ctx->fp_env.flags);
  10192     }
  10193     if (ret & BF_ST_MEM_ERROR) {
  10194         JS_FreeValue(ctx, val);
  10195         return JS_ThrowOutOfMemory(ctx);
  10196     }
  10197     return val;
  10198 }
  10199 
  10200 static JSValue js_string_to_bigdecimal(JSContext *ctx, const char *buf,
  10201                                        int radix, int flags, slimb_t *pexponent)
  10202 {
  10203     bfdec_t *a;
  10204     int ret;
  10205     JSValue val;
  10206 
  10207     val = JS_NewBigDecimal(ctx);
  10208     if (JS_IsException(val))
  10209         return val;
  10210     a = JS_GetBigDecimal(val);
  10211     ret = bfdec_atof(a, buf, NULL, BF_PREC_INF,
  10212                      BF_RNDZ | BF_ATOF_NO_NAN_INF);
  10213     if (ret & BF_ST_MEM_ERROR) {
  10214         JS_FreeValue(ctx, val);
  10215         return JS_ThrowOutOfMemory(ctx);
  10216     }
  10217     return val;
  10218 }
  10219 #endif
  10220 
  10221 /* return an exception in case of memory error. Return JS_NAN if
  10222    invalid syntax */
  10223 #ifdef CONFIG_BIGNUM
  10224 static JSValue js_atof2(JSContext *ctx, const char *str, const char **pp,
  10225                         int radix, int flags, slimb_t *pexponent)
  10226 #else
  10227 static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
  10228                        int radix, int flags)
  10229 #endif
  10230 {
  10231     const char *p, *p_start;
  10232     int sep, is_neg;
  10233     BOOL is_float, has_legacy_octal;
  10234     int atod_type = flags & ATOD_TYPE_MASK;
  10235     char buf1[64], *buf;
  10236     int i, j, len;
  10237     BOOL buf_allocated = FALSE;
  10238     JSValue val;
  10239 
  10240     /* optional separator between digits */
  10241     sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256;
  10242     has_legacy_octal = FALSE;
  10243 
  10244     p = str;
  10245     p_start = p;
  10246     is_neg = 0;
  10247     if (p[0] == '+') {
  10248         p++;
  10249         p_start++;
  10250         if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))
  10251             goto no_radix_prefix;
  10252     } else if (p[0] == '-') {
  10253         p++;
  10254         p_start++;
  10255         is_neg = 1;
  10256         if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))
  10257             goto no_radix_prefix;
  10258     }
  10259     if (p[0] == '0') {
  10260         if ((p[1] == 'x' || p[1] == 'X') &&
  10261             (radix == 0 || radix == 16)) {
  10262             p += 2;
  10263             radix = 16;
  10264         } else if ((p[1] == 'o' || p[1] == 'O') &&
  10265                    radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {
  10266             p += 2;
  10267             radix = 8;
  10268         } else if ((p[1] == 'b' || p[1] == 'B') &&
  10269                    radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {
  10270             p += 2;
  10271             radix = 2;
  10272         } else if ((p[1] >= '0' && p[1] <= '9') &&
  10273                    radix == 0 && (flags & ATOD_ACCEPT_LEGACY_OCTAL)) {
  10274             int i;
  10275             has_legacy_octal = TRUE;
  10276             sep = 256;
  10277             for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++)
  10278                 continue;
  10279             if (p[i] == '8' || p[i] == '9')
  10280                 goto no_prefix;
  10281             p += 1;
  10282             radix = 8;
  10283         } else {
  10284             goto no_prefix;
  10285         }
  10286         /* there must be a digit after the prefix */
  10287         if (to_digit((uint8_t)*p) >= radix)
  10288             goto fail;
  10289     no_prefix: ;
  10290     } else {
  10291  no_radix_prefix:
  10292         if (!(flags & ATOD_INT_ONLY) &&
  10293             (atod_type == ATOD_TYPE_FLOAT64
  10294 #ifdef CONFIG_BIGNUM
  10295              || atod_type == ATOD_TYPE_BIG_FLOAT
  10296 #endif
  10297              ) &&
  10298             strstart(p, "Infinity", &p)) {
  10299 #ifdef CONFIG_BIGNUM
  10300             if (atod_type == ATOD_TYPE_BIG_FLOAT) {
  10301                 bf_t *a;
  10302                 val = JS_NewBigFloat(ctx);
  10303                 if (JS_IsException(val))
  10304                     goto done;
  10305                 a = JS_GetBigFloat(val);
  10306                 bf_set_inf(a, is_neg);
  10307             } else
  10308 #endif
  10309             {
  10310                 double d = 1.0 / 0.0;
  10311                 if (is_neg)
  10312                     d = -d;
  10313                 val = JS_NewFloat64(ctx, d);
  10314             }
  10315             goto done;
  10316         }
  10317     }
  10318     if (radix == 0)
  10319         radix = 10;
  10320     is_float = FALSE;
  10321     p_start = p;
  10322     while (to_digit((uint8_t)*p) < radix
  10323            ||  (*p == sep && (radix != 10 ||
  10324                               p != p_start + 1 || p[-1] != '0') &&
  10325                 to_digit((uint8_t)p[1]) < radix)) {
  10326         p++;
  10327     }
  10328     if (!(flags & ATOD_INT_ONLY)) {
  10329         if (*p == '.' && (p > p_start || to_digit((uint8_t)p[1]) < radix)) {
  10330             is_float = TRUE;
  10331             p++;
  10332             if (*p == sep)
  10333                 goto fail;
  10334             while (to_digit((uint8_t)*p) < radix ||
  10335                    (*p == sep && to_digit((uint8_t)p[1]) < radix))
  10336                 p++;
  10337         }
  10338         if (p > p_start &&
  10339             (((*p == 'e' || *p == 'E') && radix == 10) ||
  10340              ((*p == 'p' || *p == 'P') && (radix == 2 || radix == 8 || radix == 16)))) {
  10341             const char *p1 = p + 1;
  10342             is_float = TRUE;
  10343             if (*p1 == '+') {
  10344                 p1++;
  10345             } else if (*p1 == '-') {
  10346                 p1++;
  10347             }
  10348             if (is_digit((uint8_t)*p1)) {
  10349                 p = p1 + 1;
  10350                 while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1])))
  10351                     p++;
  10352             }
  10353         }
  10354     }
  10355     if (p == p_start)
  10356         goto fail;
  10357 
  10358     buf = buf1;
  10359     buf_allocated = FALSE;
  10360     len = p - p_start;
  10361     if (unlikely((len + 2) > sizeof(buf1))) {
  10362         buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */
  10363         if (!buf)
  10364             goto mem_error;
  10365         buf_allocated = TRUE;
  10366     }
  10367     /* remove the separators and the radix prefixes */
  10368     j = 0;
  10369     if (is_neg)
  10370         buf[j++] = '-';
  10371     for (i = 0; i < len; i++) {
  10372         if (p_start[i] != '_')
  10373             buf[j++] = p_start[i];
  10374     }
  10375     buf[j] = '\0';
  10376 
  10377     if (flags & ATOD_ACCEPT_SUFFIX) {
  10378         if (*p == 'n') {
  10379             p++;
  10380             atod_type = ATOD_TYPE_BIG_INT;
  10381         } else
  10382 #ifdef CONFIG_BIGNUM
  10383         if (*p == 'l') {
  10384             p++;
  10385             atod_type = ATOD_TYPE_BIG_FLOAT;
  10386         } else if (*p == 'm') {
  10387             p++;
  10388             atod_type = ATOD_TYPE_BIG_DECIMAL;
  10389         } else if (flags & ATOD_MODE_BIGINT) {
  10390             if (!is_float)
  10391                 atod_type = ATOD_TYPE_BIG_INT;
  10392             if (has_legacy_octal)
  10393                 goto fail;
  10394         } else
  10395 #endif
  10396         {
  10397             if (is_float && radix != 10)
  10398                 goto fail;
  10399         }
  10400     } else {
  10401         if (atod_type == ATOD_TYPE_FLOAT64) {
  10402 #ifdef CONFIG_BIGNUM
  10403             if (flags & ATOD_MODE_BIGINT) {
  10404                 if (!is_float)
  10405                     atod_type = ATOD_TYPE_BIG_INT;
  10406                 if (has_legacy_octal)
  10407                     goto fail;
  10408             } else
  10409 #endif
  10410             {
  10411                 if (is_float && radix != 10)
  10412                     goto fail;
  10413             }
  10414         }
  10415     }
  10416 
  10417     switch(atod_type) {
  10418     case ATOD_TYPE_FLOAT64:
  10419         {
  10420             double d;
  10421             d = js_strtod(buf, radix, is_float);
  10422             /* return int or float64 */
  10423             val = JS_NewFloat64(ctx, d);
  10424         }
  10425         break;
  10426     case ATOD_TYPE_BIG_INT:
  10427         if (has_legacy_octal || is_float)
  10428             goto fail;
  10429         val = ctx->rt->bigint_ops.from_string(ctx, buf, radix, flags, NULL);
  10430         break;
  10431 #ifdef CONFIG_BIGNUM
  10432     case ATOD_TYPE_BIG_FLOAT:
  10433         if (has_legacy_octal)
  10434             goto fail;
  10435         val = ctx->rt->bigfloat_ops.from_string(ctx, buf, radix, flags,
  10436                                                 pexponent);
  10437         break;
  10438     case ATOD_TYPE_BIG_DECIMAL:
  10439         if (radix != 10)
  10440             goto fail;
  10441         val = ctx->rt->bigdecimal_ops.from_string(ctx, buf, radix, flags, NULL);
  10442         break;
  10443 #endif
  10444     default:
  10445         abort();
  10446     }
  10447 
  10448 done:
  10449     if (buf_allocated)
  10450         js_free_rt(ctx->rt, buf);
  10451     if (pp)
  10452         *pp = p;
  10453     return val;
  10454  fail:
  10455     val = JS_NAN;
  10456     goto done;
  10457  mem_error:
  10458     val = JS_ThrowOutOfMemory(ctx);
  10459     goto done;
  10460 }
  10461 
  10462 #ifdef CONFIG_BIGNUM
  10463 static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
  10464                        int radix, int flags)
  10465 {
  10466     return js_atof2(ctx, str, pp, radix, flags, NULL);
  10467 }
  10468 #endif
  10469 
  10470 typedef enum JSToNumberHintEnum {
  10471     TON_FLAG_NUMBER,
  10472     TON_FLAG_NUMERIC,
  10473 } JSToNumberHintEnum;
  10474 
  10475 static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val,
  10476                                    JSToNumberHintEnum flag)
  10477 {
  10478     uint32_t tag;
  10479     JSValue ret;
  10480 
  10481  redo:
  10482     tag = JS_VALUE_GET_NORM_TAG(val);
  10483     switch(tag) {
  10484     case JS_TAG_BIG_INT:
  10485         if (flag != TON_FLAG_NUMERIC) {
  10486             JS_FreeValue(ctx, val);
  10487             return JS_ThrowTypeError(ctx, "cannot convert bigint to number");
  10488         }
  10489         ret = val;
  10490         break;
  10491 #ifdef CONFIG_BIGNUM
  10492     case JS_TAG_BIG_DECIMAL:
  10493         if (flag != TON_FLAG_NUMERIC) {
  10494             JS_FreeValue(ctx, val);
  10495             return JS_ThrowTypeError(ctx, "cannot convert bigdecimal to number");
  10496         }
  10497         ret = val;
  10498         break;
  10499     case JS_TAG_BIG_FLOAT:
  10500         if (flag != TON_FLAG_NUMERIC) {
  10501             JS_FreeValue(ctx, val);
  10502             return JS_ThrowTypeError(ctx, "cannot convert bigfloat to number");
  10503         }
  10504         ret = val;
  10505         break;
  10506 #endif
  10507     case JS_TAG_FLOAT64:
  10508     case JS_TAG_INT:
  10509     case JS_TAG_EXCEPTION:
  10510         ret = val;
  10511         break;
  10512     case JS_TAG_BOOL:
  10513     case JS_TAG_NULL:
  10514         ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
  10515         break;
  10516     case JS_TAG_UNDEFINED:
  10517         ret = JS_NAN;
  10518         break;
  10519     case JS_TAG_OBJECT:
  10520         val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
  10521         if (JS_IsException(val))
  10522             return JS_EXCEPTION;
  10523         goto redo;
  10524     case JS_TAG_STRING:
  10525         {
  10526             const char *str;
  10527             const char *p;
  10528             size_t len;
  10529 
  10530             str = JS_ToCStringLen(ctx, &len, val);
  10531             JS_FreeValue(ctx, val);
  10532             if (!str)
  10533                 return JS_EXCEPTION;
  10534             p = str;
  10535             p += skip_spaces(p);
  10536             if ((p - str) == len) {
  10537                 ret = JS_NewInt32(ctx, 0);
  10538             } else {
  10539                 int flags = ATOD_ACCEPT_BIN_OCT;
  10540                 ret = js_atof(ctx, p, &p, 0, flags);
  10541                 if (!JS_IsException(ret)) {
  10542                     p += skip_spaces(p);
  10543                     if ((p - str) != len) {
  10544                         JS_FreeValue(ctx, ret);
  10545                         ret = JS_NAN;
  10546                     }
  10547                 }
  10548             }
  10549             JS_FreeCString(ctx, str);
  10550         }
  10551         break;
  10552     case JS_TAG_SYMBOL:
  10553         JS_FreeValue(ctx, val);
  10554         return JS_ThrowTypeError(ctx, "cannot convert symbol to number");
  10555     default:
  10556         JS_FreeValue(ctx, val);
  10557         ret = JS_NAN;
  10558         break;
  10559     }
  10560     return ret;
  10561 }
  10562 
  10563 static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val)
  10564 {
  10565     return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER);
  10566 }
  10567 
  10568 static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val)
  10569 {
  10570     return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC);
  10571 }
  10572 
  10573 static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val)
  10574 {
  10575     return JS_ToNumericFree(ctx, JS_DupValue(ctx, val));
  10576 }
  10577 
  10578 static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres,
  10579                                           JSValue val)
  10580 {
  10581     double d;
  10582     uint32_t tag;
  10583 
  10584     val = JS_ToNumberFree(ctx, val);
  10585     if (JS_IsException(val)) {
  10586         *pres = JS_FLOAT64_NAN;
  10587         return -1;
  10588     }
  10589     tag = JS_VALUE_GET_NORM_TAG(val);
  10590     switch(tag) {
  10591     case JS_TAG_INT:
  10592         d = JS_VALUE_GET_INT(val);
  10593         break;
  10594     case JS_TAG_FLOAT64:
  10595         d = JS_VALUE_GET_FLOAT64(val);
  10596         break;
  10597     case JS_TAG_BIG_INT:
  10598 #ifdef CONFIG_BIGNUM
  10599     case JS_TAG_BIG_FLOAT:
  10600 #endif
  10601         {
  10602             JSBigFloat *p = JS_VALUE_GET_PTR(val);
  10603             /* XXX: there can be a double rounding issue with some
  10604                primitives (such as JS_ToUint8ClampFree()), but it is
  10605                not critical to fix it. */
  10606             bf_get_float64(&p->num, &d, BF_RNDN);
  10607             JS_FreeValue(ctx, val);
  10608         }
  10609         break;
  10610     default:
  10611         abort();
  10612     }
  10613     *pres = d;
  10614     return 0;
  10615 }
  10616 
  10617 static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val)
  10618 {
  10619     uint32_t tag;
  10620 
  10621     tag = JS_VALUE_GET_TAG(val);
  10622     if (tag <= JS_TAG_NULL) {
  10623         *pres = JS_VALUE_GET_INT(val);
  10624         return 0;
  10625     } else if (JS_TAG_IS_FLOAT64(tag)) {
  10626         *pres = JS_VALUE_GET_FLOAT64(val);
  10627         return 0;
  10628     } else {
  10629         return __JS_ToFloat64Free(ctx, pres, val);
  10630     }
  10631 }
  10632 
  10633 int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val)
  10634 {
  10635     return JS_ToFloat64Free(ctx, pres, JS_DupValue(ctx, val));
  10636 }
  10637 
  10638 static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val)
  10639 {
  10640     return JS_ToNumberFree(ctx, JS_DupValue(ctx, val));
  10641 }
  10642 
  10643 /* same as JS_ToNumber() but return 0 in case of NaN/Undefined */
  10644 static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val)
  10645 {
  10646     uint32_t tag;
  10647     JSValue ret;
  10648 
  10649  redo:
  10650     tag = JS_VALUE_GET_NORM_TAG(val);
  10651     switch(tag) {
  10652     case JS_TAG_INT:
  10653     case JS_TAG_BOOL:
  10654     case JS_TAG_NULL:
  10655     case JS_TAG_UNDEFINED:
  10656         ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
  10657         break;
  10658     case JS_TAG_FLOAT64:
  10659         {
  10660             double d = JS_VALUE_GET_FLOAT64(val);
  10661             if (isnan(d)) {
  10662                 ret = JS_NewInt32(ctx, 0);
  10663             } else {
  10664                 /* convert -0 to +0 */
  10665                 d = trunc(d) + 0.0;
  10666                 ret = JS_NewFloat64(ctx, d);
  10667             }
  10668         }
  10669         break;
  10670 #ifdef CONFIG_BIGNUM
  10671     case JS_TAG_BIG_FLOAT:
  10672         {
  10673             bf_t a_s, *a, r_s, *r = &r_s;
  10674             BOOL is_nan;
  10675 
  10676             a = JS_ToBigFloat(ctx, &a_s, val);
  10677             if (!a) {
  10678                 JS_FreeValue(ctx, val);
  10679                 return JS_EXCEPTION;
  10680             }
  10681             if (!bf_is_finite(a)) {
  10682                 is_nan = bf_is_nan(a);
  10683                 if (is_nan)
  10684                     ret = JS_NewInt32(ctx, 0);
  10685                 else
  10686                     ret = JS_DupValue(ctx, val);
  10687             } else {
  10688                 ret = JS_NewBigInt(ctx);
  10689                 if (!JS_IsException(ret)) {
  10690                     r = JS_GetBigInt(ret);
  10691                     bf_set(r, a);
  10692                     bf_rint(r, BF_RNDZ);
  10693                     ret = JS_CompactBigInt(ctx, ret);
  10694                 }
  10695             }
  10696             if (a == &a_s)
  10697                 bf_delete(a);
  10698             JS_FreeValue(ctx, val);
  10699         }
  10700         break;
  10701 #endif
  10702     default:
  10703         val = JS_ToNumberFree(ctx, val);
  10704         if (JS_IsException(val))
  10705             return val;
  10706         goto redo;
  10707     }
  10708     return ret;
  10709 }
  10710 
  10711 /* Note: the integer value is satured to 32 bits */
  10712 static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val)
  10713 {
  10714     uint32_t tag;
  10715     int ret;
  10716 
  10717  redo:
  10718     tag = JS_VALUE_GET_NORM_TAG(val);
  10719     switch(tag) {
  10720     case JS_TAG_INT:
  10721     case JS_TAG_BOOL:
  10722     case JS_TAG_NULL:
  10723     case JS_TAG_UNDEFINED:
  10724         ret = JS_VALUE_GET_INT(val);
  10725         break;
  10726     case JS_TAG_EXCEPTION:
  10727         *pres = 0;
  10728         return -1;
  10729     case JS_TAG_FLOAT64:
  10730         {
  10731             double d = JS_VALUE_GET_FLOAT64(val);
  10732             if (isnan(d)) {
  10733                 ret = 0;
  10734             } else {
  10735                 if (d < INT32_MIN)
  10736                     ret = INT32_MIN;
  10737                 else if (d > INT32_MAX)
  10738                     ret = INT32_MAX;
  10739                 else
  10740                     ret = (int)d;
  10741             }
  10742         }
  10743         break;
  10744 #ifdef CONFIG_BIGNUM
  10745     case JS_TAG_BIG_FLOAT:
  10746         {
  10747             JSBigFloat *p = JS_VALUE_GET_PTR(val);
  10748             bf_get_int32(&ret, &p->num, 0);
  10749             JS_FreeValue(ctx, val);
  10750         }
  10751         break;
  10752 #endif
  10753     default:
  10754         val = JS_ToNumberFree(ctx, val);
  10755         if (JS_IsException(val)) {
  10756             *pres = 0;
  10757             return -1;
  10758         }
  10759         goto redo;
  10760     }
  10761     *pres = ret;
  10762     return 0;
  10763 }
  10764 
  10765 int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValueConst val)
  10766 {
  10767     return JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));
  10768 }
  10769 
  10770 int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValueConst val,
  10771                     int min, int max, int min_offset)
  10772 {
  10773     int res = JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));
  10774     if (res == 0) {
  10775         if (*pres < min) {
  10776             *pres += min_offset;
  10777             if (*pres < min)
  10778                 *pres = min;
  10779         } else {
  10780             if (*pres > max)
  10781                 *pres = max;
  10782         }
  10783     }
  10784     return res;
  10785 }
  10786 
  10787 static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val)
  10788 {
  10789     uint32_t tag;
  10790 
  10791  redo:
  10792     tag = JS_VALUE_GET_NORM_TAG(val);
  10793     switch(tag) {
  10794     case JS_TAG_INT:
  10795     case JS_TAG_BOOL:
  10796     case JS_TAG_NULL:
  10797     case JS_TAG_UNDEFINED:
  10798         *pres = JS_VALUE_GET_INT(val);
  10799         return 0;
  10800     case JS_TAG_EXCEPTION:
  10801         *pres = 0;
  10802         return -1;
  10803     case JS_TAG_FLOAT64:
  10804         {
  10805             double d = JS_VALUE_GET_FLOAT64(val);
  10806             if (isnan(d)) {
  10807                 *pres = 0;
  10808             } else {
  10809                 if (d < INT64_MIN)
  10810                     *pres = INT64_MIN;
  10811                 else if (d >= 0x1p63) /* must use INT64_MAX + 1 because INT64_MAX cannot be exactly represented as a double */
  10812                     *pres = INT64_MAX;
  10813                 else
  10814                     *pres = (int64_t)d;
  10815             }
  10816         }
  10817         return 0;
  10818 #ifdef CONFIG_BIGNUM
  10819     case JS_TAG_BIG_FLOAT:
  10820         {
  10821             JSBigFloat *p = JS_VALUE_GET_PTR(val);
  10822             bf_get_int64(pres, &p->num, 0);
  10823             JS_FreeValue(ctx, val);
  10824         }
  10825         return 0;
  10826 #endif
  10827     default:
  10828         val = JS_ToNumberFree(ctx, val);
  10829         if (JS_IsException(val)) {
  10830             *pres = 0;
  10831             return -1;
  10832         }
  10833         goto redo;
  10834     }
  10835 }
  10836 
  10837 int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValueConst val)
  10838 {
  10839     return JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));
  10840 }
  10841 
  10842 int JS_ToInt64Clamp(JSContext *ctx, int64_t *pres, JSValueConst val,
  10843                     int64_t min, int64_t max, int64_t neg_offset)
  10844 {
  10845     int res = JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));
  10846     if (res == 0) {
  10847         if (*pres < 0)
  10848             *pres += neg_offset;
  10849         if (*pres < min)
  10850             *pres = min;
  10851         else if (*pres > max)
  10852             *pres = max;
  10853     }
  10854     return res;
  10855 }
  10856 
  10857 /* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0)
  10858    in case of exception */
  10859 static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
  10860 {
  10861     uint32_t tag;
  10862     int64_t ret;
  10863 
  10864  redo:
  10865     tag = JS_VALUE_GET_NORM_TAG(val);
  10866     switch(tag) {
  10867     case JS_TAG_INT:
  10868     case JS_TAG_BOOL:
  10869     case JS_TAG_NULL:
  10870     case JS_TAG_UNDEFINED:
  10871         ret = JS_VALUE_GET_INT(val);
  10872         break;
  10873     case JS_TAG_FLOAT64:
  10874         {
  10875             JSFloat64Union u;
  10876             double d;
  10877             int e;
  10878             d = JS_VALUE_GET_FLOAT64(val);
  10879             u.d = d;
  10880             /* we avoid doing fmod(x, 2^64) */
  10881             e = (u.u64 >> 52) & 0x7ff;
  10882             if (likely(e <= (1023 + 62))) {
  10883                 /* fast case */
  10884                 ret = (int64_t)d;
  10885             } else if (e <= (1023 + 62 + 53)) {
  10886                 uint64_t v;
  10887                 /* remainder modulo 2^64 */
  10888                 v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);
  10889                 ret = v << ((e - 1023) - 52);
  10890                 /* take the sign into account */
  10891                 if (u.u64 >> 63)
  10892                     ret = -ret;
  10893             } else {
  10894                 ret = 0; /* also handles NaN and +inf */
  10895             }
  10896         }
  10897         break;
  10898 #ifdef CONFIG_BIGNUM
  10899     case JS_TAG_BIG_FLOAT:
  10900         {
  10901             JSBigFloat *p = JS_VALUE_GET_PTR(val);
  10902             bf_get_int64(&ret, &p->num, BF_GET_INT_MOD);
  10903             JS_FreeValue(ctx, val);
  10904         }
  10905         break;
  10906 #endif
  10907     default:
  10908         val = JS_ToNumberFree(ctx, val);
  10909         if (JS_IsException(val)) {
  10910             *pres = 0;
  10911             return -1;
  10912         }
  10913         goto redo;
  10914     }
  10915     *pres = ret;
  10916     return 0;
  10917 }
  10918 
  10919 int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
  10920 {
  10921     return JS_ToInt64Free(ctx, pres, JS_DupValue(ctx, val));
  10922 }
  10923 
  10924 int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val)
  10925 {
  10926     if (JS_IsBigInt(ctx, val))
  10927         return JS_ToBigInt64(ctx, pres, val);
  10928     else
  10929         return JS_ToInt64(ctx, pres, val);
  10930 }
  10931 
  10932 /* return (<0, 0) in case of exception */
  10933 static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val)
  10934 {
  10935     uint32_t tag;
  10936     int32_t ret;
  10937 
  10938  redo:
  10939     tag = JS_VALUE_GET_NORM_TAG(val);
  10940     switch(tag) {
  10941     case JS_TAG_INT:
  10942     case JS_TAG_BOOL:
  10943     case JS_TAG_NULL:
  10944     case JS_TAG_UNDEFINED:
  10945         ret = JS_VALUE_GET_INT(val);
  10946         break;
  10947     case JS_TAG_FLOAT64:
  10948         {
  10949             JSFloat64Union u;
  10950             double d;
  10951             int e;
  10952             d = JS_VALUE_GET_FLOAT64(val);
  10953             u.d = d;
  10954             /* we avoid doing fmod(x, 2^32) */
  10955             e = (u.u64 >> 52) & 0x7ff;
  10956             if (likely(e <= (1023 + 30))) {
  10957                 /* fast case */
  10958                 ret = (int32_t)d;
  10959             } else if (e <= (1023 + 30 + 53)) {
  10960                 uint64_t v;
  10961                 /* remainder modulo 2^32 */
  10962                 v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);
  10963                 v = v << ((e - 1023) - 52 + 32);
  10964                 ret = v >> 32;
  10965                 /* take the sign into account */
  10966                 if (u.u64 >> 63)
  10967                     ret = -ret;
  10968             } else {
  10969                 ret = 0; /* also handles NaN and +inf */
  10970             }
  10971         }
  10972         break;
  10973 #ifdef CONFIG_BIGNUM
  10974     case JS_TAG_BIG_FLOAT:
  10975         {
  10976             JSBigFloat *p = JS_VALUE_GET_PTR(val);
  10977             bf_get_int32(&ret, &p->num, BF_GET_INT_MOD);
  10978             JS_FreeValue(ctx, val);
  10979         }
  10980         break;
  10981 #endif
  10982     default:
  10983         val = JS_ToNumberFree(ctx, val);
  10984         if (JS_IsException(val)) {
  10985             *pres = 0;
  10986             return -1;
  10987         }
  10988         goto redo;
  10989     }
  10990     *pres = ret;
  10991     return 0;
  10992 }
  10993 
  10994 int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val)
  10995 {
  10996     return JS_ToInt32Free(ctx, pres, JS_DupValue(ctx, val));
  10997 }
  10998 
  10999 static inline int JS_ToUint32Free(JSContext *ctx, uint32_t *pres, JSValue val)
  11000 {
  11001     return JS_ToInt32Free(ctx, (int32_t *)pres, val);
  11002 }
  11003 
  11004 static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val)
  11005 {
  11006     uint32_t tag;
  11007     int res;
  11008 
  11009  redo:
  11010     tag = JS_VALUE_GET_NORM_TAG(val);
  11011     switch(tag) {
  11012     case JS_TAG_INT:
  11013     case JS_TAG_BOOL:
  11014     case JS_TAG_NULL:
  11015     case JS_TAG_UNDEFINED:
  11016         res = JS_VALUE_GET_INT(val);
  11017 #ifdef CONFIG_BIGNUM
  11018     int_clamp:
  11019 #endif
  11020         res = max_int(0, min_int(255, res));
  11021         break;
  11022     case JS_TAG_FLOAT64:
  11023         {
  11024             double d = JS_VALUE_GET_FLOAT64(val);
  11025             if (isnan(d)) {
  11026                 res = 0;
  11027             } else {
  11028                 if (d < 0)
  11029                     res = 0;
  11030                 else if (d > 255)
  11031                     res = 255;
  11032                 else
  11033                     res = lrint(d);
  11034             }
  11035         }
  11036         break;
  11037 #ifdef CONFIG_BIGNUM
  11038     case JS_TAG_BIG_FLOAT:
  11039         {
  11040             JSBigFloat *p = JS_VALUE_GET_PTR(val);
  11041             bf_t r_s, *r = &r_s;
  11042             bf_init(ctx->bf_ctx, r);
  11043             bf_set(r, &p->num);
  11044             bf_rint(r, BF_RNDN);
  11045             bf_get_int32(&res, r, 0);
  11046             bf_delete(r);
  11047             JS_FreeValue(ctx, val);
  11048         }
  11049         goto int_clamp;
  11050 #endif
  11051     default:
  11052         val = JS_ToNumberFree(ctx, val);
  11053         if (JS_IsException(val)) {
  11054             *pres = 0;
  11055             return -1;
  11056         }
  11057         goto redo;
  11058     }
  11059     *pres = res;
  11060     return 0;
  11061 }
  11062 
  11063 static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
  11064                                             JSValue val, BOOL is_array_ctor)
  11065 {
  11066     uint32_t tag, len;
  11067 
  11068     tag = JS_VALUE_GET_TAG(val);
  11069     switch(tag) {
  11070     case JS_TAG_INT:
  11071     case JS_TAG_BOOL:
  11072     case JS_TAG_NULL:
  11073         {
  11074             int v;
  11075             v = JS_VALUE_GET_INT(val);
  11076             if (v < 0)
  11077                 goto fail;
  11078             len = v;
  11079         }
  11080         break;
  11081     case JS_TAG_BIG_INT:
  11082 #ifdef CONFIG_BIGNUM
  11083     case JS_TAG_BIG_FLOAT:
  11084 #endif
  11085         {
  11086             JSBigFloat *p = JS_VALUE_GET_PTR(val);
  11087             bf_t a;
  11088             BOOL res;
  11089             bf_get_int32((int32_t *)&len, &p->num, BF_GET_INT_MOD);
  11090             bf_init(ctx->bf_ctx, &a);
  11091             bf_set_ui(&a, len);
  11092             res = bf_cmp_eq(&a, &p->num);
  11093             bf_delete(&a);
  11094             JS_FreeValue(ctx, val);
  11095             if (!res)
  11096                 goto fail;
  11097         }
  11098         break;
  11099     default:
  11100         if (JS_TAG_IS_FLOAT64(tag)) {
  11101             double d;
  11102             d = JS_VALUE_GET_FLOAT64(val);
  11103             if (!(d >= 0 && d <= UINT32_MAX))
  11104                 goto fail;
  11105             len = (uint32_t)d;
  11106             if (len != d)
  11107                 goto fail;
  11108         } else {
  11109             uint32_t len1;
  11110 
  11111             if (is_array_ctor) {
  11112                 val = JS_ToNumberFree(ctx, val);
  11113                 if (JS_IsException(val))
  11114                     return -1;
  11115                 /* cannot recurse because val is a number */
  11116                 if (JS_ToArrayLengthFree(ctx, &len, val, TRUE))
  11117                     return -1;
  11118             } else {
  11119                 /* legacy behavior: must do the conversion twice and compare */
  11120                 if (JS_ToUint32(ctx, &len, val)) {
  11121                     JS_FreeValue(ctx, val);
  11122                     return -1;
  11123                 }
  11124                 val = JS_ToNumberFree(ctx, val);
  11125                 if (JS_IsException(val))
  11126                     return -1;
  11127                 /* cannot recurse because val is a number */
  11128                 if (JS_ToArrayLengthFree(ctx, &len1, val, FALSE))
  11129                     return -1;
  11130                 if (len1 != len) {
  11131                 fail:
  11132                     JS_ThrowRangeError(ctx, "invalid array length");
  11133                     return -1;
  11134                 }
  11135             }
  11136         }
  11137         break;
  11138     }
  11139     *plen = len;
  11140     return 0;
  11141 }
  11142 
  11143 #define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1)
  11144 
  11145 static BOOL is_safe_integer(double d)
  11146 {
  11147     return isfinite(d) && floor(d) == d &&
  11148         fabs(d) <= (double)MAX_SAFE_INTEGER;
  11149 }
  11150 
  11151 int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val)
  11152 {
  11153     int64_t v;
  11154     if (JS_ToInt64Sat(ctx, &v, val))
  11155         return -1;
  11156     if (v < 0 || v > MAX_SAFE_INTEGER) {
  11157         JS_ThrowRangeError(ctx, "invalid array index");
  11158         *plen = 0;
  11159         return -1;
  11160     }
  11161     *plen = v;
  11162     return 0;
  11163 }
  11164 
  11165 /* convert a value to a length between 0 and MAX_SAFE_INTEGER.
  11166    return -1 for exception */
  11167 static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen,
  11168                                        JSValue val)
  11169 {
  11170     int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0);
  11171     JS_FreeValue(ctx, val);
  11172     return res;
  11173 }
  11174 
  11175 /* Note: can return an exception */
  11176 static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val)
  11177 {
  11178     double d;
  11179     if (!JS_IsNumber(val))
  11180         return FALSE;
  11181     if (unlikely(JS_ToFloat64(ctx, &d, val)))
  11182         return -1;
  11183     return isfinite(d) && floor(d) == d;
  11184 }
  11185 
  11186 static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val)
  11187 {
  11188     uint32_t tag;
  11189 
  11190     tag = JS_VALUE_GET_NORM_TAG(val);
  11191     switch(tag) {
  11192     case JS_TAG_INT:
  11193         {
  11194             int v;
  11195             v = JS_VALUE_GET_INT(val);
  11196             return (v < 0);
  11197         }
  11198     case JS_TAG_FLOAT64:
  11199         {
  11200             JSFloat64Union u;
  11201             u.d = JS_VALUE_GET_FLOAT64(val);
  11202             return (u.u64 >> 63);
  11203         }
  11204     case JS_TAG_BIG_INT:
  11205         {
  11206             JSBigFloat *p = JS_VALUE_GET_PTR(val);
  11207             /* Note: integer zeros are not necessarily positive */
  11208             return p->num.sign && !bf_is_zero(&p->num);
  11209         }
  11210 #ifdef CONFIG_BIGNUM
  11211     case JS_TAG_BIG_FLOAT:
  11212         {
  11213             JSBigFloat *p = JS_VALUE_GET_PTR(val);
  11214             return p->num.sign;
  11215         }
  11216         break;
  11217     case JS_TAG_BIG_DECIMAL:
  11218         {
  11219             JSBigDecimal *p = JS_VALUE_GET_PTR(val);
  11220             return p->num.sign;
  11221         }
  11222         break;
  11223 #endif
  11224     default:
  11225         return FALSE;
  11226     }
  11227 }
  11228 
  11229 static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix)
  11230 {
  11231     JSValue ret;
  11232     bf_t a_s, *a;
  11233     char *str;
  11234     int saved_sign;
  11235 
  11236     a = JS_ToBigInt(ctx, &a_s, val);
  11237     if (!a)
  11238         return JS_EXCEPTION;
  11239     saved_sign = a->sign;
  11240     if (a->expn == BF_EXP_ZERO)
  11241         a->sign = 0;
  11242     str = bf_ftoa(NULL, a, radix, 0, BF_RNDZ | BF_FTOA_FORMAT_FRAC |
  11243                   BF_FTOA_JS_QUIRKS);
  11244     a->sign = saved_sign;
  11245     JS_FreeBigInt(ctx, a, &a_s);
  11246     if (!str)
  11247         return JS_ThrowOutOfMemory(ctx);
  11248     ret = JS_NewString(ctx, str);
  11249     bf_free(ctx->bf_ctx, str);
  11250     return ret;
  11251 }
  11252 
  11253 static JSValue js_bigint_to_string(JSContext *ctx, JSValueConst val)
  11254 {
  11255     return js_bigint_to_string1(ctx, val, 10);
  11256 }
  11257 
  11258 #ifdef CONFIG_BIGNUM
  11259 
  11260 static JSValue js_ftoa(JSContext *ctx, JSValueConst val1, int radix,
  11261                        limb_t prec, bf_flags_t flags)
  11262 {
  11263     JSValue val, ret;
  11264     bf_t a_s, *a;
  11265     char *str;
  11266     int saved_sign;
  11267 
  11268     val = JS_ToNumeric(ctx, val1);
  11269     if (JS_IsException(val))
  11270         return val;
  11271     a = JS_ToBigFloat(ctx, &a_s, val);
  11272     if (!a) {
  11273         JS_FreeValue(ctx, val);
  11274         return JS_EXCEPTION;
  11275     }
  11276     saved_sign = a->sign;
  11277     if (a->expn == BF_EXP_ZERO)
  11278         a->sign = 0;
  11279     flags |= BF_FTOA_JS_QUIRKS;
  11280     if ((flags & BF_FTOA_FORMAT_MASK) == BF_FTOA_FORMAT_FREE_MIN) {
  11281         /* Note: for floating point numbers with a radix which is not
  11282            a power of two, the current precision is used to compute
  11283            the number of digits. */
  11284         if ((radix & (radix - 1)) != 0) {
  11285             bf_t r_s, *r = &r_s;
  11286             int prec, flags1;
  11287             /* must round first */
  11288             if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {
  11289                 prec = ctx->fp_env.prec;
  11290                 flags1 = ctx->fp_env.flags &
  11291                     (BF_FLAG_SUBNORMAL | (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT));
  11292             } else {
  11293                 prec = 53;
  11294                 flags1 = bf_set_exp_bits(11) | BF_FLAG_SUBNORMAL;
  11295             }
  11296             bf_init(ctx->bf_ctx, r);
  11297             bf_set(r, a);
  11298             bf_round(r, prec, flags1 | BF_RNDN);
  11299             str = bf_ftoa(NULL, r, radix, prec, flags1 | flags);
  11300             bf_delete(r);
  11301         } else {
  11302             str = bf_ftoa(NULL, a, radix, BF_PREC_INF, flags);
  11303         }
  11304     } else {
  11305         str = bf_ftoa(NULL, a, radix, prec, flags);
  11306     }
  11307     a->sign = saved_sign;
  11308     if (a == &a_s)
  11309         bf_delete(a);
  11310     JS_FreeValue(ctx, val);
  11311     if (!str)
  11312         return JS_ThrowOutOfMemory(ctx);
  11313     ret = JS_NewString(ctx, str);
  11314     bf_free(ctx->bf_ctx, str);
  11315     return ret;
  11316 }
  11317 
  11318 static JSValue js_bigfloat_to_string(JSContext *ctx, JSValueConst val)
  11319 {
  11320     return js_ftoa(ctx, val, 10, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN);
  11321 }
  11322 
  11323 static JSValue js_bigdecimal_to_string1(JSContext *ctx, JSValueConst val,
  11324                                         limb_t prec, int flags)
  11325 {
  11326     JSValue ret;
  11327     bfdec_t *a;
  11328     char *str;
  11329     int saved_sign;
  11330 
  11331     a = JS_ToBigDecimal(ctx, val);
  11332     if (!a)
  11333         return JS_EXCEPTION;
  11334     saved_sign = a->sign;
  11335     if (a->expn == BF_EXP_ZERO)
  11336         a->sign = 0;
  11337     str = bfdec_ftoa(NULL, a, prec, flags | BF_FTOA_JS_QUIRKS);
  11338     a->sign = saved_sign;
  11339     if (!str)
  11340         return JS_ThrowOutOfMemory(ctx);
  11341     ret = JS_NewString(ctx, str);
  11342     bf_free(ctx->bf_ctx, str);
  11343     return ret;
  11344 }
  11345 
  11346 static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val)
  11347 {
  11348     return js_bigdecimal_to_string1(ctx, val, 0,
  11349                                     BF_RNDZ | BF_FTOA_FORMAT_FREE);
  11350 }
  11351 
  11352 #endif /* CONFIG_BIGNUM */
  11353 
  11354 /* 2 <= base <= 36 */
  11355 static char const digits[36] = "0123456789abcdefghijklmnopqrstuvwxyz";
  11356 
  11357 static char *i64toa(char *buf_end, int64_t n, unsigned int base)
  11358 {
  11359     char *q = buf_end;
  11360     int digit, is_neg;
  11361 
  11362     is_neg = 0;
  11363     if (n < 0) {
  11364         is_neg = 1;
  11365         n = -n;
  11366     }
  11367     *--q = '\0';
  11368     if (base == 10) {
  11369         /* division by known base uses multiplication */
  11370         do {
  11371             digit = (uint64_t)n % 10;
  11372             n = (uint64_t)n / 10;
  11373             *--q = '0' + digit;
  11374         } while (n != 0);
  11375     } else {
  11376         do {
  11377             digit = (uint64_t)n % base;
  11378             n = (uint64_t)n / base;
  11379             *--q = digits[digit];
  11380         } while (n != 0);
  11381     }
  11382     if (is_neg)
  11383         *--q = '-';
  11384     return q;
  11385 }
  11386 
  11387 /* buf1 contains the printf result */
  11388 static void js_ecvt1(double d, int n_digits, int *decpt, int *sign, char *buf,
  11389                      int rounding_mode, char *buf1, int buf1_size)
  11390 {
  11391     if (rounding_mode != FE_TONEAREST)
  11392         fesetround(rounding_mode);
  11393     snprintf(buf1, buf1_size, "%+.*e", n_digits - 1, d);
  11394     if (rounding_mode != FE_TONEAREST)
  11395         fesetround(FE_TONEAREST);
  11396     *sign = (buf1[0] == '-');
  11397     /* mantissa */
  11398     buf[0] = buf1[1];
  11399     if (n_digits > 1)
  11400         memcpy(buf + 1, buf1 + 3, n_digits - 1);
  11401     buf[n_digits] = '\0';
  11402     /* exponent */
  11403     *decpt = atoi(buf1 + n_digits + 2 + (n_digits > 1)) + 1;
  11404 }
  11405 
  11406 /* maximum buffer size for js_dtoa */
  11407 #define JS_DTOA_BUF_SIZE 128
  11408 
  11409 /* needed because ecvt usually limits the number of digits to
  11410    17. Return the number of digits. */
  11411 static int js_ecvt(double d, int n_digits, int *decpt, int *sign, char *buf,
  11412                    BOOL is_fixed)
  11413 {
  11414     int rounding_mode;
  11415     char buf_tmp[JS_DTOA_BUF_SIZE];
  11416 
  11417     if (!is_fixed) {
  11418         unsigned int n_digits_min, n_digits_max;
  11419         /* find the minimum amount of digits (XXX: inefficient but simple) */
  11420         n_digits_min = 1;
  11421         n_digits_max = 17;
  11422         while (n_digits_min < n_digits_max) {
  11423             n_digits = (n_digits_min + n_digits_max) / 2;
  11424             js_ecvt1(d, n_digits, decpt, sign, buf, FE_TONEAREST,
  11425                      buf_tmp, sizeof(buf_tmp));
  11426             if (strtod(buf_tmp, NULL) == d) {
  11427                 /* no need to keep the trailing zeros */
  11428                 while (n_digits >= 2 && buf[n_digits - 1] == '0')
  11429                     n_digits--;
  11430                 n_digits_max = n_digits;
  11431             } else {
  11432                 n_digits_min = n_digits + 1;
  11433             }
  11434         }
  11435         n_digits = n_digits_max;
  11436         rounding_mode = FE_TONEAREST;
  11437     } else {
  11438         rounding_mode = FE_TONEAREST;
  11439 #ifdef CONFIG_PRINTF_RNDN
  11440         {
  11441             char buf1[JS_DTOA_BUF_SIZE], buf2[JS_DTOA_BUF_SIZE];
  11442             int decpt1, sign1, decpt2, sign2;
  11443             /* The JS rounding is specified as round to nearest ties away
  11444                from zero (RNDNA), but in printf the "ties" case is not
  11445                specified (for example it is RNDN for glibc, RNDNA for
  11446                Windows), so we must round manually. */
  11447             js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_TONEAREST,
  11448                      buf_tmp, sizeof(buf_tmp));
  11449             /* XXX: could use 2 digits to reduce the average running time */
  11450             if (buf1[n_digits] == '5') {
  11451                 js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_DOWNWARD,
  11452                          buf_tmp, sizeof(buf_tmp));
  11453                 js_ecvt1(d, n_digits + 1, &decpt2, &sign2, buf2, FE_UPWARD,
  11454                          buf_tmp, sizeof(buf_tmp));
  11455                 if (memcmp(buf1, buf2, n_digits + 1) == 0 && decpt1 == decpt2) {
  11456                     /* exact result: round away from zero */
  11457                     if (sign1)
  11458                         rounding_mode = FE_DOWNWARD;
  11459                     else
  11460                         rounding_mode = FE_UPWARD;
  11461                 }
  11462             }
  11463         }
  11464 #endif /* CONFIG_PRINTF_RNDN */
  11465     }
  11466     js_ecvt1(d, n_digits, decpt, sign, buf, rounding_mode,
  11467              buf_tmp, sizeof(buf_tmp));
  11468     return n_digits;
  11469 }
  11470 
  11471 static int js_fcvt1(char (*buf)[JS_DTOA_BUF_SIZE], double d, int n_digits,
  11472                     int rounding_mode)
  11473 {
  11474     int n;
  11475     if (rounding_mode != FE_TONEAREST)
  11476         fesetround(rounding_mode);
  11477     n = snprintf(*buf, sizeof(*buf), "%.*f", n_digits, d);
  11478     if (rounding_mode != FE_TONEAREST)
  11479         fesetround(FE_TONEAREST);
  11480     assert(n < sizeof(*buf));
  11481     return n;
  11482 }
  11483 
  11484 static void js_fcvt(char (*buf)[JS_DTOA_BUF_SIZE], double d, int n_digits)
  11485 {
  11486     int rounding_mode;
  11487     rounding_mode = FE_TONEAREST;
  11488 #ifdef CONFIG_PRINTF_RNDN
  11489     {
  11490         int n1, n2;
  11491         char buf1[JS_DTOA_BUF_SIZE];
  11492         char buf2[JS_DTOA_BUF_SIZE];
  11493 
  11494         /* The JS rounding is specified as round to nearest ties away from
  11495            zero (RNDNA), but in printf the "ties" case is not specified
  11496            (for example it is RNDN for glibc, RNDNA for Windows), so we
  11497            must round manually. */
  11498         n1 = js_fcvt1(&buf1, d, n_digits + 1, FE_TONEAREST);
  11499         rounding_mode = FE_TONEAREST;
  11500         /* XXX: could use 2 digits to reduce the average running time */
  11501         if (buf1[n1 - 1] == '5') {
  11502             n1 = js_fcvt1(&buf1, d, n_digits + 1, FE_DOWNWARD);
  11503             n2 = js_fcvt1(&buf2, d, n_digits + 1, FE_UPWARD);
  11504             if (n1 == n2 && memcmp(buf1, buf2, n1) == 0) {
  11505                 /* exact result: round away from zero */
  11506                 if (buf1[0] == '-')
  11507                     rounding_mode = FE_DOWNWARD;
  11508                 else
  11509                     rounding_mode = FE_UPWARD;
  11510             }
  11511         }
  11512     }
  11513 #endif /* CONFIG_PRINTF_RNDN */
  11514     js_fcvt1(buf, d, n_digits, rounding_mode);
  11515 }
  11516 
  11517 /* radix != 10 is only supported with flags = JS_DTOA_VAR_FORMAT */
  11518 /* use as many digits as necessary */
  11519 #define JS_DTOA_VAR_FORMAT   (0 << 0)
  11520 /* use n_digits significant digits (1 <= n_digits <= 101) */
  11521 #define JS_DTOA_FIXED_FORMAT (1 << 0)
  11522 /* force fractional format: [-]dd.dd with n_digits fractional digits */
  11523 #define JS_DTOA_FRAC_FORMAT  (2 << 0)
  11524 /* force exponential notation either in fixed or variable format */
  11525 #define JS_DTOA_FORCE_EXP    (1 << 2)
  11526 
  11527 /* XXX: slow and maybe not fully correct. Use libbf when it is fast enough.
  11528    XXX: radix != 10 is only supported for small integers
  11529 */
  11530 static void js_dtoa1(char (*buf)[JS_DTOA_BUF_SIZE], double d,
  11531                      int radix, int n_digits, int flags)
  11532 {
  11533     char *q;
  11534 
  11535     if (!isfinite(d)) {
  11536         if (isnan(d)) {
  11537             pstrcpy(*buf, sizeof(*buf), "NaN");
  11538         } else if (d < 0) {
  11539             pstrcpy(*buf, sizeof(*buf), "-Infinity");
  11540         } else {
  11541             pstrcpy(*buf, sizeof(*buf), "Infinity");
  11542         }
  11543     } else if (flags == JS_DTOA_VAR_FORMAT) {
  11544         int64_t i64;
  11545         char buf1[70], *ptr;
  11546         if (d > (double)MAX_SAFE_INTEGER || d < (double)-MAX_SAFE_INTEGER)
  11547             goto generic_conv;
  11548         i64 = (int64_t)d;
  11549         if (d != i64)
  11550             goto generic_conv;
  11551         /* fast path for integers */
  11552         ptr = i64toa(buf1 + sizeof(buf1), i64, radix);
  11553         pstrcpy(*buf, sizeof(*buf), ptr);
  11554     } else {
  11555         if (d == 0.0)
  11556             d = 0.0; /* convert -0 to 0 */
  11557         if (flags == JS_DTOA_FRAC_FORMAT) {
  11558             js_fcvt(buf, d, n_digits);
  11559         } else {
  11560             char buf1[JS_DTOA_BUF_SIZE];
  11561             int sign, decpt, k, n, i, p, n_max;
  11562             BOOL is_fixed;
  11563         generic_conv:
  11564             is_fixed = ((flags & 3) == JS_DTOA_FIXED_FORMAT);
  11565             if (is_fixed) {
  11566                 n_max = n_digits;
  11567             } else {
  11568                 n_max = 21;
  11569             }
  11570             /* the number has k digits (k >= 1) */
  11571             k = js_ecvt(d, n_digits, &decpt, &sign, buf1, is_fixed);
  11572             n = decpt; /* d=10^(n-k)*(buf1) i.e. d= < x.yyyy 10^(n-1) */
  11573             q = *buf;
  11574             if (sign)
  11575                 *q++ = '-';
  11576             if (flags & JS_DTOA_FORCE_EXP)
  11577                 goto force_exp;
  11578             if (n >= 1 && n <= n_max) {
  11579                 if (k <= n) {
  11580                     memcpy(q, buf1, k);
  11581                     q += k;
  11582                     for(i = 0; i < (n - k); i++)
  11583                         *q++ = '0';
  11584                     *q = '\0';
  11585                 } else {
  11586                     /* k > n */
  11587                     memcpy(q, buf1, n);
  11588                     q += n;
  11589                     *q++ = '.';
  11590                     for(i = 0; i < (k - n); i++)
  11591                         *q++ = buf1[n + i];
  11592                     *q = '\0';
  11593                 }
  11594             } else if (n >= -5 && n <= 0) {
  11595                 *q++ = '0';
  11596                 *q++ = '.';
  11597                 for(i = 0; i < -n; i++)
  11598                     *q++ = '0';
  11599                 memcpy(q, buf1, k);
  11600                 q += k;
  11601                 *q = '\0';
  11602             } else {
  11603             force_exp:
  11604                 /* exponential notation */
  11605                 *q++ = buf1[0];
  11606                 if (k > 1) {
  11607                     *q++ = '.';
  11608                     for(i = 1; i < k; i++)
  11609                         *q++ = buf1[i];
  11610                 }
  11611                 *q++ = 'e';
  11612                 p = n - 1;
  11613                 if (p >= 0)
  11614                     *q++ = '+';
  11615                 snprintf(q, *buf + sizeof(*buf) - q, "%d", p);
  11616             }
  11617         }
  11618     }
  11619 }
  11620 
  11621 static JSValue js_dtoa(JSContext *ctx,
  11622                        double d, int radix, int n_digits, int flags)
  11623 {
  11624     char buf[JS_DTOA_BUF_SIZE];
  11625     js_dtoa1(&buf, d, radix, n_digits, flags);
  11626     return JS_NewString(ctx, buf);
  11627 }
  11628 
  11629 static JSValue js_dtoa_radix(JSContext *ctx, double d, int radix)
  11630 {
  11631     char buf[2200], *ptr, *ptr2;
  11632     /* d is finite */
  11633     int sign = d < 0;
  11634     int digit;
  11635     double frac, d0;
  11636     int64_t n0 = 0;
  11637     d = fabs(d);
  11638     d0 = trunc(d);
  11639     frac = d - d0;
  11640     ptr = buf + 1100;
  11641     *ptr = '\0';
  11642     if (d0 <= MAX_SAFE_INTEGER) {
  11643         int64_t n = n0 = (int64_t)d0;
  11644         while (n >= radix) {
  11645             digit = n % radix;
  11646             n = n / radix;
  11647             *--ptr = digits[digit];
  11648         }
  11649         *--ptr = digits[(int)n];
  11650     } else {
  11651         /* no decimals */
  11652         while (d0 >= radix) {
  11653             digit = fmod(d0, radix);
  11654             d0 = trunc(d0 / radix);
  11655             if (d0 >= MAX_SAFE_INTEGER)
  11656                 digit = 0;
  11657             *--ptr = digits[digit];
  11658         }
  11659         *--ptr = digits[(int)d0];
  11660         goto done;
  11661     }
  11662     if (frac != 0) {
  11663         double log2_radix = log2(radix);
  11664         double prec = 1023 + 51;  // handle subnormals
  11665         ptr2 = buf + 1100;
  11666         *ptr2++ = '.';
  11667         while (frac != 0 && n0 <= MAX_SAFE_INTEGER/2 && prec > 0) {
  11668             frac *= radix;
  11669             digit = trunc(frac);
  11670             frac -= digit;
  11671             *ptr2++ = digits[digit];
  11672             n0 = n0 * radix + digit;
  11673             prec -= log2_radix;
  11674         }
  11675         *ptr2 = '\0';
  11676         if (frac * radix >= radix / 2) {
  11677             char nine = digits[radix - 1];
  11678             // round to closest
  11679             while (ptr2[-1] == nine)
  11680                 *--ptr2 = '\0';
  11681             if (ptr2[-1] == '.') {
  11682                 *--ptr2 = '\0';
  11683                 while (ptr2[-1] == nine)
  11684                     *--ptr2 = '0';
  11685             }
  11686             if (ptr2 - 1 == ptr)
  11687                 *--ptr = '1';
  11688             else
  11689                 ptr2[-1] += 1;
  11690         } else {
  11691             while (ptr2[-1] == '0')
  11692                 *--ptr2 = '\0';
  11693             if (ptr2[-1] == '.')
  11694                 *--ptr2 = '\0';
  11695         }
  11696     }
  11697 done:
  11698     ptr[-1] = '-';
  11699     ptr -= sign;
  11700     return JS_NewString(ctx, ptr);
  11701 }
  11702 
  11703 JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToPropertyKey)
  11704 {
  11705     uint32_t tag;
  11706     const char *str;
  11707     char buf[32];
  11708 
  11709     tag = JS_VALUE_GET_NORM_TAG(val);
  11710     switch(tag) {
  11711     case JS_TAG_STRING:
  11712         return JS_DupValue(ctx, val);
  11713     case JS_TAG_INT:
  11714         snprintf(buf, sizeof(buf), "%d", JS_VALUE_GET_INT(val));
  11715         str = buf;
  11716         goto new_string;
  11717     case JS_TAG_BOOL:
  11718         return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ?
  11719                           JS_ATOM_true : JS_ATOM_false);
  11720     case JS_TAG_NULL:
  11721         return JS_AtomToString(ctx, JS_ATOM_null);
  11722     case JS_TAG_UNDEFINED:
  11723         return JS_AtomToString(ctx, JS_ATOM_undefined);
  11724     case JS_TAG_EXCEPTION:
  11725         return JS_EXCEPTION;
  11726     case JS_TAG_OBJECT:
  11727         {
  11728             JSValue val1, ret;
  11729             val1 = JS_ToPrimitive(ctx, val, HINT_STRING);
  11730             if (JS_IsException(val1))
  11731                 return val1;
  11732             ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey);
  11733             JS_FreeValue(ctx, val1);
  11734             return ret;
  11735         }
  11736         break;
  11737     case JS_TAG_FUNCTION_BYTECODE:
  11738         str = "[function bytecode]";
  11739         goto new_string;
  11740     case JS_TAG_SYMBOL:
  11741         if (is_ToPropertyKey) {
  11742             return JS_DupValue(ctx, val);
  11743         } else {
  11744             return JS_ThrowTypeError(ctx, "cannot convert symbol to string");
  11745         }
  11746     case JS_TAG_FLOAT64:
  11747         return js_dtoa(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0,
  11748                        JS_DTOA_VAR_FORMAT);
  11749     case JS_TAG_BIG_INT:
  11750         return ctx->rt->bigint_ops.to_string(ctx, val);
  11751 #ifdef CONFIG_BIGNUM
  11752     case JS_TAG_BIG_FLOAT:
  11753         return ctx->rt->bigfloat_ops.to_string(ctx, val);
  11754     case JS_TAG_BIG_DECIMAL:
  11755         return ctx->rt->bigdecimal_ops.to_string(ctx, val);
  11756 #endif
  11757     default:
  11758         str = "[unsupported type]";
  11759     new_string:
  11760         return JS_NewString(ctx, str);
  11761     }
  11762 }
  11763 
  11764 JSValue JS_ToString(JSContext *ctx, JSValueConst val)
  11765 {
  11766     return JS_ToStringInternal(ctx, val, FALSE);
  11767 }
  11768 
  11769 static JSValue JS_ToStringFree(JSContext *ctx, JSValue val)
  11770 {
  11771     JSValue ret;
  11772     ret = JS_ToString(ctx, val);
  11773     JS_FreeValue(ctx, val);
  11774     return ret;
  11775 }
  11776 
  11777 static JSValue JS_ToLocaleStringFree(JSContext *ctx, JSValue val)
  11778 {
  11779     if (JS_IsUndefined(val) || JS_IsNull(val))
  11780         return JS_ToStringFree(ctx, val);
  11781     return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL);
  11782 }
  11783 
  11784 JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val)
  11785 {
  11786     return JS_ToStringInternal(ctx, val, TRUE);
  11787 }
  11788 
  11789 static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValueConst val)
  11790 {
  11791     uint32_t tag = JS_VALUE_GET_TAG(val);
  11792     if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)
  11793         return JS_ThrowTypeError(ctx, "null or undefined are forbidden");
  11794     return JS_ToString(ctx, val);
  11795 }
  11796 
  11797 static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1)
  11798 {
  11799     JSValue val;
  11800     JSString *p;
  11801     int i;
  11802     uint32_t c;
  11803     StringBuffer b_s, *b = &b_s;
  11804     char buf[16];
  11805 
  11806     val = JS_ToStringCheckObject(ctx, val1);
  11807     if (JS_IsException(val))
  11808         return val;
  11809     p = JS_VALUE_GET_STRING(val);
  11810 
  11811     if (string_buffer_init(ctx, b, p->len + 2))
  11812         goto fail;
  11813 
  11814     if (string_buffer_putc8(b, '\"'))
  11815         goto fail;
  11816     for(i = 0; i < p->len; ) {
  11817         c = string_getc(p, &i);
  11818         switch(c) {
  11819         case '\t':
  11820             c = 't';
  11821             goto quote;
  11822         case '\r':
  11823             c = 'r';
  11824             goto quote;
  11825         case '\n':
  11826             c = 'n';
  11827             goto quote;
  11828         case '\b':
  11829             c = 'b';
  11830             goto quote;
  11831         case '\f':
  11832             c = 'f';
  11833             goto quote;
  11834         case '\"':
  11835         case '\\':
  11836         quote:
  11837             if (string_buffer_putc8(b, '\\'))
  11838                 goto fail;
  11839             if (string_buffer_putc8(b, c))
  11840                 goto fail;
  11841             break;
  11842         default:
  11843             if (c < 32 || is_surrogate(c)) {
  11844                 snprintf(buf, sizeof(buf), "\\u%04x", c);
  11845                 if (string_buffer_puts8(b, buf))
  11846                     goto fail;
  11847             } else {
  11848                 if (string_buffer_putc(b, c))
  11849                     goto fail;
  11850             }
  11851             break;
  11852         }
  11853     }
  11854     if (string_buffer_putc8(b, '\"'))
  11855         goto fail;
  11856     JS_FreeValue(ctx, val);
  11857     return string_buffer_end(b);
  11858  fail:
  11859     JS_FreeValue(ctx, val);
  11860     string_buffer_free(b);
  11861     return JS_EXCEPTION;
  11862 }
  11863 
  11864 static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt)
  11865 {
  11866     printf("%14s %4s %4s %14s %10s %s\n",
  11867            "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS");
  11868 }
  11869 
  11870 /* for debug only: dump an object without side effect */
  11871 static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
  11872 {
  11873     uint32_t i;
  11874     char atom_buf[ATOM_GET_STR_BUF_SIZE];
  11875     JSShape *sh;
  11876     JSShapeProperty *prs;
  11877     JSProperty *pr;
  11878     BOOL is_first = TRUE;
  11879 
  11880     /* XXX: should encode atoms with special characters */
  11881     sh = p->shape; /* the shape can be NULL while freeing an object */
  11882     printf("%14p %4d ",
  11883            (void *)p,
  11884            p->header.ref_count);
  11885     if (sh) {
  11886         printf("%3d%c %14p ",
  11887                sh->header.ref_count,
  11888                " *"[sh->is_hashed],
  11889                (void *)sh->proto);
  11890     } else {
  11891         printf("%3s  %14s ", "-", "-");
  11892     }
  11893     printf("%10s ",
  11894            JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), rt->class_array[p->class_id].class_name));
  11895     if (p->is_exotic && p->fast_array) {
  11896         printf("[ ");
  11897         for(i = 0; i < p->u.array.count; i++) {
  11898             if (i != 0)
  11899                 printf(", ");
  11900             switch (p->class_id) {
  11901             case JS_CLASS_ARRAY:
  11902             case JS_CLASS_ARGUMENTS:
  11903                 JS_DumpValueShort(rt, p->u.array.u.values[i]);
  11904                 break;
  11905             case JS_CLASS_UINT8C_ARRAY:
  11906             case JS_CLASS_INT8_ARRAY:
  11907             case JS_CLASS_UINT8_ARRAY:
  11908             case JS_CLASS_INT16_ARRAY:
  11909             case JS_CLASS_UINT16_ARRAY:
  11910             case JS_CLASS_INT32_ARRAY:
  11911             case JS_CLASS_UINT32_ARRAY:
  11912             case JS_CLASS_BIG_INT64_ARRAY:
  11913             case JS_CLASS_BIG_UINT64_ARRAY:
  11914             case JS_CLASS_FLOAT32_ARRAY:
  11915             case JS_CLASS_FLOAT64_ARRAY:
  11916                 {
  11917                     int size = 1 << typed_array_size_log2(p->class_id);
  11918                     const uint8_t *b = p->u.array.u.uint8_ptr + i * size;
  11919                     while (size-- > 0)
  11920                         printf("%02X", *b++);
  11921                 }
  11922                 break;
  11923             }
  11924         }
  11925         printf(" ] ");
  11926     }
  11927 
  11928     if (sh) {
  11929         printf("{ ");
  11930         for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
  11931             if (prs->atom != JS_ATOM_NULL) {
  11932                 pr = &p->prop[i];
  11933                 if (!is_first)
  11934                     printf(", ");
  11935                 printf("%s: ",
  11936                        JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), prs->atom));
  11937                 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
  11938                     printf("[getset %p %p]", (void *)pr->u.getset.getter,
  11939                            (void *)pr->u.getset.setter);
  11940                 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
  11941                     printf("[varref %p]", (void *)pr->u.var_ref);
  11942                 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
  11943                     printf("[autoinit %p %d %p]",
  11944                            (void *)js_autoinit_get_realm(pr),
  11945                            js_autoinit_get_id(pr),
  11946                            (void *)pr->u.init.opaque);
  11947                 } else {
  11948                     JS_DumpValueShort(rt, pr->u.value);
  11949                 }
  11950                 is_first = FALSE;
  11951             }
  11952         }
  11953         printf(" }");
  11954     }
  11955 
  11956     if (js_class_has_bytecode(p->class_id)) {
  11957         JSFunctionBytecode *b = p->u.func.function_bytecode;
  11958         JSVarRef **var_refs;
  11959         if (b->closure_var_count) {
  11960             var_refs = p->u.func.var_refs;
  11961             printf(" Closure:");
  11962             for(i = 0; i < b->closure_var_count; i++) {
  11963                 printf(" ");
  11964                 JS_DumpValueShort(rt, var_refs[i]->value);
  11965             }
  11966             if (p->u.func.home_object) {
  11967                 printf(" HomeObject: ");
  11968                 JS_DumpValueShort(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object));
  11969             }
  11970         }
  11971     }
  11972     printf("\n");
  11973 }
  11974 
  11975 static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p)
  11976 {
  11977     if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
  11978         JS_DumpObject(rt, (JSObject *)p);
  11979     } else {
  11980         printf("%14p %4d ",
  11981                (void *)p,
  11982                p->ref_count);
  11983         switch(p->gc_obj_type) {
  11984         case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
  11985             printf("[function bytecode]");
  11986             break;
  11987         case JS_GC_OBJ_TYPE_SHAPE:
  11988             printf("[shape]");
  11989             break;
  11990         case JS_GC_OBJ_TYPE_VAR_REF:
  11991             printf("[var_ref]");
  11992             break;
  11993         case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
  11994             printf("[async_function]");
  11995             break;
  11996         case JS_GC_OBJ_TYPE_JS_CONTEXT:
  11997             printf("[js_context]");
  11998             break;
  11999         default:
  12000             printf("[unknown %d]", p->gc_obj_type);
  12001             break;
  12002         }
  12003         printf("\n");
  12004     }
  12005 }
  12006 
  12007 static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
  12008                                                       JSValueConst val)
  12009 {
  12010     uint32_t tag = JS_VALUE_GET_NORM_TAG(val);
  12011     const char *str;
  12012 
  12013     switch(tag) {
  12014     case JS_TAG_INT:
  12015         printf("%d", JS_VALUE_GET_INT(val));
  12016         break;
  12017     case JS_TAG_BOOL:
  12018         if (JS_VALUE_GET_BOOL(val))
  12019             str = "true";
  12020         else
  12021             str = "false";
  12022         goto print_str;
  12023     case JS_TAG_NULL:
  12024         str = "null";
  12025         goto print_str;
  12026     case JS_TAG_EXCEPTION:
  12027         str = "exception";
  12028         goto print_str;
  12029     case JS_TAG_UNINITIALIZED:
  12030         str = "uninitialized";
  12031         goto print_str;
  12032     case JS_TAG_UNDEFINED:
  12033         str = "undefined";
  12034     print_str:
  12035         printf("%s", str);
  12036         break;
  12037     case JS_TAG_FLOAT64:
  12038         printf("%.14g", JS_VALUE_GET_FLOAT64(val));
  12039         break;
  12040     case JS_TAG_BIG_INT:
  12041         {
  12042             JSBigFloat *p = JS_VALUE_GET_PTR(val);
  12043             char *str;
  12044             str = bf_ftoa(NULL, &p->num, 10, 0,
  12045                           BF_RNDZ | BF_FTOA_FORMAT_FRAC);
  12046             printf("%sn", str);
  12047             bf_realloc(&rt->bf_ctx, str, 0);
  12048         }
  12049         break;
  12050 #ifdef CONFIG_BIGNUM
  12051     case JS_TAG_BIG_FLOAT:
  12052         {
  12053             JSBigFloat *p = JS_VALUE_GET_PTR(val);
  12054             char *str;
  12055             str = bf_ftoa(NULL, &p->num, 16, BF_PREC_INF,
  12056                           BF_RNDZ | BF_FTOA_FORMAT_FREE | BF_FTOA_ADD_PREFIX);
  12057             printf("%sl", str);
  12058             bf_free(&rt->bf_ctx, str);
  12059         }
  12060         break;
  12061     case JS_TAG_BIG_DECIMAL:
  12062         {
  12063             JSBigDecimal *p = JS_VALUE_GET_PTR(val);
  12064             char *str;
  12065             str = bfdec_ftoa(NULL, &p->num, BF_PREC_INF,
  12066                              BF_RNDZ | BF_FTOA_FORMAT_FREE);
  12067             printf("%sm", str);
  12068             bf_free(&rt->bf_ctx, str);
  12069         }
  12070         break;
  12071 #endif
  12072     case JS_TAG_STRING:
  12073         {
  12074             JSString *p;
  12075             p = JS_VALUE_GET_STRING(val);
  12076             JS_DumpString(rt, p);
  12077         }
  12078         break;
  12079     case JS_TAG_FUNCTION_BYTECODE:
  12080         {
  12081             JSFunctionBytecode *b = JS_VALUE_GET_PTR(val);
  12082             char buf[ATOM_GET_STR_BUF_SIZE];
  12083             printf("[bytecode %s]", JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));
  12084         }
  12085         break;
  12086     case JS_TAG_OBJECT:
  12087         {
  12088             JSObject *p = JS_VALUE_GET_OBJ(val);
  12089             JSAtom atom = rt->class_array[p->class_id].class_name;
  12090             char atom_buf[ATOM_GET_STR_BUF_SIZE];
  12091             printf("[%s %p]",
  12092                    JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), atom), (void *)p);
  12093         }
  12094         break;
  12095     case JS_TAG_SYMBOL:
  12096         {
  12097             JSAtomStruct *p = JS_VALUE_GET_PTR(val);
  12098             char atom_buf[ATOM_GET_STR_BUF_SIZE];
  12099             printf("Symbol(%s)",
  12100                    JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), js_get_atom_index(rt, p)));
  12101         }
  12102         break;
  12103     case JS_TAG_MODULE:
  12104         printf("[module]");
  12105         break;
  12106     default:
  12107         printf("[unknown tag %d]", tag);
  12108         break;
  12109     }
  12110 }
  12111 
  12112 static __maybe_unused void JS_DumpValue(JSContext *ctx,
  12113                                                  JSValueConst val)
  12114 {
  12115     JS_DumpValueShort(ctx->rt, val);
  12116 }
  12117 
  12118 static __maybe_unused void JS_PrintValue(JSContext *ctx,
  12119                                                   const char *str,
  12120                                                   JSValueConst val)
  12121 {
  12122     printf("%s=", str);
  12123     JS_DumpValueShort(ctx->rt, val);
  12124     printf("\n");
  12125 }
  12126 
  12127 /* return -1 if exception (proxy case) or TRUE/FALSE */
  12128 // TODO: should take flags to make proxy resolution and exceptions optional
  12129 int JS_IsArray(JSContext *ctx, JSValueConst val)
  12130 {
  12131     if (js_resolve_proxy(ctx, &val, TRUE))
  12132         return -1;
  12133     if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) {
  12134         JSObject *p = JS_VALUE_GET_OBJ(val);
  12135         return p->class_id == JS_CLASS_ARRAY;
  12136     } else {
  12137         return FALSE;
  12138     }
  12139 }
  12140 
  12141 static double js_pow(double a, double b)
  12142 {
  12143     if (unlikely(!isfinite(b)) && fabs(a) == 1) {
  12144         /* not compatible with IEEE 754 */
  12145         return JS_FLOAT64_NAN;
  12146     } else {
  12147         return pow(a, b);
  12148     }
  12149 }
  12150 
  12151 JSValue JS_NewBigInt64_1(JSContext *ctx, int64_t v)
  12152 {
  12153     JSValue val;
  12154     bf_t *a;
  12155     val = JS_NewBigInt(ctx);
  12156     if (JS_IsException(val))
  12157         return val;
  12158     a = JS_GetBigInt(val);
  12159     if (bf_set_si(a, v)) {
  12160         JS_FreeValue(ctx, val);
  12161         return JS_ThrowOutOfMemory(ctx);
  12162     }
  12163     return val;
  12164 }
  12165 
  12166 JSValue JS_NewBigInt64(JSContext *ctx, int64_t v)
  12167 {
  12168     if (is_math_mode(ctx) &&
  12169         v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) {
  12170         return JS_NewInt64(ctx, v);
  12171     } else {
  12172         return JS_NewBigInt64_1(ctx, v);
  12173     }
  12174 }
  12175 
  12176 JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v)
  12177 {
  12178     JSValue val;
  12179     if (is_math_mode(ctx) && v <= MAX_SAFE_INTEGER) {
  12180         val = JS_NewInt64(ctx, v);
  12181     } else {
  12182         bf_t *a;
  12183         val = JS_NewBigInt(ctx);
  12184         if (JS_IsException(val))
  12185             return val;
  12186         a = JS_GetBigInt(val);
  12187         if (bf_set_ui(a, v)) {
  12188             JS_FreeValue(ctx, val);
  12189             return JS_ThrowOutOfMemory(ctx);
  12190         }
  12191     }
  12192     return val;
  12193 }
  12194 
  12195 /* return NaN if bad bigint literal */
  12196 static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val)
  12197 {
  12198     const char *str, *p;
  12199     size_t len;
  12200     int flags;
  12201 
  12202     str = JS_ToCStringLen(ctx, &len, val);
  12203     JS_FreeValue(ctx, val);
  12204     if (!str)
  12205         return JS_EXCEPTION;
  12206     p = str;
  12207     p += skip_spaces(p);
  12208     if ((p - str) == len) {
  12209         val = JS_NewBigInt64(ctx, 0);
  12210     } else {
  12211         flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT;
  12212 #ifdef CONFIG_BIGNUM
  12213         if (is_math_mode(ctx))
  12214             flags |= ATOD_MODE_BIGINT;
  12215 #endif
  12216         val = js_atof(ctx, p, &p, 0, flags);
  12217         p += skip_spaces(p);
  12218         if (!JS_IsException(val)) {
  12219             if ((p - str) != len) {
  12220                 JS_FreeValue(ctx, val);
  12221                 val = JS_NAN;
  12222             }
  12223         }
  12224     }
  12225     JS_FreeCString(ctx, str);
  12226     return val;
  12227 }
  12228 
  12229 static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val)
  12230 {
  12231     val = JS_StringToBigInt(ctx, val);
  12232     if (JS_VALUE_IS_NAN(val))
  12233         return JS_ThrowSyntaxError(ctx, "invalid bigint literal");
  12234     return val;
  12235 }
  12236 
  12237 /* if the returned bigfloat is allocated it is equal to
  12238    'buf'. Otherwise it is a pointer to the bigfloat in 'val'. */
  12239 static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val)
  12240 {
  12241     uint32_t tag;
  12242     bf_t *r;
  12243     JSBigFloat *p;
  12244 
  12245  redo:
  12246     tag = JS_VALUE_GET_NORM_TAG(val);
  12247     switch(tag) {
  12248     case JS_TAG_INT:
  12249     case JS_TAG_NULL:
  12250     case JS_TAG_UNDEFINED:
  12251         if (!is_math_mode(ctx))
  12252             goto fail;
  12253         /* fall tru */
  12254     case JS_TAG_BOOL:
  12255         r = buf;
  12256         bf_init(ctx->bf_ctx, r);
  12257         bf_set_si(r, JS_VALUE_GET_INT(val));
  12258         break;
  12259     case JS_TAG_FLOAT64:
  12260         {
  12261             double d = JS_VALUE_GET_FLOAT64(val);
  12262             if (!is_math_mode(ctx))
  12263                 goto fail;
  12264             if (!isfinite(d))
  12265                 goto fail;
  12266             r = buf;
  12267             bf_init(ctx->bf_ctx, r);
  12268             d = trunc(d);
  12269             bf_set_float64(r, d);
  12270         }
  12271         break;
  12272     case JS_TAG_BIG_INT:
  12273         p = JS_VALUE_GET_PTR(val);
  12274         r = &p->num;
  12275         break;
  12276 #ifdef CONFIG_BIGNUM
  12277     case JS_TAG_BIG_FLOAT:
  12278         if (!is_math_mode(ctx))
  12279             goto fail;
  12280         p = JS_VALUE_GET_PTR(val);
  12281         if (!bf_is_finite(&p->num))
  12282             goto fail;
  12283         r = buf;
  12284         bf_init(ctx->bf_ctx, r);
  12285         bf_set(r, &p->num);
  12286         bf_rint(r, BF_RNDZ);
  12287         JS_FreeValue(ctx, val);
  12288         break;
  12289 #endif
  12290     case JS_TAG_STRING:
  12291         val = JS_StringToBigIntErr(ctx, val);
  12292         if (JS_IsException(val))
  12293             return NULL;
  12294         goto redo;
  12295     case JS_TAG_OBJECT:
  12296         val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
  12297         if (JS_IsException(val))
  12298             return NULL;
  12299         goto redo;
  12300     default:
  12301     fail:
  12302         JS_FreeValue(ctx, val);
  12303         JS_ThrowTypeError(ctx, "cannot convert to bigint");
  12304         return NULL;
  12305     }
  12306     return r;
  12307 }
  12308 
  12309 static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val)
  12310 {
  12311     return JS_ToBigIntFree(ctx, buf, JS_DupValue(ctx, val));
  12312 }
  12313 
  12314 static __maybe_unused JSValue JS_ToBigIntValueFree(JSContext *ctx, JSValue val)
  12315 {
  12316     if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT) {
  12317         return val;
  12318     } else {
  12319         bf_t a_s, *a, *r;
  12320         int ret;
  12321         JSValue res;
  12322 
  12323         res = JS_NewBigInt(ctx);
  12324         if (JS_IsException(res))
  12325             return JS_EXCEPTION;
  12326         a = JS_ToBigIntFree(ctx, &a_s, val);
  12327         if (!a) {
  12328             JS_FreeValue(ctx, res);
  12329             return JS_EXCEPTION;
  12330         }
  12331         r = JS_GetBigInt(res);
  12332         ret = bf_set(r, a);
  12333         JS_FreeBigInt(ctx, a, &a_s);
  12334         if (ret) {
  12335             JS_FreeValue(ctx, res);
  12336             return JS_ThrowOutOfMemory(ctx);
  12337         }
  12338         return JS_CompactBigInt(ctx, res);
  12339     }
  12340 }
  12341 
  12342 /* free the bf_t allocated by JS_ToBigInt */
  12343 static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf)
  12344 {
  12345     if (a == buf) {
  12346         bf_delete(a);
  12347     } else {
  12348         JSBigFloat *p = (JSBigFloat *)((uint8_t *)a -
  12349                                        offsetof(JSBigFloat, num));
  12350         JS_FreeValue(ctx, JS_MKPTR(JS_TAG_BIG_INT, p));
  12351     }
  12352 }
  12353 
  12354 /* XXX: merge with JS_ToInt64Free with a specific flag */
  12355 static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
  12356 {
  12357     bf_t a_s, *a;
  12358 
  12359     a = JS_ToBigIntFree(ctx, &a_s, val);
  12360     if (!a) {
  12361         *pres = 0;
  12362         return -1;
  12363     }
  12364     bf_get_int64(pres, a, BF_GET_INT_MOD);
  12365     JS_FreeBigInt(ctx, a, &a_s);
  12366     return 0;
  12367 }
  12368 
  12369 int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
  12370 {
  12371     return JS_ToBigInt64Free(ctx, pres, JS_DupValue(ctx, val));
  12372 }
  12373 
  12374 static JSBigFloat *js_new_bf(JSContext *ctx)
  12375 {
  12376     JSBigFloat *p;
  12377     p = js_malloc(ctx, sizeof(*p));
  12378     if (!p)
  12379         return NULL;
  12380     p->header.ref_count = 1;
  12381     bf_init(ctx->bf_ctx, &p->num);
  12382     return p;
  12383 }
  12384 
  12385 static JSValue JS_NewBigInt(JSContext *ctx)
  12386 {
  12387     JSBigFloat *p;
  12388     p = js_malloc(ctx, sizeof(*p));
  12389     if (!p)
  12390         return JS_EXCEPTION;
  12391     p->header.ref_count = 1;
  12392     bf_init(ctx->bf_ctx, &p->num);
  12393     return JS_MKPTR(JS_TAG_BIG_INT, p);
  12394 }
  12395 
  12396 static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val,
  12397                                  BOOL convert_to_safe_integer)
  12398 {
  12399     int64_t v;
  12400     bf_t *a;
  12401 
  12402     if (JS_VALUE_GET_TAG(val) != JS_TAG_BIG_INT)
  12403         return val; /* fail safe */
  12404     a = JS_GetBigInt(val);
  12405     if (convert_to_safe_integer && bf_get_int64(&v, a, 0) == 0 &&
  12406         v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) {
  12407         JS_FreeValue(ctx, val);
  12408         return JS_NewInt64(ctx, v);
  12409     } else if (a->expn == BF_EXP_ZERO && a->sign) {
  12410         JSBigFloat *p = JS_VALUE_GET_PTR(val);
  12411         assert(p->header.ref_count == 1);
  12412         a->sign = 0;
  12413     }
  12414     return val;
  12415 }
  12416 
  12417 /* Convert the big int to a safe integer if in math mode. normalize
  12418    the zero representation. Could also be used to convert the bigint
  12419    to a short bigint value. The reference count of the value must be
  12420    1. Cannot fail */
  12421 static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val)
  12422 {
  12423     return JS_CompactBigInt1(ctx, val, is_math_mode(ctx));
  12424 }
  12425 
  12426 static JSValue throw_bf_exception(JSContext *ctx, int status)
  12427 {
  12428     const char *str;
  12429     if (status & BF_ST_MEM_ERROR)
  12430         return JS_ThrowOutOfMemory(ctx);
  12431     if (status & BF_ST_DIVIDE_ZERO) {
  12432         str = "division by zero";
  12433     } else if (status & BF_ST_INVALID_OP) {
  12434         str = "invalid operation";
  12435     } else {
  12436         str = "integer overflow";
  12437     }
  12438     return JS_ThrowRangeError(ctx, "%s", str);
  12439 }
  12440 
  12441 /* if the returned bigfloat is allocated it is equal to
  12442    'buf'. Otherwise it is a pointer to the bigfloat in 'val'. Return
  12443    NULL in case of error. */
  12444 static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val)
  12445 {
  12446     uint32_t tag;
  12447     bf_t *r;
  12448     JSBigFloat *p;
  12449 
  12450     tag = JS_VALUE_GET_NORM_TAG(val);
  12451     switch(tag) {
  12452     case JS_TAG_INT:
  12453     case JS_TAG_BOOL:
  12454     case JS_TAG_NULL:
  12455         r = buf;
  12456         bf_init(ctx->bf_ctx, r);
  12457         if (bf_set_si(r, JS_VALUE_GET_INT(val)))
  12458             goto fail;
  12459         break;
  12460     case JS_TAG_FLOAT64:
  12461         r = buf;
  12462         bf_init(ctx->bf_ctx, r);
  12463         if (bf_set_float64(r, JS_VALUE_GET_FLOAT64(val))) {
  12464         fail:
  12465             bf_delete(r);
  12466             return NULL;
  12467         }
  12468         break;
  12469     case JS_TAG_BIG_INT:
  12470 #ifdef CONFIG_BIGNUM
  12471     case JS_TAG_BIG_FLOAT:
  12472 #endif
  12473         p = JS_VALUE_GET_PTR(val);
  12474         r = &p->num;
  12475         break;
  12476     case JS_TAG_UNDEFINED:
  12477     default:
  12478         r = buf;
  12479         bf_init(ctx->bf_ctx, r);
  12480         bf_set_nan(r);
  12481         break;
  12482     }
  12483     return r;
  12484 }
  12485 
  12486 #ifdef CONFIG_BIGNUM
  12487 /* return NULL if invalid type */
  12488 static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val)
  12489 {
  12490     uint32_t tag;
  12491     JSBigDecimal *p;
  12492     bfdec_t *r;
  12493 
  12494     tag = JS_VALUE_GET_NORM_TAG(val);
  12495     switch(tag) {
  12496     case JS_TAG_BIG_DECIMAL:
  12497         p = JS_VALUE_GET_PTR(val);
  12498         r = &p->num;
  12499         break;
  12500     default:
  12501         JS_ThrowTypeError(ctx, "bigdecimal expected");
  12502         r = NULL;
  12503         break;
  12504     }
  12505     return r;
  12506 }
  12507 
  12508 static JSValue JS_NewBigFloat(JSContext *ctx)
  12509 {
  12510     JSBigFloat *p;
  12511     p = js_malloc(ctx, sizeof(*p));
  12512     if (!p)
  12513         return JS_EXCEPTION;
  12514     p->header.ref_count = 1;
  12515     bf_init(ctx->bf_ctx, &p->num);
  12516     return JS_MKPTR(JS_TAG_BIG_FLOAT, p);
  12517 }
  12518 
  12519 static JSValue JS_NewBigDecimal(JSContext *ctx)
  12520 {
  12521     JSBigDecimal *p;
  12522     p = js_malloc(ctx, sizeof(*p));
  12523     if (!p)
  12524         return JS_EXCEPTION;
  12525     p->header.ref_count = 1;
  12526     bfdec_init(ctx->bf_ctx, &p->num);
  12527     return JS_MKPTR(JS_TAG_BIG_DECIMAL, p);
  12528 }
  12529 
  12530 /* must be kept in sync with JSOverloadableOperatorEnum */
  12531 /* XXX: use atoms ? */
  12532 static const char js_overloadable_operator_names[JS_OVOP_COUNT][4] = {
  12533     "+",
  12534     "-",
  12535     "*",
  12536     "/",
  12537     "%",
  12538     "**",
  12539     "|",
  12540     "&",
  12541     "^",
  12542     "<<",
  12543     ">>",
  12544     ">>>",
  12545     "==",
  12546     "<",
  12547     "pos",
  12548     "neg",
  12549     "++",
  12550     "--",
  12551     "~",
  12552 };
  12553 
  12554 static int get_ovop_from_opcode(OPCodeEnum op)
  12555 {
  12556     switch(op) {
  12557     case OP_add:
  12558         return JS_OVOP_ADD;
  12559     case OP_sub:
  12560         return JS_OVOP_SUB;
  12561     case OP_mul:
  12562         return JS_OVOP_MUL;
  12563     case OP_div:
  12564         return JS_OVOP_DIV;
  12565     case OP_mod:
  12566     case OP_math_mod:
  12567         return JS_OVOP_MOD;
  12568     case OP_pow:
  12569         return JS_OVOP_POW;
  12570     case OP_or:
  12571         return JS_OVOP_OR;
  12572     case OP_and:
  12573         return JS_OVOP_AND;
  12574     case OP_xor:
  12575         return JS_OVOP_XOR;
  12576     case OP_shl:
  12577         return JS_OVOP_SHL;
  12578     case OP_sar:
  12579         return JS_OVOP_SAR;
  12580     case OP_shr:
  12581         return JS_OVOP_SHR;
  12582     case OP_eq:
  12583     case OP_neq:
  12584         return JS_OVOP_EQ;
  12585     case OP_lt:
  12586     case OP_lte:
  12587     case OP_gt:
  12588     case OP_gte:
  12589         return JS_OVOP_LESS;
  12590     case OP_plus:
  12591         return JS_OVOP_POS;
  12592     case OP_neg:
  12593         return JS_OVOP_NEG;
  12594     case OP_inc:
  12595         return JS_OVOP_INC;
  12596     case OP_dec:
  12597         return JS_OVOP_DEC;
  12598     default:
  12599         abort();
  12600     }
  12601 }
  12602 
  12603 /* return NULL if not present */
  12604 static JSObject *find_binary_op(JSBinaryOperatorDef *def,
  12605                                 uint32_t operator_index,
  12606                                 JSOverloadableOperatorEnum op)
  12607 {
  12608     JSBinaryOperatorDefEntry *ent;
  12609     int i;
  12610     for(i = 0; i < def->count; i++) {
  12611         ent = &def->tab[i];
  12612         if (ent->operator_index == operator_index)
  12613             return ent->ops[op];
  12614     }
  12615     return NULL;
  12616 }
  12617 
  12618 /* return -1 if exception, 0 if no operator overloading, 1 if
  12619    overloaded operator called */
  12620 static __exception int js_call_binary_op_fallback(JSContext *ctx,
  12621                                                   JSValue *pret,
  12622                                                   JSValueConst op1,
  12623                                                   JSValueConst op2,
  12624                                                   OPCodeEnum op,
  12625                                                   BOOL is_numeric,
  12626                                                   int hint)
  12627 {
  12628     JSValue opset1_obj, opset2_obj, method, ret, new_op1, new_op2;
  12629     JSOperatorSetData *opset1, *opset2;
  12630     JSOverloadableOperatorEnum ovop;
  12631     JSObject *p;
  12632     JSValueConst args[2];
  12633 
  12634     if (!ctx->allow_operator_overloading)
  12635         return 0;
  12636 
  12637     opset2_obj = JS_UNDEFINED;
  12638     opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet);
  12639     if (JS_IsException(opset1_obj))
  12640         goto exception;
  12641     if (JS_IsUndefined(opset1_obj))
  12642         return 0;
  12643     opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);
  12644     if (!opset1)
  12645         goto exception;
  12646 
  12647     opset2_obj = JS_GetProperty(ctx, op2, JS_ATOM_Symbol_operatorSet);
  12648     if (JS_IsException(opset2_obj))
  12649         goto exception;
  12650     if (JS_IsUndefined(opset2_obj)) {
  12651         JS_FreeValue(ctx, opset1_obj);
  12652         return 0;
  12653     }
  12654     opset2 = JS_GetOpaque2(ctx, opset2_obj, JS_CLASS_OPERATOR_SET);
  12655     if (!opset2)
  12656         goto exception;
  12657 
  12658     if (opset1->is_primitive && opset2->is_primitive) {
  12659         JS_FreeValue(ctx, opset1_obj);
  12660         JS_FreeValue(ctx, opset2_obj);
  12661         return 0;
  12662     }
  12663 
  12664     ovop = get_ovop_from_opcode(op);
  12665 
  12666     if (opset1->operator_counter == opset2->operator_counter) {
  12667         p = opset1->self_ops[ovop];
  12668     } else if (opset1->operator_counter > opset2->operator_counter) {
  12669         p = find_binary_op(&opset1->left, opset2->operator_counter, ovop);
  12670     } else {
  12671         p = find_binary_op(&opset2->right, opset1->operator_counter, ovop);
  12672     }
  12673     if (!p) {
  12674         JS_ThrowTypeError(ctx, "operator %s: no function defined",
  12675                           js_overloadable_operator_names[ovop]);
  12676         goto exception;
  12677     }
  12678 
  12679     if (opset1->is_primitive) {
  12680         if (is_numeric) {
  12681             new_op1 = JS_ToNumeric(ctx, op1);
  12682         } else {
  12683             new_op1 = JS_ToPrimitive(ctx, op1, hint);
  12684         }
  12685         if (JS_IsException(new_op1))
  12686             goto exception;
  12687     } else {
  12688         new_op1 = JS_DupValue(ctx, op1);
  12689     }
  12690 
  12691     if (opset2->is_primitive) {
  12692         if (is_numeric) {
  12693             new_op2 = JS_ToNumeric(ctx, op2);
  12694         } else {
  12695             new_op2 = JS_ToPrimitive(ctx, op2, hint);
  12696         }
  12697         if (JS_IsException(new_op2)) {
  12698             JS_FreeValue(ctx, new_op1);
  12699             goto exception;
  12700         }
  12701     } else {
  12702         new_op2 = JS_DupValue(ctx, op2);
  12703     }
  12704 
  12705     /* XXX: could apply JS_ToPrimitive() if primitive type so that the
  12706        operator function does not get a value object */
  12707 
  12708     method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
  12709     if (ovop == JS_OVOP_LESS && (op == OP_lte || op == OP_gt)) {
  12710         args[0] = new_op2;
  12711         args[1] = new_op1;
  12712     } else {
  12713         args[0] = new_op1;
  12714         args[1] = new_op2;
  12715     }
  12716     ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args);
  12717     JS_FreeValue(ctx, new_op1);
  12718     JS_FreeValue(ctx, new_op2);
  12719     if (JS_IsException(ret))
  12720         goto exception;
  12721     if (ovop == JS_OVOP_EQ) {
  12722         BOOL res = JS_ToBoolFree(ctx, ret);
  12723         if (op == OP_neq)
  12724             res ^= 1;
  12725         ret = JS_NewBool(ctx, res);
  12726     } else if (ovop == JS_OVOP_LESS) {
  12727         if (JS_IsUndefined(ret)) {
  12728             ret = JS_FALSE;
  12729         } else {
  12730             BOOL res = JS_ToBoolFree(ctx, ret);
  12731             if (op == OP_lte || op == OP_gte)
  12732                 res ^= 1;
  12733             ret = JS_NewBool(ctx, res);
  12734         }
  12735     }
  12736     JS_FreeValue(ctx, opset1_obj);
  12737     JS_FreeValue(ctx, opset2_obj);
  12738     *pret = ret;
  12739     return 1;
  12740  exception:
  12741     JS_FreeValue(ctx, opset1_obj);
  12742     JS_FreeValue(ctx, opset2_obj);
  12743     *pret = JS_UNDEFINED;
  12744     return -1;
  12745 }
  12746 
  12747 /* try to call the operation on the operatorSet field of 'obj'. Only
  12748    used for "/" and "**" on the BigInt prototype in math mode */
  12749 static __exception int js_call_binary_op_simple(JSContext *ctx,
  12750                                                 JSValue *pret,
  12751                                                 JSValueConst obj,
  12752                                                 JSValueConst op1,
  12753                                                 JSValueConst op2,
  12754                                                 OPCodeEnum op)
  12755 {
  12756     JSValue opset1_obj, method, ret, new_op1, new_op2;
  12757     JSOperatorSetData *opset1;
  12758     JSOverloadableOperatorEnum ovop;
  12759     JSObject *p;
  12760     JSValueConst args[2];
  12761 
  12762     opset1_obj = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet);
  12763     if (JS_IsException(opset1_obj))
  12764         goto exception;
  12765     if (JS_IsUndefined(opset1_obj))
  12766         return 0;
  12767     opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);
  12768     if (!opset1)
  12769         goto exception;
  12770     ovop = get_ovop_from_opcode(op);
  12771 
  12772     p = opset1->self_ops[ovop];
  12773     if (!p) {
  12774         JS_FreeValue(ctx, opset1_obj);
  12775         return 0;
  12776     }
  12777 
  12778     new_op1 = JS_ToNumeric(ctx, op1);
  12779     if (JS_IsException(new_op1))
  12780         goto exception;
  12781     new_op2 = JS_ToNumeric(ctx, op2);
  12782     if (JS_IsException(new_op2)) {
  12783         JS_FreeValue(ctx, new_op1);
  12784         goto exception;
  12785     }
  12786 
  12787     method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
  12788     args[0] = new_op1;
  12789     args[1] = new_op2;
  12790     ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args);
  12791     JS_FreeValue(ctx, new_op1);
  12792     JS_FreeValue(ctx, new_op2);
  12793     if (JS_IsException(ret))
  12794         goto exception;
  12795     JS_FreeValue(ctx, opset1_obj);
  12796     *pret = ret;
  12797     return 1;
  12798  exception:
  12799     JS_FreeValue(ctx, opset1_obj);
  12800     *pret = JS_UNDEFINED;
  12801     return -1;
  12802 }
  12803 
  12804 /* return -1 if exception, 0 if no operator overloading, 1 if
  12805    overloaded operator called */
  12806 static __exception int js_call_unary_op_fallback(JSContext *ctx,
  12807                                                  JSValue *pret,
  12808                                                  JSValueConst op1,
  12809                                                  OPCodeEnum op)
  12810 {
  12811     JSValue opset1_obj, method, ret;
  12812     JSOperatorSetData *opset1;
  12813     JSOverloadableOperatorEnum ovop;
  12814     JSObject *p;
  12815 
  12816     if (!ctx->allow_operator_overloading)
  12817         return 0;
  12818 
  12819     opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet);
  12820     if (JS_IsException(opset1_obj))
  12821         goto exception;
  12822     if (JS_IsUndefined(opset1_obj))
  12823         return 0;
  12824     opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);
  12825     if (!opset1)
  12826         goto exception;
  12827     if (opset1->is_primitive) {
  12828         JS_FreeValue(ctx, opset1_obj);
  12829         return 0;
  12830     }
  12831 
  12832     ovop = get_ovop_from_opcode(op);
  12833 
  12834     p = opset1->self_ops[ovop];
  12835     if (!p) {
  12836         JS_ThrowTypeError(ctx, "no overloaded operator %s",
  12837                           js_overloadable_operator_names[ovop]);
  12838         goto exception;
  12839     }
  12840     method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
  12841     ret = JS_CallFree(ctx, method, JS_UNDEFINED, 1, &op1);
  12842     if (JS_IsException(ret))
  12843         goto exception;
  12844     JS_FreeValue(ctx, opset1_obj);
  12845     *pret = ret;
  12846     return 1;
  12847  exception:
  12848     JS_FreeValue(ctx, opset1_obj);
  12849     *pret = JS_UNDEFINED;
  12850     return -1;
  12851 }
  12852 
  12853 static int js_unary_arith_bigfloat(JSContext *ctx,
  12854                                    JSValue *pres, OPCodeEnum op, JSValue op1)
  12855 {
  12856     bf_t a_s, *r, *a;
  12857     int ret, v;
  12858     JSValue res;
  12859 
  12860     if (op == OP_plus && !is_math_mode(ctx)) {
  12861         JS_ThrowTypeError(ctx, "bigfloat argument with unary +");
  12862         JS_FreeValue(ctx, op1);
  12863         return -1;
  12864     }
  12865 
  12866     res = JS_NewBigFloat(ctx);
  12867     if (JS_IsException(res)) {
  12868         JS_FreeValue(ctx, op1);
  12869         return -1;
  12870     }
  12871     r = JS_GetBigFloat(res);
  12872     a = JS_ToBigFloat(ctx, &a_s, op1);
  12873     if (!a) {
  12874         JS_FreeValue(ctx, res);
  12875         JS_FreeValue(ctx, op1);
  12876         return -1;
  12877     }
  12878     ret = 0;
  12879     switch(op) {
  12880     case OP_inc:
  12881     case OP_dec:
  12882         v = 2 * (op - OP_dec) - 1;
  12883         ret = bf_add_si(r, a, v, ctx->fp_env.prec, ctx->fp_env.flags);
  12884         break;
  12885     case OP_plus:
  12886         ret = bf_set(r, a);
  12887         break;
  12888     case OP_neg:
  12889         ret = bf_set(r, a);
  12890         bf_neg(r);
  12891         break;
  12892     default:
  12893         abort();
  12894     }
  12895     if (a == &a_s)
  12896         bf_delete(a);
  12897     JS_FreeValue(ctx, op1);
  12898     if (unlikely(ret & BF_ST_MEM_ERROR)) {
  12899         JS_FreeValue(ctx, res);
  12900         throw_bf_exception(ctx, ret);
  12901         return -1;
  12902     }
  12903     *pres = res;
  12904     return 0;
  12905 }
  12906 
  12907 static int js_unary_arith_bigdecimal(JSContext *ctx,
  12908                                      JSValue *pres, OPCodeEnum op, JSValue op1)
  12909 {
  12910     bfdec_t *r, *a;
  12911     int ret, v;
  12912     JSValue res;
  12913 
  12914     if (op == OP_plus && !is_math_mode(ctx)) {
  12915         JS_ThrowTypeError(ctx, "bigdecimal argument with unary +");
  12916         JS_FreeValue(ctx, op1);
  12917         return -1;
  12918     }
  12919 
  12920     res = JS_NewBigDecimal(ctx);
  12921     if (JS_IsException(res)) {
  12922         JS_FreeValue(ctx, op1);
  12923         return -1;
  12924     }
  12925     r = JS_GetBigDecimal(res);
  12926     a = JS_ToBigDecimal(ctx, op1);
  12927     if (!a) {
  12928         JS_FreeValue(ctx, res);
  12929         JS_FreeValue(ctx, op1);
  12930         return -1;
  12931     }
  12932     ret = 0;
  12933     switch(op) {
  12934     case OP_inc:
  12935     case OP_dec:
  12936         v = 2 * (op - OP_dec) - 1;
  12937         ret = bfdec_add_si(r, a, v, BF_PREC_INF, BF_RNDZ);
  12938         break;
  12939     case OP_plus:
  12940         ret = bfdec_set(r, a);
  12941         break;
  12942     case OP_neg:
  12943         ret = bfdec_set(r, a);
  12944         bfdec_neg(r);
  12945         break;
  12946     default:
  12947         abort();
  12948     }
  12949     JS_FreeValue(ctx, op1);
  12950     if (unlikely(ret)) {
  12951         JS_FreeValue(ctx, res);
  12952         throw_bf_exception(ctx, ret);
  12953         return -1;
  12954     }
  12955     *pres = res;
  12956     return 0;
  12957 }
  12958 
  12959 #endif /* CONFIG_BIGNUM */
  12960 
  12961 static int js_unary_arith_bigint(JSContext *ctx,
  12962                                  JSValue *pres, OPCodeEnum op, JSValue op1)
  12963 {
  12964     bf_t a_s, *r, *a;
  12965     int ret, v;
  12966     JSValue res;
  12967 
  12968     if (op == OP_plus && !is_math_mode(ctx)) {
  12969         JS_ThrowTypeError(ctx, "bigint argument with unary +");
  12970         JS_FreeValue(ctx, op1);
  12971         return -1;
  12972     }
  12973     res = JS_NewBigInt(ctx);
  12974     if (JS_IsException(res)) {
  12975         JS_FreeValue(ctx, op1);
  12976         return -1;
  12977     }
  12978     r = JS_GetBigInt(res);
  12979     a = JS_ToBigInt(ctx, &a_s, op1);
  12980     if (!a) {
  12981         JS_FreeValue(ctx, res);
  12982         JS_FreeValue(ctx, op1);
  12983         return -1;
  12984     }
  12985     ret = 0;
  12986     switch(op) {
  12987     case OP_inc:
  12988     case OP_dec:
  12989         v = 2 * (op - OP_dec) - 1;
  12990         ret = bf_add_si(r, a, v, BF_PREC_INF, BF_RNDZ);
  12991         break;
  12992     case OP_plus:
  12993         ret = bf_set(r, a);
  12994         break;
  12995     case OP_neg:
  12996         ret = bf_set(r, a);
  12997         bf_neg(r);
  12998         break;
  12999     case OP_not:
  13000         ret = bf_add_si(r, a, 1, BF_PREC_INF, BF_RNDZ);
  13001         bf_neg(r);
  13002         break;
  13003     default:
  13004         abort();
  13005     }
  13006     JS_FreeBigInt(ctx, a, &a_s);
  13007     JS_FreeValue(ctx, op1);
  13008     if (unlikely(ret)) {
  13009         JS_FreeValue(ctx, res);
  13010         throw_bf_exception(ctx, ret);
  13011         return -1;
  13012     }
  13013     res = JS_CompactBigInt(ctx, res);
  13014     *pres = res;
  13015     return 0;
  13016 }
  13017 
  13018 static no_inline __exception int js_unary_arith_slow(JSContext *ctx,
  13019                                                      JSValue *sp,
  13020                                                      OPCodeEnum op)
  13021 {
  13022     JSValue op1;
  13023     int v;
  13024     uint32_t tag;
  13025 
  13026     op1 = sp[-1];
  13027     /* fast path for float64 */
  13028     if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1)))
  13029         goto handle_float64;
  13030 #ifdef CONFIG_BIGNUM
  13031     if (JS_IsObject(op1)) {
  13032         JSValue val;
  13033         int ret = js_call_unary_op_fallback(ctx, &val, op1, op);
  13034         if (ret < 0)
  13035             return -1;
  13036         if (ret) {
  13037             JS_FreeValue(ctx, op1);
  13038             sp[-1] = val;
  13039             return 0;
  13040         }
  13041     }
  13042 #endif
  13043     op1 = JS_ToNumericFree(ctx, op1);
  13044     if (JS_IsException(op1))
  13045         goto exception;
  13046     tag = JS_VALUE_GET_TAG(op1);
  13047     switch(tag) {
  13048     case JS_TAG_INT:
  13049         {
  13050             int64_t v64;
  13051             v64 = JS_VALUE_GET_INT(op1);
  13052             switch(op) {
  13053             case OP_inc:
  13054             case OP_dec:
  13055                 v = 2 * (op - OP_dec) - 1;
  13056                 v64 += v;
  13057                 break;
  13058             case OP_plus:
  13059                 break;
  13060             case OP_neg:
  13061                 if (v64 == 0) {
  13062                     sp[-1] = __JS_NewFloat64(ctx, -0.0);
  13063                     return 0;
  13064                 } else {
  13065                     v64 = -v64;
  13066                 }
  13067                 break;
  13068             default:
  13069                 abort();
  13070             }
  13071             sp[-1] = JS_NewInt64(ctx, v64);
  13072         }
  13073         break;
  13074     case JS_TAG_BIG_INT:
  13075     handle_bigint:
  13076         if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, op, op1))
  13077             goto exception;
  13078         break;
  13079 #ifdef CONFIG_BIGNUM
  13080     case JS_TAG_BIG_FLOAT:
  13081         if (ctx->rt->bigfloat_ops.unary_arith(ctx, sp - 1, op, op1))
  13082             goto exception;
  13083         break;
  13084     case JS_TAG_BIG_DECIMAL:
  13085         if (ctx->rt->bigdecimal_ops.unary_arith(ctx, sp - 1, op, op1))
  13086             goto exception;
  13087         break;
  13088 #endif
  13089     default:
  13090     handle_float64:
  13091         {
  13092             double d;
  13093             if (is_math_mode(ctx))
  13094                 goto handle_bigint;
  13095             d = JS_VALUE_GET_FLOAT64(op1);
  13096             switch(op) {
  13097             case OP_inc:
  13098             case OP_dec:
  13099                 v = 2 * (op - OP_dec) - 1;
  13100                 d += v;
  13101                 break;
  13102             case OP_plus:
  13103                 break;
  13104             case OP_neg:
  13105                 d = -d;
  13106                 break;
  13107             default:
  13108                 abort();
  13109             }
  13110             sp[-1] = __JS_NewFloat64(ctx, d);
  13111         }
  13112         break;
  13113     }
  13114     return 0;
  13115  exception:
  13116     sp[-1] = JS_UNDEFINED;
  13117     return -1;
  13118 }
  13119 
  13120 static __exception int js_post_inc_slow(JSContext *ctx,
  13121                                         JSValue *sp, OPCodeEnum op)
  13122 {
  13123     JSValue op1;
  13124 
  13125     /* XXX: allow custom operators */
  13126     op1 = sp[-1];
  13127     op1 = JS_ToNumericFree(ctx, op1);
  13128     if (JS_IsException(op1)) {
  13129         sp[-1] = JS_UNDEFINED;
  13130         return -1;
  13131     }
  13132     sp[-1] = op1;
  13133     sp[0] = JS_DupValue(ctx, op1);
  13134     return js_unary_arith_slow(ctx, sp + 1, op - OP_post_dec + OP_dec);
  13135 }
  13136 
  13137 static no_inline int js_not_slow(JSContext *ctx, JSValue *sp)
  13138 {
  13139     JSValue op1;
  13140 
  13141     op1 = sp[-1];
  13142 #ifdef CONFIG_BIGNUM
  13143     if (JS_IsObject(op1)) {
  13144         JSValue val;
  13145         int ret = js_call_unary_op_fallback(ctx, &val, op1, OP_not);
  13146         if (ret < 0)
  13147             return -1;
  13148         if (ret) {
  13149             JS_FreeValue(ctx, op1);
  13150             sp[-1] = val;
  13151             return 0;
  13152         }
  13153     }
  13154 #endif
  13155     op1 = JS_ToNumericFree(ctx, op1);
  13156     if (JS_IsException(op1))
  13157         goto exception;
  13158     if (is_math_mode(ctx) || JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) {
  13159         if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, OP_not, op1))
  13160             goto exception;
  13161     } else {
  13162         int32_t v1;
  13163         if (unlikely(JS_ToInt32Free(ctx, &v1, op1)))
  13164             goto exception;
  13165         sp[-1] = JS_NewInt32(ctx, ~v1);
  13166     }
  13167     return 0;
  13168  exception:
  13169     sp[-1] = JS_UNDEFINED;
  13170     return -1;
  13171 }
  13172 
  13173 static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op,
  13174                                   JSValue *pres, JSValue op1, JSValue op2)
  13175 {
  13176     bf_t a_s, b_s, *r, *a, *b;
  13177     int ret;
  13178     JSValue res;
  13179 
  13180     res = JS_NewBigInt(ctx);
  13181     if (JS_IsException(res))
  13182         goto fail;
  13183     a = JS_ToBigInt(ctx, &a_s, op1);
  13184     if (!a)
  13185         goto fail;
  13186     b = JS_ToBigInt(ctx, &b_s, op2);
  13187     if (!b) {
  13188         JS_FreeBigInt(ctx, a, &a_s);
  13189         goto fail;
  13190     }
  13191     r = JS_GetBigInt(res);
  13192     ret = 0;
  13193     switch(op) {
  13194     case OP_add:
  13195         ret = bf_add(r, a, b, BF_PREC_INF, BF_RNDZ);
  13196         break;
  13197     case OP_sub:
  13198         ret = bf_sub(r, a, b, BF_PREC_INF, BF_RNDZ);
  13199         break;
  13200     case OP_mul:
  13201         ret = bf_mul(r, a, b, BF_PREC_INF, BF_RNDZ);
  13202         break;
  13203     case OP_div:
  13204         if (!is_math_mode(ctx)) {
  13205             bf_t rem_s, *rem = &rem_s;
  13206             bf_init(ctx->bf_ctx, rem);
  13207             ret = bf_divrem(r, rem, a, b, BF_PREC_INF, BF_RNDZ,
  13208                             BF_RNDZ);
  13209             bf_delete(rem);
  13210         } else {
  13211             goto math_mode_div_pow;
  13212         }
  13213         break;
  13214 #ifdef CONFIG_BIGNUM
  13215     case OP_math_mod:
  13216         /* Euclidian remainder */
  13217         ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ,
  13218                      BF_DIVREM_EUCLIDIAN) & BF_ST_INVALID_OP;
  13219         break;
  13220 #endif
  13221     case OP_mod:
  13222         ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ,
  13223                      BF_RNDZ) & BF_ST_INVALID_OP;
  13224         break;
  13225     case OP_pow:
  13226         if (b->sign) {
  13227             if (!is_math_mode(ctx)) {
  13228                 ret = BF_ST_INVALID_OP;
  13229             } else {
  13230             math_mode_div_pow:
  13231 #ifdef CONFIG_BIGNUM
  13232                 JS_FreeValue(ctx, res);
  13233                 ret = js_call_binary_op_simple(ctx, &res, ctx->class_proto[JS_CLASS_BIG_INT], op1, op2, op);
  13234                 if (ret != 0) {
  13235                     JS_FreeBigInt(ctx, a, &a_s);
  13236                     JS_FreeBigInt(ctx, b, &b_s);
  13237                     JS_FreeValue(ctx, op1);
  13238                     JS_FreeValue(ctx, op2);
  13239                     if (ret < 0) {
  13240                         return -1;
  13241                     } else {
  13242                         *pres = res;
  13243                         return 0;
  13244                     }
  13245                 }
  13246                 /* if no BigInt power operator defined, return a
  13247                    bigfloat */
  13248                 res = JS_NewBigFloat(ctx);
  13249                 if (JS_IsException(res)) {
  13250                     JS_FreeBigInt(ctx, a, &a_s);
  13251                     JS_FreeBigInt(ctx, b, &b_s);
  13252                     goto fail;
  13253                 }
  13254                 r = JS_GetBigFloat(res);
  13255                 if (op == OP_div) {
  13256                     ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags) & BF_ST_MEM_ERROR;
  13257                 } else {
  13258                     ret = bf_pow(r, a, b, ctx->fp_env.prec,
  13259                                  ctx->fp_env.flags | BF_POW_JS_QUIRKS) & BF_ST_MEM_ERROR;
  13260                 }
  13261                 JS_FreeBigInt(ctx, a, &a_s);
  13262                 JS_FreeBigInt(ctx, b, &b_s);
  13263                 JS_FreeValue(ctx, op1);
  13264                 JS_FreeValue(ctx, op2);
  13265                 if (unlikely(ret)) {
  13266                     JS_FreeValue(ctx, res);
  13267                     throw_bf_exception(ctx, ret);
  13268                     return -1;
  13269                 }
  13270                 *pres = res;
  13271                 return 0;
  13272 #else
  13273                 abort();
  13274 #endif
  13275             }
  13276         } else {
  13277             ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUIRKS);
  13278         }
  13279         break;
  13280 
  13281         /* logical operations */
  13282     case OP_shl:
  13283     case OP_sar:
  13284         {
  13285             slimb_t v2;
  13286 #if LIMB_BITS == 32
  13287             bf_get_int32(&v2, b, 0);
  13288             if (v2 == INT32_MIN)
  13289                 v2 = INT32_MIN + 1;
  13290 #else
  13291             bf_get_int64(&v2, b, 0);
  13292             if (v2 == INT64_MIN)
  13293                 v2 = INT64_MIN + 1;
  13294 #endif
  13295             if (op == OP_sar)
  13296                 v2 = -v2;
  13297             ret = bf_set(r, a);
  13298             ret |= bf_mul_2exp(r, v2, BF_PREC_INF, BF_RNDZ);
  13299             if (v2 < 0) {
  13300                 ret |= bf_rint(r, BF_RNDD) & (BF_ST_OVERFLOW | BF_ST_MEM_ERROR);
  13301             }
  13302         }
  13303         break;
  13304     case OP_and:
  13305         ret = bf_logic_and(r, a, b);
  13306         break;
  13307     case OP_or:
  13308         ret = bf_logic_or(r, a, b);
  13309         break;
  13310     case OP_xor:
  13311         ret = bf_logic_xor(r, a, b);
  13312         break;
  13313     default:
  13314         abort();
  13315     }
  13316     JS_FreeBigInt(ctx, a, &a_s);
  13317     JS_FreeBigInt(ctx, b, &b_s);
  13318     JS_FreeValue(ctx, op1);
  13319     JS_FreeValue(ctx, op2);
  13320     if (unlikely(ret)) {
  13321         JS_FreeValue(ctx, res);
  13322         throw_bf_exception(ctx, ret);
  13323         return -1;
  13324     }
  13325     *pres = JS_CompactBigInt(ctx, res);
  13326     return 0;
  13327  fail:
  13328     JS_FreeValue(ctx, res);
  13329     JS_FreeValue(ctx, op1);
  13330     JS_FreeValue(ctx, op2);
  13331     return -1;
  13332 }
  13333 
  13334 #ifdef CONFIG_BIGNUM
  13335 static int js_binary_arith_bigfloat(JSContext *ctx, OPCodeEnum op,
  13336                                     JSValue *pres, JSValue op1, JSValue op2)
  13337 {
  13338     bf_t a_s, b_s, *r, *a, *b;
  13339     int ret;
  13340     JSValue res;
  13341 
  13342     res = JS_NewBigFloat(ctx);
  13343     if (JS_IsException(res))
  13344         goto fail;
  13345     r = JS_GetBigFloat(res);
  13346     a = JS_ToBigFloat(ctx, &a_s, op1);
  13347     if (!a) {
  13348         JS_FreeValue(ctx, res);
  13349         goto fail;
  13350     }
  13351     b = JS_ToBigFloat(ctx, &b_s, op2);
  13352     if (!b) {
  13353         if (a == &a_s)
  13354             bf_delete(a);
  13355         JS_FreeValue(ctx, res);
  13356         goto fail;
  13357     }
  13358     bf_init(ctx->bf_ctx, r);
  13359     switch(op) {
  13360     case OP_add:
  13361         ret = bf_add(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
  13362         break;
  13363     case OP_sub:
  13364         ret = bf_sub(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
  13365         break;
  13366     case OP_mul:
  13367         ret = bf_mul(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
  13368         break;
  13369     case OP_div:
  13370         ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
  13371         break;
  13372     case OP_math_mod:
  13373         /* Euclidian remainder */
  13374         ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags,
  13375                      BF_DIVREM_EUCLIDIAN);
  13376         break;
  13377     case OP_mod:
  13378         ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags,
  13379                      BF_RNDZ);
  13380         break;
  13381     case OP_pow:
  13382         ret = bf_pow(r, a, b, ctx->fp_env.prec,
  13383                      ctx->fp_env.flags | BF_POW_JS_QUIRKS);
  13384         break;
  13385     default:
  13386         abort();
  13387     }
  13388     if (a == &a_s)
  13389         bf_delete(a);
  13390     if (b == &b_s)
  13391         bf_delete(b);
  13392     JS_FreeValue(ctx, op1);
  13393     JS_FreeValue(ctx, op2);
  13394     if (unlikely(ret & BF_ST_MEM_ERROR)) {
  13395         JS_FreeValue(ctx, res);
  13396         throw_bf_exception(ctx, ret);
  13397         return -1;
  13398     }
  13399     *pres = res;
  13400     return 0;
  13401  fail:
  13402     JS_FreeValue(ctx, op1);
  13403     JS_FreeValue(ctx, op2);
  13404     return -1;
  13405 }
  13406 
  13407 /* b must be a positive integer */
  13408 static int js_bfdec_pow(bfdec_t *r, const bfdec_t *a, const bfdec_t *b)
  13409 {
  13410     bfdec_t b1;
  13411     int32_t b2;
  13412     int ret;
  13413 
  13414     bfdec_init(b->ctx, &b1);
  13415     ret = bfdec_set(&b1, b);
  13416     if (ret) {
  13417         bfdec_delete(&b1);
  13418         return ret;
  13419     }
  13420     ret = bfdec_rint(&b1, BF_RNDZ);
  13421     if (ret) {
  13422         bfdec_delete(&b1);
  13423         return BF_ST_INVALID_OP; /* must be an integer */
  13424     }
  13425     ret = bfdec_get_int32(&b2, &b1);
  13426     bfdec_delete(&b1);
  13427     if (ret)
  13428         return ret; /* overflow */
  13429     if (b2 < 0)
  13430         return BF_ST_INVALID_OP; /* must be positive */
  13431     return bfdec_pow_ui(r, a, b2);
  13432 }
  13433 
  13434 static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op,
  13435                                       JSValue *pres, JSValue op1, JSValue op2)
  13436 {
  13437     bfdec_t *r, *a, *b;
  13438     int ret;
  13439     JSValue res;
  13440 
  13441     res = JS_NewBigDecimal(ctx);
  13442     if (JS_IsException(res))
  13443         goto fail;
  13444     r = JS_GetBigDecimal(res);
  13445 
  13446     a = JS_ToBigDecimal(ctx, op1);
  13447     if (!a)
  13448         goto fail;
  13449     b = JS_ToBigDecimal(ctx, op2);
  13450     if (!b)
  13451         goto fail;
  13452     switch(op) {
  13453     case OP_add:
  13454         ret = bfdec_add(r, a, b, BF_PREC_INF, BF_RNDZ);
  13455         break;
  13456     case OP_sub:
  13457         ret = bfdec_sub(r, a, b, BF_PREC_INF, BF_RNDZ);
  13458         break;
  13459     case OP_mul:
  13460         ret = bfdec_mul(r, a, b, BF_PREC_INF, BF_RNDZ);
  13461         break;
  13462     case OP_div:
  13463         ret = bfdec_div(r, a, b, BF_PREC_INF, BF_RNDZ);
  13464         break;
  13465     case OP_math_mod:
  13466         /* Euclidian remainder */
  13467         ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_DIVREM_EUCLIDIAN);
  13468         break;
  13469     case OP_mod:
  13470         ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_RNDZ);
  13471         break;
  13472     case OP_pow:
  13473         ret = js_bfdec_pow(r, a, b);
  13474         break;
  13475     default:
  13476         abort();
  13477     }
  13478     JS_FreeValue(ctx, op1);
  13479     JS_FreeValue(ctx, op2);
  13480     if (unlikely(ret)) {
  13481         JS_FreeValue(ctx, res);
  13482         throw_bf_exception(ctx, ret);
  13483         return -1;
  13484     }
  13485     *pres = res;
  13486     return 0;
  13487  fail:
  13488     JS_FreeValue(ctx, res);
  13489     JS_FreeValue(ctx, op1);
  13490     JS_FreeValue(ctx, op2);
  13491     return -1;
  13492 }
  13493 #endif /* CONFIG_BIGNUM */
  13494 
  13495 static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp,
  13496                                                       OPCodeEnum op)
  13497 {
  13498     JSValue op1, op2;
  13499     uint32_t tag1, tag2;
  13500     double d1, d2;
  13501 
  13502     op1 = sp[-2];
  13503     op2 = sp[-1];
  13504     tag1 = JS_VALUE_GET_NORM_TAG(op1);
  13505     tag2 = JS_VALUE_GET_NORM_TAG(op2);
  13506     /* fast path for float operations */
  13507     if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) {
  13508         d1 = JS_VALUE_GET_FLOAT64(op1);
  13509         d2 = JS_VALUE_GET_FLOAT64(op2);
  13510         goto handle_float64;
  13511     }
  13512 
  13513 #ifdef CONFIG_BIGNUM
  13514     /* try to call an overloaded operator */
  13515     if ((tag1 == JS_TAG_OBJECT &&
  13516          (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||
  13517         (tag2 == JS_TAG_OBJECT &&
  13518          (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {
  13519         JSValue res;
  13520         int ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0);
  13521         if (ret != 0) {
  13522             JS_FreeValue(ctx, op1);
  13523             JS_FreeValue(ctx, op2);
  13524             if (ret < 0) {
  13525                 goto exception;
  13526             } else {
  13527                 sp[-2] = res;
  13528                 return 0;
  13529             }
  13530         }
  13531     }
  13532 #endif
  13533 
  13534     op1 = JS_ToNumericFree(ctx, op1);
  13535     if (JS_IsException(op1)) {
  13536         JS_FreeValue(ctx, op2);
  13537         goto exception;
  13538     }
  13539     op2 = JS_ToNumericFree(ctx, op2);
  13540     if (JS_IsException(op2)) {
  13541         JS_FreeValue(ctx, op1);
  13542         goto exception;
  13543     }
  13544     tag1 = JS_VALUE_GET_NORM_TAG(op1);
  13545     tag2 = JS_VALUE_GET_NORM_TAG(op2);
  13546 
  13547     if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {
  13548         int32_t v1, v2;
  13549         int64_t v;
  13550         v1 = JS_VALUE_GET_INT(op1);
  13551         v2 = JS_VALUE_GET_INT(op2);
  13552         switch(op) {
  13553         case OP_sub:
  13554             v = (int64_t)v1 - (int64_t)v2;
  13555             break;
  13556         case OP_mul:
  13557             v = (int64_t)v1 * (int64_t)v2;
  13558             if (is_math_mode(ctx) &&
  13559                 (v < -MAX_SAFE_INTEGER || v > MAX_SAFE_INTEGER))
  13560                 goto handle_bigint;
  13561             if (v == 0 && (v1 | v2) < 0) {
  13562                 sp[-2] = __JS_NewFloat64(ctx, -0.0);
  13563                 return 0;
  13564             }
  13565             break;
  13566         case OP_div:
  13567             if (is_math_mode(ctx))
  13568                 goto handle_bigint;
  13569             sp[-2] = __JS_NewFloat64(ctx, (double)v1 / (double)v2);
  13570             return 0;
  13571 #ifdef CONFIG_BIGNUM
  13572         case OP_math_mod:
  13573             if (unlikely(v2 == 0)) {
  13574                 throw_bf_exception(ctx, BF_ST_DIVIDE_ZERO);
  13575                 goto exception;
  13576             }
  13577             v = (int64_t)v1 % (int64_t)v2;
  13578             if (v < 0) {
  13579                 if (v2 < 0)
  13580                     v -= v2;
  13581                 else
  13582                     v += v2;
  13583             }
  13584             break;
  13585 #endif
  13586         case OP_mod:
  13587             if (v1 < 0 || v2 <= 0) {
  13588                 sp[-2] = JS_NewFloat64(ctx, fmod(v1, v2));
  13589                 return 0;
  13590             } else {
  13591                 v = (int64_t)v1 % (int64_t)v2;
  13592             }
  13593             break;
  13594         case OP_pow:
  13595             if (!is_math_mode(ctx)) {
  13596                 sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2));
  13597                 return 0;
  13598             } else {
  13599                 goto handle_bigint;
  13600             }
  13601             break;
  13602         default:
  13603             abort();
  13604         }
  13605         sp[-2] = JS_NewInt64(ctx, v);
  13606     } else
  13607 #ifdef CONFIG_BIGNUM
  13608     if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
  13609         if (ctx->rt->bigdecimal_ops.binary_arith(ctx, op, sp - 2, op1, op2))
  13610             goto exception;
  13611     } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
  13612         if (ctx->rt->bigfloat_ops.binary_arith(ctx, op, sp - 2, op1, op2))
  13613             goto exception;
  13614     } else
  13615 #endif
  13616     if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
  13617     handle_bigint:
  13618         if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))
  13619             goto exception;
  13620     } else {
  13621         double dr;
  13622         /* float64 result */
  13623         if (JS_ToFloat64Free(ctx, &d1, op1)) {
  13624             JS_FreeValue(ctx, op2);
  13625             goto exception;
  13626         }
  13627         if (JS_ToFloat64Free(ctx, &d2, op2))
  13628             goto exception;
  13629     handle_float64:
  13630         if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2))
  13631             goto handle_bigint;
  13632         switch(op) {
  13633         case OP_sub:
  13634             dr = d1 - d2;
  13635             break;
  13636         case OP_mul:
  13637             dr = d1 * d2;
  13638             break;
  13639         case OP_div:
  13640             dr = d1 / d2;
  13641             break;
  13642         case OP_mod:
  13643             dr = fmod(d1, d2);
  13644             break;
  13645 #ifdef CONFIG_BIGNUM
  13646         case OP_math_mod:
  13647             d2 = fabs(d2);
  13648             dr = fmod(d1, d2);
  13649             /* XXX: loss of accuracy if dr < 0 */
  13650             if (dr < 0)
  13651                 dr += d2;
  13652             break;
  13653 #endif
  13654         case OP_pow:
  13655             dr = js_pow(d1, d2);
  13656             break;
  13657         default:
  13658             abort();
  13659         }
  13660         sp[-2] = __JS_NewFloat64(ctx, dr);
  13661     }
  13662     return 0;
  13663  exception:
  13664     sp[-2] = JS_UNDEFINED;
  13665     sp[-1] = JS_UNDEFINED;
  13666     return -1;
  13667 }
  13668 
  13669 static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)
  13670 {
  13671     JSValue op1, op2;
  13672     uint32_t tag1, tag2;
  13673 
  13674     op1 = sp[-2];
  13675     op2 = sp[-1];
  13676 
  13677     tag1 = JS_VALUE_GET_NORM_TAG(op1);
  13678     tag2 = JS_VALUE_GET_NORM_TAG(op2);
  13679     /* fast path for float64 */
  13680     if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) {
  13681         double d1, d2;
  13682         d1 = JS_VALUE_GET_FLOAT64(op1);
  13683         d2 = JS_VALUE_GET_FLOAT64(op2);
  13684         sp[-2] = __JS_NewFloat64(ctx, d1 + d2);
  13685         return 0;
  13686     }
  13687 
  13688     if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) {
  13689 #ifdef CONFIG_BIGNUM
  13690         /* try to call an overloaded operator */
  13691         if ((tag1 == JS_TAG_OBJECT &&
  13692              (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED &&
  13693               tag2 != JS_TAG_STRING)) ||
  13694             (tag2 == JS_TAG_OBJECT &&
  13695              (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED &&
  13696               tag1 != JS_TAG_STRING))) {
  13697             JSValue res;
  13698             int ret = js_call_binary_op_fallback(ctx, &res, op1, op2, OP_add,
  13699                                                  FALSE, HINT_NONE);
  13700             if (ret != 0) {
  13701                 JS_FreeValue(ctx, op1);
  13702                 JS_FreeValue(ctx, op2);
  13703                 if (ret < 0) {
  13704                     goto exception;
  13705                 } else {
  13706                     sp[-2] = res;
  13707                     return 0;
  13708                 }
  13709             }
  13710         }
  13711 #endif
  13712         op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
  13713         if (JS_IsException(op1)) {
  13714             JS_FreeValue(ctx, op2);
  13715             goto exception;
  13716         }
  13717 
  13718         op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);
  13719         if (JS_IsException(op2)) {
  13720             JS_FreeValue(ctx, op1);
  13721             goto exception;
  13722         }
  13723         tag1 = JS_VALUE_GET_NORM_TAG(op1);
  13724         tag2 = JS_VALUE_GET_NORM_TAG(op2);
  13725     }
  13726 
  13727     if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) {
  13728         sp[-2] = JS_ConcatString(ctx, op1, op2);
  13729         if (JS_IsException(sp[-2]))
  13730             goto exception;
  13731         return 0;
  13732     }
  13733 
  13734     op1 = JS_ToNumericFree(ctx, op1);
  13735     if (JS_IsException(op1)) {
  13736         JS_FreeValue(ctx, op2);
  13737         goto exception;
  13738     }
  13739     op2 = JS_ToNumericFree(ctx, op2);
  13740     if (JS_IsException(op2)) {
  13741         JS_FreeValue(ctx, op1);
  13742         goto exception;
  13743     }
  13744     tag1 = JS_VALUE_GET_NORM_TAG(op1);
  13745     tag2 = JS_VALUE_GET_NORM_TAG(op2);
  13746 
  13747     if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {
  13748         int32_t v1, v2;
  13749         int64_t v;
  13750         v1 = JS_VALUE_GET_INT(op1);
  13751         v2 = JS_VALUE_GET_INT(op2);
  13752         v = (int64_t)v1 + (int64_t)v2;
  13753         sp[-2] = JS_NewInt64(ctx, v);
  13754     } else
  13755 #ifdef CONFIG_BIGNUM
  13756     if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
  13757         if (ctx->rt->bigdecimal_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))
  13758             goto exception;
  13759     } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
  13760         if (ctx->rt->bigfloat_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))
  13761             goto exception;
  13762     } else
  13763 #endif
  13764     if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
  13765     handle_bigint:
  13766         if (ctx->rt->bigint_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))
  13767             goto exception;
  13768     } else {
  13769         double d1, d2;
  13770         /* float64 result */
  13771         if (JS_ToFloat64Free(ctx, &d1, op1)) {
  13772             JS_FreeValue(ctx, op2);
  13773             goto exception;
  13774         }
  13775         if (JS_ToFloat64Free(ctx, &d2, op2))
  13776             goto exception;
  13777         if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2))
  13778             goto handle_bigint;
  13779         sp[-2] = __JS_NewFloat64(ctx, d1 + d2);
  13780     }
  13781     return 0;
  13782  exception:
  13783     sp[-2] = JS_UNDEFINED;
  13784     sp[-1] = JS_UNDEFINED;
  13785     return -1;
  13786 }
  13787 
  13788 static no_inline __exception int js_binary_logic_slow(JSContext *ctx,
  13789                                                       JSValue *sp,
  13790                                                       OPCodeEnum op)
  13791 {
  13792     JSValue op1, op2;
  13793     uint32_t tag1, tag2;
  13794     uint32_t v1, v2, r;
  13795 
  13796     op1 = sp[-2];
  13797     op2 = sp[-1];
  13798     tag1 = JS_VALUE_GET_NORM_TAG(op1);
  13799     tag2 = JS_VALUE_GET_NORM_TAG(op2);
  13800 
  13801 #ifdef CONFIG_BIGNUM
  13802     /* try to call an overloaded operator */
  13803     if ((tag1 == JS_TAG_OBJECT &&
  13804          (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||
  13805         (tag2 == JS_TAG_OBJECT &&
  13806          (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {
  13807         JSValue res;
  13808         int ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0);
  13809         if (ret != 0) {
  13810             JS_FreeValue(ctx, op1);
  13811             JS_FreeValue(ctx, op2);
  13812             if (ret < 0) {
  13813                 goto exception;
  13814             } else {
  13815                 sp[-2] = res;
  13816                 return 0;
  13817             }
  13818         }
  13819     }
  13820 #endif
  13821 
  13822     op1 = JS_ToNumericFree(ctx, op1);
  13823     if (JS_IsException(op1)) {
  13824         JS_FreeValue(ctx, op2);
  13825         goto exception;
  13826     }
  13827     op2 = JS_ToNumericFree(ctx, op2);
  13828     if (JS_IsException(op2)) {
  13829         JS_FreeValue(ctx, op1);
  13830         goto exception;
  13831     }
  13832 
  13833     if (is_math_mode(ctx))
  13834         goto bigint_op;
  13835 
  13836     tag1 = JS_VALUE_GET_TAG(op1);
  13837     tag2 = JS_VALUE_GET_TAG(op2);
  13838     if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
  13839         if (tag1 != tag2) {
  13840             JS_FreeValue(ctx, op1);
  13841             JS_FreeValue(ctx, op2);
  13842             JS_ThrowTypeError(ctx, "both operands must be bigint");
  13843             goto exception;
  13844         } else {
  13845         bigint_op:
  13846             if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))
  13847                 goto exception;
  13848         }
  13849     } else {
  13850         if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) {
  13851             JS_FreeValue(ctx, op2);
  13852             goto exception;
  13853         }
  13854         if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2)))
  13855             goto exception;
  13856         switch(op) {
  13857         case OP_shl:
  13858             r = v1 << (v2 & 0x1f);
  13859             break;
  13860         case OP_sar:
  13861             r = (int)v1 >> (v2 & 0x1f);
  13862             break;
  13863         case OP_and:
  13864             r = v1 & v2;
  13865             break;
  13866         case OP_or:
  13867             r = v1 | v2;
  13868             break;
  13869         case OP_xor:
  13870             r = v1 ^ v2;
  13871             break;
  13872         default:
  13873             abort();
  13874         }
  13875         sp[-2] = JS_NewInt32(ctx, r);
  13876     }
  13877     return 0;
  13878  exception:
  13879     sp[-2] = JS_UNDEFINED;
  13880     sp[-1] = JS_UNDEFINED;
  13881     return -1;
  13882 }
  13883 
  13884 /* Note: also used for bigint */
  13885 static int js_compare_bigfloat(JSContext *ctx, OPCodeEnum op,
  13886                                JSValue op1, JSValue op2)
  13887 {
  13888     bf_t a_s, b_s, *a, *b;
  13889     int res;
  13890 
  13891     a = JS_ToBigFloat(ctx, &a_s, op1);
  13892     if (!a) {
  13893         JS_FreeValue(ctx, op2);
  13894         return -1;
  13895     }
  13896     b = JS_ToBigFloat(ctx, &b_s, op2);
  13897     if (!b) {
  13898         if (a == &a_s)
  13899             bf_delete(a);
  13900         JS_FreeValue(ctx, op1);
  13901         return -1;
  13902     }
  13903     switch(op) {
  13904     case OP_lt:
  13905         res = bf_cmp_lt(a, b); /* if NaN return false */
  13906         break;
  13907     case OP_lte:
  13908         res = bf_cmp_le(a, b); /* if NaN return false */
  13909         break;
  13910     case OP_gt:
  13911         res = bf_cmp_lt(b, a); /* if NaN return false */
  13912         break;
  13913     case OP_gte:
  13914         res = bf_cmp_le(b, a); /* if NaN return false */
  13915         break;
  13916     case OP_eq:
  13917         res = bf_cmp_eq(a, b); /* if NaN return false */
  13918         break;
  13919     default:
  13920         abort();
  13921     }
  13922     if (a == &a_s)
  13923         bf_delete(a);
  13924     if (b == &b_s)
  13925         bf_delete(b);
  13926     JS_FreeValue(ctx, op1);
  13927     JS_FreeValue(ctx, op2);
  13928     return res;
  13929 }
  13930 
  13931 #ifdef CONFIG_BIGNUM
  13932 static int js_compare_bigdecimal(JSContext *ctx, OPCodeEnum op,
  13933                                  JSValue op1, JSValue op2)
  13934 {
  13935     bfdec_t *a, *b;
  13936     int res;
  13937 
  13938     /* Note: binary floats are converted to bigdecimal with
  13939        toString(). It is not mathematically correct but is consistent
  13940        with the BigDecimal() constructor behavior */
  13941     op1 = JS_ToBigDecimalFree(ctx, op1, TRUE);
  13942     if (JS_IsException(op1)) {
  13943         JS_FreeValue(ctx, op2);
  13944         return -1;
  13945     }
  13946     op2 = JS_ToBigDecimalFree(ctx, op2, TRUE);
  13947     if (JS_IsException(op2)) {
  13948         JS_FreeValue(ctx, op1);
  13949         return -1;
  13950     }
  13951     a = JS_ToBigDecimal(ctx, op1); /* cannot fail */
  13952     b = JS_ToBigDecimal(ctx, op2); /* cannot fail */
  13953 
  13954     switch(op) {
  13955     case OP_lt:
  13956         res = bfdec_cmp_lt(a, b); /* if NaN return false */
  13957         break;
  13958     case OP_lte:
  13959         res = bfdec_cmp_le(a, b); /* if NaN return false */
  13960         break;
  13961     case OP_gt:
  13962         res = bfdec_cmp_lt(b, a); /* if NaN return false */
  13963         break;
  13964     case OP_gte:
  13965         res = bfdec_cmp_le(b, a); /* if NaN return false */
  13966         break;
  13967     case OP_eq:
  13968         res = bfdec_cmp_eq(a, b); /* if NaN return false */
  13969         break;
  13970     default:
  13971         abort();
  13972     }
  13973     JS_FreeValue(ctx, op1);
  13974     JS_FreeValue(ctx, op2);
  13975     return res;
  13976 }
  13977 #endif /* !CONFIG_BIGNUM */
  13978 
  13979 static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,
  13980                                         OPCodeEnum op)
  13981 {
  13982     JSValue op1, op2;
  13983     int res;
  13984     uint32_t tag1, tag2;
  13985 
  13986     op1 = sp[-2];
  13987     op2 = sp[-1];
  13988     tag1 = JS_VALUE_GET_NORM_TAG(op1);
  13989     tag2 = JS_VALUE_GET_NORM_TAG(op2);
  13990 #ifdef CONFIG_BIGNUM
  13991     /* try to call an overloaded operator */
  13992     if ((tag1 == JS_TAG_OBJECT &&
  13993          (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||
  13994         (tag2 == JS_TAG_OBJECT &&
  13995          (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {
  13996         JSValue ret;
  13997         res = js_call_binary_op_fallback(ctx, &ret, op1, op2, op,
  13998                                          FALSE, HINT_NUMBER);
  13999         if (res != 0) {
  14000             JS_FreeValue(ctx, op1);
  14001             JS_FreeValue(ctx, op2);
  14002             if (res < 0) {
  14003                 goto exception;
  14004             } else {
  14005                 sp[-2] = ret;
  14006                 return 0;
  14007             }
  14008         }
  14009     }
  14010 #endif
  14011     op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER);
  14012     if (JS_IsException(op1)) {
  14013         JS_FreeValue(ctx, op2);
  14014         goto exception;
  14015     }
  14016     op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER);
  14017     if (JS_IsException(op2)) {
  14018         JS_FreeValue(ctx, op1);
  14019         goto exception;
  14020     }
  14021     tag1 = JS_VALUE_GET_NORM_TAG(op1);
  14022     tag2 = JS_VALUE_GET_NORM_TAG(op2);
  14023 
  14024     if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) {
  14025         JSString *p1, *p2;
  14026         p1 = JS_VALUE_GET_STRING(op1);
  14027         p2 = JS_VALUE_GET_STRING(op2);
  14028         res = js_string_compare(ctx, p1, p2);
  14029         switch(op) {
  14030         case OP_lt:
  14031             res = (res < 0);
  14032             break;
  14033         case OP_lte:
  14034             res = (res <= 0);
  14035             break;
  14036         case OP_gt:
  14037             res = (res > 0);
  14038             break;
  14039         default:
  14040         case OP_gte:
  14041             res = (res >= 0);
  14042             break;
  14043         }
  14044         JS_FreeValue(ctx, op1);
  14045         JS_FreeValue(ctx, op2);
  14046     } else if ((tag1 <= JS_TAG_NULL || tag1 == JS_TAG_FLOAT64) &&
  14047                (tag2 <= JS_TAG_NULL || tag2 == JS_TAG_FLOAT64)) {
  14048         /* fast path for float64/int */
  14049         goto float64_compare;
  14050     } else {
  14051         if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) ||
  14052              (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING)) &&
  14053             !is_math_mode(ctx)) {
  14054             if (tag1 == JS_TAG_STRING) {
  14055                 op1 = JS_StringToBigInt(ctx, op1);
  14056                 if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT)
  14057                     goto invalid_bigint_string;
  14058             }
  14059             if (tag2 == JS_TAG_STRING) {
  14060                 op2 = JS_StringToBigInt(ctx, op2);
  14061                 if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) {
  14062                 invalid_bigint_string:
  14063                     JS_FreeValue(ctx, op1);
  14064                     JS_FreeValue(ctx, op2);
  14065                     res = FALSE;
  14066                     goto done;
  14067                 }
  14068             }
  14069         } else {
  14070             op1 = JS_ToNumericFree(ctx, op1);
  14071             if (JS_IsException(op1)) {
  14072                 JS_FreeValue(ctx, op2);
  14073                 goto exception;
  14074             }
  14075             op2 = JS_ToNumericFree(ctx, op2);
  14076             if (JS_IsException(op2)) {
  14077                 JS_FreeValue(ctx, op1);
  14078                 goto exception;
  14079             }
  14080         }
  14081 
  14082         tag1 = JS_VALUE_GET_NORM_TAG(op1);
  14083         tag2 = JS_VALUE_GET_NORM_TAG(op2);
  14084 
  14085 #ifdef CONFIG_BIGNUM
  14086         if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
  14087             res = ctx->rt->bigdecimal_ops.compare(ctx, op, op1, op2);
  14088             if (res < 0)
  14089                 goto exception;
  14090         } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
  14091             res = ctx->rt->bigfloat_ops.compare(ctx, op, op1, op2);
  14092             if (res < 0)
  14093                 goto exception;
  14094         } else
  14095 #endif
  14096         if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
  14097             res = ctx->rt->bigint_ops.compare(ctx, op, op1, op2);
  14098             if (res < 0)
  14099                 goto exception;
  14100         } else {
  14101             double d1, d2;
  14102 
  14103         float64_compare:
  14104             /* can use floating point comparison */
  14105             if (tag1 == JS_TAG_FLOAT64) {
  14106                 d1 = JS_VALUE_GET_FLOAT64(op1);
  14107             } else {
  14108                 d1 = JS_VALUE_GET_INT(op1);
  14109             }
  14110             if (tag2 == JS_TAG_FLOAT64) {
  14111                 d2 = JS_VALUE_GET_FLOAT64(op2);
  14112             } else {
  14113                 d2 = JS_VALUE_GET_INT(op2);
  14114             }
  14115             switch(op) {
  14116             case OP_lt:
  14117                 res = (d1 < d2); /* if NaN return false */
  14118                 break;
  14119             case OP_lte:
  14120                 res = (d1 <= d2); /* if NaN return false */
  14121                 break;
  14122             case OP_gt:
  14123                 res = (d1 > d2); /* if NaN return false */
  14124                 break;
  14125             default:
  14126             case OP_gte:
  14127                 res = (d1 >= d2); /* if NaN return false */
  14128                 break;
  14129             }
  14130         }
  14131     }
  14132  done:
  14133     sp[-2] = JS_NewBool(ctx, res);
  14134     return 0;
  14135  exception:
  14136     sp[-2] = JS_UNDEFINED;
  14137     sp[-1] = JS_UNDEFINED;
  14138     return -1;
  14139 }
  14140 
  14141 static BOOL tag_is_number(uint32_t tag)
  14142 {
  14143     return (tag == JS_TAG_INT || tag == JS_TAG_BIG_INT ||
  14144             tag == JS_TAG_FLOAT64
  14145 #ifdef CONFIG_BIGNUM
  14146             || tag == JS_TAG_BIG_FLOAT || tag == JS_TAG_BIG_DECIMAL
  14147 #endif
  14148             );
  14149 }
  14150 
  14151 static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,
  14152                                             BOOL is_neq)
  14153 {
  14154     JSValue op1, op2;
  14155 #ifdef CONFIG_BIGNUM
  14156     JSValue ret;
  14157 #endif
  14158     int res;
  14159     uint32_t tag1, tag2;
  14160 
  14161     op1 = sp[-2];
  14162     op2 = sp[-1];
  14163  redo:
  14164     tag1 = JS_VALUE_GET_NORM_TAG(op1);
  14165     tag2 = JS_VALUE_GET_NORM_TAG(op2);
  14166     if (tag_is_number(tag1) && tag_is_number(tag2)) {
  14167         if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {
  14168             res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2);
  14169         } else if ((tag1 == JS_TAG_FLOAT64 &&
  14170                     (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64)) ||
  14171                    (tag2 == JS_TAG_FLOAT64 &&
  14172                     (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64))) {
  14173             double d1, d2;
  14174             if (tag1 == JS_TAG_FLOAT64) {
  14175                 d1 = JS_VALUE_GET_FLOAT64(op1);
  14176             } else {
  14177                 d1 = JS_VALUE_GET_INT(op1);
  14178             }
  14179             if (tag2 == JS_TAG_FLOAT64) {
  14180                 d2 = JS_VALUE_GET_FLOAT64(op2);
  14181             } else {
  14182                 d2 = JS_VALUE_GET_INT(op2);
  14183             }
  14184             res = (d1 == d2);
  14185         } else
  14186 #ifdef CONFIG_BIGNUM
  14187         if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
  14188             res = ctx->rt->bigdecimal_ops.compare(ctx, OP_eq, op1, op2);
  14189             if (res < 0)
  14190                 goto exception;
  14191         } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
  14192             res = ctx->rt->bigfloat_ops.compare(ctx, OP_eq, op1, op2);
  14193             if (res < 0)
  14194                 goto exception;
  14195         } else
  14196 #endif
  14197         {
  14198             res = ctx->rt->bigint_ops.compare(ctx, OP_eq, op1, op2);
  14199             if (res < 0)
  14200                 goto exception;
  14201         }
  14202     } else if (tag1 == tag2) {
  14203 #ifdef CONFIG_BIGNUM
  14204         if (tag1 == JS_TAG_OBJECT) {
  14205             /* try the fallback operator */
  14206             res = js_call_binary_op_fallback(ctx, &ret, op1, op2,
  14207                                              is_neq ? OP_neq : OP_eq,
  14208                                              FALSE, HINT_NONE);
  14209             if (res != 0) {
  14210                 JS_FreeValue(ctx, op1);
  14211                 JS_FreeValue(ctx, op2);
  14212                 if (res < 0) {
  14213                     goto exception;
  14214                 } else {
  14215                     sp[-2] = ret;
  14216                     return 0;
  14217                 }
  14218             }
  14219         }
  14220 #endif
  14221         res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);
  14222     } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) ||
  14223                (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) {
  14224         res = TRUE;
  14225     } else if ((tag1 == JS_TAG_STRING && tag_is_number(tag2)) ||
  14226                (tag2 == JS_TAG_STRING && tag_is_number(tag1))) {
  14227 
  14228         if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) &&
  14229             !is_math_mode(ctx)) {
  14230             if (tag1 == JS_TAG_STRING) {
  14231                 op1 = JS_StringToBigInt(ctx, op1);
  14232                 if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT)
  14233                     goto invalid_bigint_string;
  14234             }
  14235             if (tag2 == JS_TAG_STRING) {
  14236                 op2 = JS_StringToBigInt(ctx, op2);
  14237                 if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) {
  14238                 invalid_bigint_string:
  14239                     JS_FreeValue(ctx, op1);
  14240                     JS_FreeValue(ctx, op2);
  14241                     res = FALSE;
  14242                     goto done;
  14243                 }
  14244             }
  14245         } else {
  14246             op1 = JS_ToNumericFree(ctx, op1);
  14247             if (JS_IsException(op1)) {
  14248                 JS_FreeValue(ctx, op2);
  14249                 goto exception;
  14250             }
  14251             op2 = JS_ToNumericFree(ctx, op2);
  14252             if (JS_IsException(op2)) {
  14253                 JS_FreeValue(ctx, op1);
  14254                 goto exception;
  14255             }
  14256         }
  14257         res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);
  14258     } else if (tag1 == JS_TAG_BOOL) {
  14259         op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1));
  14260         goto redo;
  14261     } else if (tag2 == JS_TAG_BOOL) {
  14262         op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2));
  14263         goto redo;
  14264     } else if ((tag1 == JS_TAG_OBJECT &&
  14265                 (tag_is_number(tag2) || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) ||
  14266                (tag2 == JS_TAG_OBJECT &&
  14267                 (tag_is_number(tag1) || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL))) {
  14268 #ifdef CONFIG_BIGNUM
  14269         /* try the fallback operator */
  14270         res = js_call_binary_op_fallback(ctx, &ret, op1, op2,
  14271                                          is_neq ? OP_neq : OP_eq,
  14272                                          FALSE, HINT_NONE);
  14273         if (res != 0) {
  14274             JS_FreeValue(ctx, op1);
  14275             JS_FreeValue(ctx, op2);
  14276             if (res < 0) {
  14277                 goto exception;
  14278             } else {
  14279                 sp[-2] = ret;
  14280                 return 0;
  14281             }
  14282         }
  14283 #endif
  14284         op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
  14285         if (JS_IsException(op1)) {
  14286             JS_FreeValue(ctx, op2);
  14287             goto exception;
  14288         }
  14289         op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);
  14290         if (JS_IsException(op2)) {
  14291             JS_FreeValue(ctx, op1);
  14292             goto exception;
  14293         }
  14294         goto redo;
  14295     } else {
  14296         /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */
  14297         if ((JS_IsHTMLDDA(ctx, op1) &&
  14298              (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) ||
  14299             (JS_IsHTMLDDA(ctx, op2) &&
  14300              (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) {
  14301             res = TRUE;
  14302         } else {
  14303             res = FALSE;
  14304         }
  14305         JS_FreeValue(ctx, op1);
  14306         JS_FreeValue(ctx, op2);
  14307     }
  14308  done:
  14309     sp[-2] = JS_NewBool(ctx, res ^ is_neq);
  14310     return 0;
  14311  exception:
  14312     sp[-2] = JS_UNDEFINED;
  14313     sp[-1] = JS_UNDEFINED;
  14314     return -1;
  14315 }
  14316 
  14317 static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp)
  14318 {
  14319     JSValue op1, op2;
  14320     uint32_t v1, v2, r;
  14321 
  14322     op1 = sp[-2];
  14323     op2 = sp[-1];
  14324     op1 = JS_ToNumericFree(ctx, op1);
  14325     if (JS_IsException(op1)) {
  14326         JS_FreeValue(ctx, op2);
  14327         goto exception;
  14328     }
  14329     op2 = JS_ToNumericFree(ctx, op2);
  14330     if (JS_IsException(op2)) {
  14331         JS_FreeValue(ctx, op1);
  14332         goto exception;
  14333     }
  14334     /* XXX: could forbid >>> in bignum mode */
  14335     if (!is_math_mode(ctx) &&
  14336         (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT ||
  14337          JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT)) {
  14338         JS_ThrowTypeError(ctx, "bigint operands are forbidden for >>>");
  14339         JS_FreeValue(ctx, op1);
  14340         JS_FreeValue(ctx, op2);
  14341         goto exception;
  14342     }
  14343     /* cannot give an exception */
  14344     JS_ToUint32Free(ctx, &v1, op1);
  14345     JS_ToUint32Free(ctx, &v2, op2);
  14346     r = v1 >> (v2 & 0x1f);
  14347     sp[-2] = JS_NewUint32(ctx, r);
  14348     return 0;
  14349  exception:
  14350     sp[-2] = JS_UNDEFINED;
  14351     sp[-1] = JS_UNDEFINED;
  14352     return -1;
  14353 }
  14354 
  14355 #ifdef CONFIG_BIGNUM
  14356 static JSValue js_mul_pow10_to_float64(JSContext *ctx, const bf_t *a,
  14357                                        int64_t exponent)
  14358 {
  14359     bf_t r_s, *r = &r_s;
  14360     double d;
  14361     int ret;
  14362 
  14363     /* always convert to Float64 */
  14364     bf_init(ctx->bf_ctx, r);
  14365     ret = bf_mul_pow_radix(r, a, 10, exponent,
  14366                            53, bf_set_exp_bits(11) | BF_RNDN |
  14367                            BF_FLAG_SUBNORMAL);
  14368     bf_get_float64(r, &d, BF_RNDN);
  14369     bf_delete(r);
  14370     if (ret & BF_ST_MEM_ERROR)
  14371         return JS_ThrowOutOfMemory(ctx);
  14372     else
  14373         return __JS_NewFloat64(ctx, d);
  14374 }
  14375 
  14376 static no_inline int js_mul_pow10(JSContext *ctx, JSValue *sp)
  14377 {
  14378     bf_t a_s, *a, *r;
  14379     JSValue op1, op2, res;
  14380     int64_t e;
  14381     int ret;
  14382 
  14383     res = JS_NewBigFloat(ctx);
  14384     if (JS_IsException(res))
  14385         return -1;
  14386     r = JS_GetBigFloat(res);
  14387     op1 = sp[-2];
  14388     op2 = sp[-1];
  14389     a = JS_ToBigFloat(ctx, &a_s, op1);
  14390     if (!a) {
  14391         JS_FreeValue(ctx, res);
  14392         return -1;
  14393     }
  14394     if (JS_IsBigInt(ctx, op2)) {
  14395         ret = JS_ToBigInt64(ctx, &e, op2);
  14396     } else {
  14397         ret = JS_ToInt64(ctx, &e, op2);
  14398     }
  14399     if (ret) {
  14400         if (a == &a_s)
  14401             bf_delete(a);
  14402         JS_FreeValue(ctx, res);
  14403         return -1;
  14404     }
  14405 
  14406     bf_mul_pow_radix(r, a, 10, e, ctx->fp_env.prec, ctx->fp_env.flags);
  14407     if (a == &a_s)
  14408         bf_delete(a);
  14409     JS_FreeValue(ctx, op1);
  14410     JS_FreeValue(ctx, op2);
  14411     sp[-2] = res;
  14412     return 0;
  14413 }
  14414 #endif
  14415 
  14416 /* XXX: Should take JSValueConst arguments */
  14417 static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,
  14418                           JSStrictEqModeEnum eq_mode)
  14419 {
  14420     BOOL res;
  14421     int tag1, tag2;
  14422     double d1, d2;
  14423 
  14424     tag1 = JS_VALUE_GET_NORM_TAG(op1);
  14425     tag2 = JS_VALUE_GET_NORM_TAG(op2);
  14426     switch(tag1) {
  14427     case JS_TAG_BOOL:
  14428         if (tag1 != tag2) {
  14429             res = FALSE;
  14430         } else {
  14431             res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2);
  14432             goto done_no_free;
  14433         }
  14434         break;
  14435     case JS_TAG_NULL:
  14436     case JS_TAG_UNDEFINED:
  14437         res = (tag1 == tag2);
  14438         break;
  14439     case JS_TAG_STRING:
  14440         {
  14441             JSString *p1, *p2;
  14442             if (tag1 != tag2) {
  14443                 res = FALSE;
  14444             } else {
  14445                 p1 = JS_VALUE_GET_STRING(op1);
  14446                 p2 = JS_VALUE_GET_STRING(op2);
  14447                 res = (js_string_compare(ctx, p1, p2) == 0);
  14448             }
  14449         }
  14450         break;
  14451     case JS_TAG_SYMBOL:
  14452         {
  14453             JSAtomStruct *p1, *p2;
  14454             if (tag1 != tag2) {
  14455                 res = FALSE;
  14456             } else {
  14457                 p1 = JS_VALUE_GET_PTR(op1);
  14458                 p2 = JS_VALUE_GET_PTR(op2);
  14459                 res = (p1 == p2);
  14460             }
  14461         }
  14462         break;
  14463     case JS_TAG_OBJECT:
  14464         if (tag1 != tag2)
  14465             res = FALSE;
  14466         else
  14467             res = JS_VALUE_GET_OBJ(op1) == JS_VALUE_GET_OBJ(op2);
  14468         break;
  14469     case JS_TAG_INT:
  14470         d1 = JS_VALUE_GET_INT(op1);
  14471         if (tag2 == JS_TAG_INT) {
  14472             d2 = JS_VALUE_GET_INT(op2);
  14473             goto number_test;
  14474         } else if (tag2 == JS_TAG_FLOAT64) {
  14475             d2 = JS_VALUE_GET_FLOAT64(op2);
  14476             goto number_test;
  14477         } else {
  14478             res = FALSE;
  14479         }
  14480         break;
  14481     case JS_TAG_FLOAT64:
  14482         d1 = JS_VALUE_GET_FLOAT64(op1);
  14483         if (tag2 == JS_TAG_FLOAT64) {
  14484             d2 = JS_VALUE_GET_FLOAT64(op2);
  14485         } else if (tag2 == JS_TAG_INT) {
  14486             d2 = JS_VALUE_GET_INT(op2);
  14487         } else {
  14488             res = FALSE;
  14489             break;
  14490         }
  14491     number_test:
  14492         if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) {
  14493             JSFloat64Union u1, u2;
  14494             /* NaN is not always normalized, so this test is necessary */
  14495             if (isnan(d1) || isnan(d2)) {
  14496                 res = isnan(d1) == isnan(d2);
  14497             } else if (eq_mode == JS_EQ_SAME_VALUE_ZERO) {
  14498                 res = (d1 == d2); /* +0 == -0 */
  14499             } else {
  14500                 u1.d = d1;
  14501                 u2.d = d2;
  14502                 res = (u1.u64 == u2.u64); /* +0 != -0 */
  14503             }
  14504         } else {
  14505             res = (d1 == d2); /* if NaN return false and +0 == -0 */
  14506         }
  14507         goto done_no_free;
  14508     case JS_TAG_BIG_INT:
  14509         {
  14510             bf_t a_s, *a, b_s, *b;
  14511             if (tag1 != tag2) {
  14512                 res = FALSE;
  14513                 break;
  14514             }
  14515             a = JS_ToBigFloat(ctx, &a_s, op1); /* cannot fail */
  14516             b = JS_ToBigFloat(ctx, &b_s, op2); /* cannot fail */
  14517             res = bf_cmp_eq(a, b);
  14518             if (a == &a_s)
  14519                 bf_delete(a);
  14520             if (b == &b_s)
  14521                 bf_delete(b);
  14522         }
  14523         break;
  14524 #ifdef CONFIG_BIGNUM
  14525     case JS_TAG_BIG_FLOAT:
  14526         {
  14527             JSBigFloat *p1, *p2;
  14528             const bf_t *a, *b;
  14529             if (tag1 != tag2) {
  14530                 res = FALSE;
  14531                 break;
  14532             }
  14533             p1 = JS_VALUE_GET_PTR(op1);
  14534             p2 = JS_VALUE_GET_PTR(op2);
  14535             a = &p1->num;
  14536             b = &p2->num;
  14537             if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) {
  14538                 if (eq_mode == JS_EQ_SAME_VALUE_ZERO &&
  14539                            a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO) {
  14540                     res = TRUE;
  14541                 } else {
  14542                     res = (bf_cmp_full(a, b) == 0);
  14543                 }
  14544             } else {
  14545                 res = bf_cmp_eq(a, b);
  14546             }
  14547         }
  14548         break;
  14549     case JS_TAG_BIG_DECIMAL:
  14550         {
  14551             JSBigDecimal *p1, *p2;
  14552             const bfdec_t *a, *b;
  14553             if (tag1 != tag2) {
  14554                 res = FALSE;
  14555                 break;
  14556             }
  14557             p1 = JS_VALUE_GET_PTR(op1);
  14558             p2 = JS_VALUE_GET_PTR(op2);
  14559             a = &p1->num;
  14560             b = &p2->num;
  14561             res = bfdec_cmp_eq(a, b);
  14562         }
  14563         break;
  14564 #endif
  14565     default:
  14566         res = FALSE;
  14567         break;
  14568     }
  14569     JS_FreeValue(ctx, op1);
  14570     JS_FreeValue(ctx, op2);
  14571  done_no_free:
  14572     return res;
  14573 }
  14574 
  14575 static BOOL js_strict_eq(JSContext *ctx, JSValueConst op1, JSValueConst op2)
  14576 {
  14577     return js_strict_eq2(ctx,
  14578                          JS_DupValue(ctx, op1), JS_DupValue(ctx, op2),
  14579                          JS_EQ_STRICT);
  14580 }
  14581 
  14582 BOOL JS_StrictEq(JSContext *ctx, JSValueConst op1, JSValueConst op2)
  14583 {
  14584     return js_strict_eq(ctx, op1, op2);
  14585 }
  14586 
  14587 static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2)
  14588 {
  14589     return js_strict_eq2(ctx,
  14590                          JS_DupValue(ctx, op1), JS_DupValue(ctx, op2),
  14591                          JS_EQ_SAME_VALUE);
  14592 }
  14593 
  14594 BOOL JS_SameValue(JSContext *ctx, JSValueConst op1, JSValueConst op2)
  14595 {
  14596     return js_same_value(ctx, op1, op2);
  14597 }
  14598 
  14599 static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2)
  14600 {
  14601     return js_strict_eq2(ctx,
  14602                          JS_DupValue(ctx, op1), JS_DupValue(ctx, op2),
  14603                          JS_EQ_SAME_VALUE_ZERO);
  14604 }
  14605 
  14606 BOOL JS_SameValueZero(JSContext *ctx, JSValueConst op1, JSValueConst op2)
  14607 {
  14608     return js_same_value_zero(ctx, op1, op2);
  14609 }
  14610 
  14611 static no_inline int js_strict_eq_slow(JSContext *ctx, JSValue *sp,
  14612                                        BOOL is_neq)
  14613 {
  14614     BOOL res;
  14615     res = js_strict_eq2(ctx, sp[-2], sp[-1], JS_EQ_STRICT);
  14616     sp[-2] = JS_NewBool(ctx, res ^ is_neq);
  14617     return 0;
  14618 }
  14619 
  14620 static __exception int js_operator_in(JSContext *ctx, JSValue *sp)
  14621 {
  14622     JSValue op1, op2;
  14623     JSAtom atom;
  14624     int ret;
  14625 
  14626     op1 = sp[-2];
  14627     op2 = sp[-1];
  14628 
  14629     if (JS_VALUE_GET_TAG(op2) != JS_TAG_OBJECT) {
  14630         JS_ThrowTypeError(ctx, "invalid 'in' operand");
  14631         return -1;
  14632     }
  14633     atom = JS_ValueToAtom(ctx, op1);
  14634     if (unlikely(atom == JS_ATOM_NULL))
  14635         return -1;
  14636     ret = JS_HasProperty(ctx, op2, atom);
  14637     JS_FreeAtom(ctx, atom);
  14638     if (ret < 0)
  14639         return -1;
  14640     JS_FreeValue(ctx, op1);
  14641     JS_FreeValue(ctx, op2);
  14642     sp[-2] = JS_NewBool(ctx, ret);
  14643     return 0;
  14644 }
  14645 
  14646 static __exception int js_operator_private_in(JSContext *ctx, JSValue *sp)
  14647 {
  14648     JSValue op1, op2;
  14649     int ret;
  14650 
  14651     op1 = sp[-2]; /* object */
  14652     op2 = sp[-1]; /* field name or method function */
  14653 
  14654     if (JS_VALUE_GET_TAG(op1) != JS_TAG_OBJECT) {
  14655         JS_ThrowTypeError(ctx, "invalid 'in' operand");
  14656         return -1;
  14657     }
  14658     if (JS_IsObject(op2)) {
  14659         /* method: use the brand */
  14660         ret = JS_CheckBrand(ctx, op1, op2);
  14661         if (ret < 0)
  14662             return -1;
  14663     } else {
  14664         JSAtom atom;
  14665         JSObject *p;
  14666         JSShapeProperty *prs;
  14667         JSProperty *pr;
  14668         /* field */
  14669         atom = JS_ValueToAtom(ctx, op2);
  14670         if (unlikely(atom == JS_ATOM_NULL))
  14671             return -1;
  14672         p = JS_VALUE_GET_OBJ(op1);
  14673         prs = find_own_property(&pr, p, atom);
  14674         JS_FreeAtom(ctx, atom);
  14675         ret = (prs != NULL);
  14676     }
  14677     JS_FreeValue(ctx, op1);
  14678     JS_FreeValue(ctx, op2);
  14679     sp[-2] = JS_NewBool(ctx, ret);
  14680     return 0;
  14681 }
  14682 
  14683 static __exception int js_has_unscopable(JSContext *ctx, JSValueConst obj,
  14684                                          JSAtom atom)
  14685 {
  14686     JSValue arr, val;
  14687     int ret;
  14688 
  14689     arr = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_unscopables);
  14690     if (JS_IsException(arr))
  14691         return -1;
  14692     ret = 0;
  14693     if (JS_IsObject(arr)) {
  14694         val = JS_GetProperty(ctx, arr, atom);
  14695         ret = JS_ToBoolFree(ctx, val);
  14696     }
  14697     JS_FreeValue(ctx, arr);
  14698     return ret;
  14699 }
  14700 
  14701 static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp)
  14702 {
  14703     JSValue op1, op2;
  14704     BOOL ret;
  14705 
  14706     op1 = sp[-2];
  14707     op2 = sp[-1];
  14708     ret = JS_IsInstanceOf(ctx, op1, op2);
  14709     if (ret < 0)
  14710         return ret;
  14711     JS_FreeValue(ctx, op1);
  14712     JS_FreeValue(ctx, op2);
  14713     sp[-2] = JS_NewBool(ctx, ret);
  14714     return 0;
  14715 }
  14716 
  14717 static __exception int js_operator_typeof(JSContext *ctx, JSValueConst op1)
  14718 {
  14719     JSAtom atom;
  14720     uint32_t tag;
  14721 
  14722     tag = JS_VALUE_GET_NORM_TAG(op1);
  14723     switch(tag) {
  14724     case JS_TAG_BIG_INT:
  14725         atom = JS_ATOM_bigint;
  14726         break;
  14727 #ifdef CONFIG_BIGNUM
  14728     case JS_TAG_BIG_FLOAT:
  14729         atom = JS_ATOM_bigfloat;
  14730         break;
  14731     case JS_TAG_BIG_DECIMAL:
  14732         atom = JS_ATOM_bigdecimal;
  14733         break;
  14734 #endif
  14735     case JS_TAG_INT:
  14736     case JS_TAG_FLOAT64:
  14737         atom = JS_ATOM_number;
  14738         break;
  14739     case JS_TAG_UNDEFINED:
  14740         atom = JS_ATOM_undefined;
  14741         break;
  14742     case JS_TAG_BOOL:
  14743         atom = JS_ATOM_boolean;
  14744         break;
  14745     case JS_TAG_STRING:
  14746         atom = JS_ATOM_string;
  14747         break;
  14748     case JS_TAG_OBJECT:
  14749         {
  14750             JSObject *p;
  14751             p = JS_VALUE_GET_OBJ(op1);
  14752             if (unlikely(p->is_HTMLDDA))
  14753                 atom = JS_ATOM_undefined;
  14754             else if (JS_IsFunction(ctx, op1))
  14755                 atom = JS_ATOM_function;
  14756             else
  14757                 goto obj_type;
  14758         }
  14759         break;
  14760     case JS_TAG_NULL:
  14761     obj_type:
  14762         atom = JS_ATOM_object;
  14763         break;
  14764     case JS_TAG_SYMBOL:
  14765         atom = JS_ATOM_symbol;
  14766         break;
  14767     default:
  14768         atom = JS_ATOM_unknown;
  14769         break;
  14770     }
  14771     return atom;
  14772 }
  14773 
  14774 static __exception int js_operator_delete(JSContext *ctx, JSValue *sp)
  14775 {
  14776     JSValue op1, op2;
  14777     JSAtom atom;
  14778     int ret;
  14779 
  14780     op1 = sp[-2];
  14781     op2 = sp[-1];
  14782     atom = JS_ValueToAtom(ctx, op2);
  14783     if (unlikely(atom == JS_ATOM_NULL))
  14784         return -1;
  14785     ret = JS_DeleteProperty(ctx, op1, atom, JS_PROP_THROW_STRICT);
  14786     JS_FreeAtom(ctx, atom);
  14787     if (unlikely(ret < 0))
  14788         return -1;
  14789     JS_FreeValue(ctx, op1);
  14790     JS_FreeValue(ctx, op2);
  14791     sp[-2] = JS_NewBool(ctx, ret);
  14792     return 0;
  14793 }
  14794 
  14795 static JSValue js_throw_type_error(JSContext *ctx, JSValueConst this_val,
  14796                                    int argc, JSValueConst *argv)
  14797 {
  14798     return JS_ThrowTypeError(ctx, "invalid property access");
  14799 }
  14800 
  14801 /* XXX: not 100% compatible, but mozilla seems to use a similar
  14802    implementation to ensure that caller in non strict mode does not
  14803    throw (ES5 compatibility) */
  14804 static JSValue js_function_proto_caller(JSContext *ctx, JSValueConst this_val,
  14805                                         int argc, JSValueConst *argv)
  14806 {
  14807     JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);
  14808     if (!b || (b->js_mode & JS_MODE_STRICT) || !b->has_prototype) {
  14809         return js_throw_type_error(ctx, this_val, 0, NULL);
  14810     }
  14811     return JS_UNDEFINED;
  14812 }
  14813 
  14814 static JSValue js_function_proto_fileName(JSContext *ctx,
  14815                                           JSValueConst this_val)
  14816 {
  14817     JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);
  14818     if (b && b->has_debug) {
  14819         return JS_AtomToString(ctx, b->debug.filename);
  14820     }
  14821     return JS_UNDEFINED;
  14822 }
  14823 
  14824 static JSValue js_function_proto_lineNumber(JSContext *ctx,
  14825                                             JSValueConst this_val)
  14826 {
  14827     JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);
  14828     if (b && b->has_debug) {
  14829         return JS_NewInt32(ctx, b->debug.line_num);
  14830     }
  14831     return JS_UNDEFINED;
  14832 }
  14833 
  14834 static int js_arguments_define_own_property(JSContext *ctx,
  14835                                             JSValueConst this_obj,
  14836                                             JSAtom prop, JSValueConst val,
  14837                                             JSValueConst getter, JSValueConst setter, int flags)
  14838 {
  14839     JSObject *p;
  14840     uint32_t idx;
  14841     p = JS_VALUE_GET_OBJ(this_obj);
  14842     /* convert to normal array when redefining an existing numeric field */
  14843     if (p->fast_array && JS_AtomIsArrayIndex(ctx, &idx, prop) &&
  14844         idx < p->u.array.count) {
  14845         if (convert_fast_array_to_array(ctx, p))
  14846             return -1;
  14847     }
  14848     /* run the default define own property */
  14849     return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter,
  14850                              flags | JS_PROP_NO_EXOTIC);
  14851 }
  14852 
  14853 static const JSClassExoticMethods js_arguments_exotic_methods = {
  14854     .define_own_property = js_arguments_define_own_property,
  14855 };
  14856 
  14857 static JSValue js_build_arguments(JSContext *ctx, int argc, JSValueConst *argv)
  14858 {
  14859     JSValue val, *tab;
  14860     JSProperty *pr;
  14861     JSObject *p;
  14862     int i;
  14863 
  14864     val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
  14865                                  JS_CLASS_ARGUMENTS);
  14866     if (JS_IsException(val))
  14867         return val;
  14868     p = JS_VALUE_GET_OBJ(val);
  14869 
  14870     /* add the length field (cannot fail) */
  14871     pr = add_property(ctx, p, JS_ATOM_length,
  14872                       JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
  14873     pr->u.value = JS_NewInt32(ctx, argc);
  14874 
  14875     /* initialize the fast array part */
  14876     tab = NULL;
  14877     if (argc > 0) {
  14878         tab = js_malloc(ctx, sizeof(tab[0]) * argc);
  14879         if (!tab) {
  14880             JS_FreeValue(ctx, val);
  14881             return JS_EXCEPTION;
  14882         }
  14883         for(i = 0; i < argc; i++) {
  14884             tab[i] = JS_DupValue(ctx, argv[i]);
  14885         }
  14886     }
  14887     p->u.array.u.values = tab;
  14888     p->u.array.count = argc;
  14889 
  14890     JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator,
  14891                            JS_DupValue(ctx, ctx->array_proto_values),
  14892                            JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
  14893     /* add callee property to throw a TypeError in strict mode */
  14894     JS_DefineProperty(ctx, val, JS_ATOM_callee, JS_UNDEFINED,
  14895                       ctx->throw_type_error, ctx->throw_type_error,
  14896                       JS_PROP_HAS_GET | JS_PROP_HAS_SET);
  14897     return val;
  14898 }
  14899 
  14900 #define GLOBAL_VAR_OFFSET 0x40000000
  14901 #define ARGUMENT_VAR_OFFSET 0x20000000
  14902 
  14903 /* legacy arguments object: add references to the function arguments */
  14904 static JSValue js_build_mapped_arguments(JSContext *ctx, int argc,
  14905                                          JSValueConst *argv,
  14906                                          JSStackFrame *sf, int arg_count)
  14907 {
  14908     JSValue val;
  14909     JSProperty *pr;
  14910     JSObject *p;
  14911     int i;
  14912 
  14913     val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
  14914                                  JS_CLASS_MAPPED_ARGUMENTS);
  14915     if (JS_IsException(val))
  14916         return val;
  14917     p = JS_VALUE_GET_OBJ(val);
  14918 
  14919     /* add the length field (cannot fail) */
  14920     pr = add_property(ctx, p, JS_ATOM_length,
  14921                       JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
  14922     pr->u.value = JS_NewInt32(ctx, argc);
  14923 
  14924     for(i = 0; i < arg_count; i++) {
  14925         JSVarRef *var_ref;
  14926         var_ref = get_var_ref(ctx, sf, i, TRUE);
  14927         if (!var_ref)
  14928             goto fail;
  14929         pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF);
  14930         if (!pr) {
  14931             free_var_ref(ctx->rt, var_ref);
  14932             goto fail;
  14933         }
  14934         pr->u.var_ref = var_ref;
  14935     }
  14936 
  14937     /* the arguments not mapped to the arguments of the function can
  14938        be normal properties */
  14939     for(i = arg_count; i < argc; i++) {
  14940         if (JS_DefinePropertyValueUint32(ctx, val, i,
  14941                                          JS_DupValue(ctx, argv[i]),
  14942                                          JS_PROP_C_W_E) < 0)
  14943             goto fail;
  14944     }
  14945 
  14946     JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator,
  14947                            JS_DupValue(ctx, ctx->array_proto_values),
  14948                            JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
  14949     /* callee returns this function in non strict mode */
  14950     JS_DefinePropertyValue(ctx, val, JS_ATOM_callee,
  14951                            JS_DupValue(ctx, ctx->rt->current_stack_frame->cur_func),
  14952                            JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
  14953     return val;
  14954  fail:
  14955     JS_FreeValue(ctx, val);
  14956     return JS_EXCEPTION;
  14957 }
  14958 
  14959 static JSValue js_build_rest(JSContext *ctx, int first, int argc, JSValueConst *argv)
  14960 {
  14961     JSValue val;
  14962     int i, ret;
  14963 
  14964     val = JS_NewArray(ctx);
  14965     if (JS_IsException(val))
  14966         return val;
  14967     for (i = first; i < argc; i++) {
  14968         ret = JS_DefinePropertyValueUint32(ctx, val, i - first,
  14969                                            JS_DupValue(ctx, argv[i]),
  14970                                            JS_PROP_C_W_E);
  14971         if (ret < 0) {
  14972             JS_FreeValue(ctx, val);
  14973             return JS_EXCEPTION;
  14974         }
  14975     }
  14976     return val;
  14977 }
  14978 
  14979 static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj)
  14980 {
  14981     JSObject *p, *p1;
  14982     JSPropertyEnum *tab_atom;
  14983     int i;
  14984     JSValue enum_obj;
  14985     JSForInIterator *it;
  14986     uint32_t tag, tab_atom_count;
  14987 
  14988     tag = JS_VALUE_GET_TAG(obj);
  14989     if (tag != JS_TAG_OBJECT && tag != JS_TAG_NULL && tag != JS_TAG_UNDEFINED) {
  14990         obj = JS_ToObjectFree(ctx, obj);
  14991     }
  14992 
  14993     it = js_malloc(ctx, sizeof(*it));
  14994     if (!it) {
  14995         JS_FreeValue(ctx, obj);
  14996         return JS_EXCEPTION;
  14997     }
  14998     enum_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_FOR_IN_ITERATOR);
  14999     if (JS_IsException(enum_obj)) {
  15000         js_free(ctx, it);
  15001         JS_FreeValue(ctx, obj);
  15002         return JS_EXCEPTION;
  15003     }
  15004     it->is_array = FALSE;
  15005     it->obj = obj;
  15006     it->idx = 0;
  15007     it->tab_atom = NULL;
  15008     it->atom_count = 0;
  15009     it->in_prototype_chain = FALSE;
  15010     p1 = JS_VALUE_GET_OBJ(enum_obj);
  15011     p1->u.for_in_iterator = it;
  15012 
  15013     if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)
  15014         return enum_obj;
  15015 
  15016     p = JS_VALUE_GET_OBJ(obj);
  15017     if (p->fast_array) {
  15018         JSShape *sh;
  15019         JSShapeProperty *prs;
  15020         /* check that there are no enumerable normal fields */
  15021         sh = p->shape;
  15022         for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
  15023             if (prs->flags & JS_PROP_ENUMERABLE)
  15024                 goto normal_case;
  15025         }
  15026         /* for fast arrays, we only store the number of elements */
  15027         it->is_array = TRUE;
  15028         it->atom_count = p->u.array.count;
  15029     } else {
  15030     normal_case:
  15031         if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,
  15032                                            JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) {
  15033             JS_FreeValue(ctx, enum_obj);
  15034             return JS_EXCEPTION;
  15035         }
  15036         it->tab_atom = tab_atom;
  15037         it->atom_count = tab_atom_count;
  15038     }
  15039     return enum_obj;
  15040 }
  15041 
  15042 /* obj -> enum_obj */
  15043 static __exception int js_for_in_start(JSContext *ctx, JSValue *sp)
  15044 {
  15045     sp[-1] = build_for_in_iterator(ctx, sp[-1]);
  15046     if (JS_IsException(sp[-1]))
  15047         return -1;
  15048     return 0;
  15049 }
  15050 
  15051 /* return -1 if exception, 0 if slow case, 1 if the enumeration is finished */
  15052 static __exception int js_for_in_prepare_prototype_chain_enum(JSContext *ctx,
  15053                                                               JSValueConst enum_obj)
  15054 {
  15055     JSObject *p;
  15056     JSForInIterator *it;
  15057     JSPropertyEnum *tab_atom;
  15058     uint32_t tab_atom_count, i;
  15059     JSValue obj1;
  15060 
  15061     p = JS_VALUE_GET_OBJ(enum_obj);
  15062     it = p->u.for_in_iterator;
  15063 
  15064     /* check if there are enumerable properties in the prototype chain (fast path) */
  15065     obj1 = JS_DupValue(ctx, it->obj);
  15066     for(;;) {
  15067         obj1 = JS_GetPrototypeFree(ctx, obj1);
  15068         if (JS_IsNull(obj1))
  15069             break;
  15070         if (JS_IsException(obj1))
  15071             goto fail;
  15072         if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count,
  15073                                            JS_VALUE_GET_OBJ(obj1),
  15074                                            JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) {
  15075             JS_FreeValue(ctx, obj1);
  15076             goto fail;
  15077         }
  15078         js_free_prop_enum(ctx, tab_atom, tab_atom_count);
  15079         if (tab_atom_count != 0) {
  15080             JS_FreeValue(ctx, obj1);
  15081             goto slow_path;
  15082         }
  15083         /* must check for timeout to avoid infinite loop */
  15084         if (js_poll_interrupts(ctx)) {
  15085             JS_FreeValue(ctx, obj1);
  15086             goto fail;
  15087         }
  15088     }
  15089     JS_FreeValue(ctx, obj1);
  15090     return 1;
  15091 
  15092  slow_path:
  15093     /* add the visited properties, even if they are not enumerable */
  15094     if (it->is_array) {
  15095         if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count,
  15096                                            JS_VALUE_GET_OBJ(it->obj),
  15097                                            JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) {
  15098             goto fail;
  15099         }
  15100         it->is_array = FALSE;
  15101         it->tab_atom = tab_atom;
  15102         it->atom_count = tab_atom_count;
  15103     }
  15104 
  15105     for(i = 0; i < it->atom_count; i++) {
  15106         if (JS_DefinePropertyValue(ctx, enum_obj, it->tab_atom[i].atom, JS_NULL, JS_PROP_ENUMERABLE) < 0)
  15107             goto fail;
  15108     }
  15109     return 0;
  15110  fail:
  15111     return -1;
  15112 }
  15113 
  15114 /* enum_obj -> enum_obj value done */
  15115 static __exception int js_for_in_next(JSContext *ctx, JSValue *sp)
  15116 {
  15117     JSValueConst enum_obj;
  15118     JSObject *p;
  15119     JSAtom prop;
  15120     JSForInIterator *it;
  15121     JSPropertyEnum *tab_atom;
  15122     uint32_t tab_atom_count;
  15123     int ret;
  15124 
  15125     enum_obj = sp[-1];
  15126     /* fail safe */
  15127     if (JS_VALUE_GET_TAG(enum_obj) != JS_TAG_OBJECT)
  15128         goto done;
  15129     p = JS_VALUE_GET_OBJ(enum_obj);
  15130     if (p->class_id != JS_CLASS_FOR_IN_ITERATOR)
  15131         goto done;
  15132     it = p->u.for_in_iterator;
  15133 
  15134     for(;;) {
  15135         if (it->idx >= it->atom_count) {
  15136             if (JS_IsNull(it->obj) || JS_IsUndefined(it->obj))
  15137                 goto done; /* not an object */
  15138             /* no more property in the current object: look in the prototype */
  15139             if (!it->in_prototype_chain) {
  15140                 ret = js_for_in_prepare_prototype_chain_enum(ctx, enum_obj);
  15141                 if (ret < 0)
  15142                     return -1;
  15143                 if (ret)
  15144                     goto done;
  15145                 it->in_prototype_chain = TRUE;
  15146             }
  15147             it->obj = JS_GetPrototypeFree(ctx, it->obj);
  15148             if (JS_IsException(it->obj))
  15149                 return -1;
  15150             if (JS_IsNull(it->obj))
  15151                 goto done; /* no more prototype */
  15152 
  15153             /* must check for timeout to avoid infinite loop */
  15154             if (js_poll_interrupts(ctx))
  15155                 return -1;
  15156 
  15157             if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count,
  15158                                                JS_VALUE_GET_OBJ(it->obj),
  15159                                                JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) {
  15160                 return -1;
  15161             }
  15162             js_free_prop_enum(ctx, it->tab_atom, it->atom_count);
  15163             it->tab_atom = tab_atom;
  15164             it->atom_count = tab_atom_count;
  15165             it->idx = 0;
  15166         } else {
  15167             if (it->is_array) {
  15168                 prop = __JS_AtomFromUInt32(it->idx);
  15169                 it->idx++;
  15170             } else {
  15171                 BOOL is_enumerable;
  15172                 prop = it->tab_atom[it->idx].atom;
  15173                 is_enumerable = it->tab_atom[it->idx].is_enumerable;
  15174                 it->idx++;
  15175                 if (it->in_prototype_chain) {
  15176                     /* slow case: we are in the prototype chain */
  15177                     ret = JS_GetOwnPropertyInternal(ctx, NULL, JS_VALUE_GET_OBJ(enum_obj), prop);
  15178                     if (ret < 0)
  15179                         return ret;
  15180                     if (ret)
  15181                         continue; /* already visited */
  15182                     /* add to the visited property list */
  15183                     if (JS_DefinePropertyValue(ctx, enum_obj, prop, JS_NULL,
  15184                                                JS_PROP_ENUMERABLE) < 0)
  15185                         return -1;
  15186                 }
  15187                 if (!is_enumerable)
  15188                     continue;
  15189             }
  15190             /* check if the property was deleted */
  15191             ret = JS_GetOwnPropertyInternal(ctx, NULL, JS_VALUE_GET_OBJ(it->obj), prop);
  15192             if (ret < 0)
  15193                 return ret;
  15194             if (ret)
  15195                 break;
  15196         }
  15197     }
  15198     /* return the property */
  15199     sp[0] = JS_AtomToValue(ctx, prop);
  15200     sp[1] = JS_FALSE;
  15201     return 0;
  15202  done:
  15203     /* return the end */
  15204     sp[0] = JS_UNDEFINED;
  15205     sp[1] = JS_TRUE;
  15206     return 0;
  15207 }
  15208 
  15209 static JSValue JS_GetIterator2(JSContext *ctx, JSValueConst obj,
  15210                                JSValueConst method)
  15211 {
  15212     JSValue enum_obj;
  15213 
  15214     enum_obj = JS_Call(ctx, method, obj, 0, NULL);
  15215     if (JS_IsException(enum_obj))
  15216         return enum_obj;
  15217     if (!JS_IsObject(enum_obj)) {
  15218         JS_FreeValue(ctx, enum_obj);
  15219         return JS_ThrowTypeErrorNotAnObject(ctx);
  15220     }
  15221     return enum_obj;
  15222 }
  15223 
  15224 static JSValue JS_GetIterator(JSContext *ctx, JSValueConst obj, BOOL is_async)
  15225 {
  15226     JSValue method, ret, sync_iter;
  15227 
  15228     if (is_async) {
  15229         method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_asyncIterator);
  15230         if (JS_IsException(method))
  15231             return method;
  15232         if (JS_IsUndefined(method) || JS_IsNull(method)) {
  15233             method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);
  15234             if (JS_IsException(method))
  15235                 return method;
  15236             sync_iter = JS_GetIterator2(ctx, obj, method);
  15237             JS_FreeValue(ctx, method);
  15238             if (JS_IsException(sync_iter))
  15239                 return sync_iter;
  15240             ret = JS_CreateAsyncFromSyncIterator(ctx, sync_iter);
  15241             JS_FreeValue(ctx, sync_iter);
  15242             return ret;
  15243         }
  15244     } else {
  15245         method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);
  15246         if (JS_IsException(method))
  15247             return method;
  15248     }
  15249     if (!JS_IsFunction(ctx, method)) {
  15250         JS_FreeValue(ctx, method);
  15251         return JS_ThrowTypeError(ctx, "value is not iterable");
  15252     }
  15253     ret = JS_GetIterator2(ctx, obj, method);
  15254     JS_FreeValue(ctx, method);
  15255     return ret;
  15256 }
  15257 
  15258 /* return *pdone = 2 if the iterator object is not parsed */
  15259 static JSValue JS_IteratorNext2(JSContext *ctx, JSValueConst enum_obj,
  15260                                 JSValueConst method,
  15261                                 int argc, JSValueConst *argv, int *pdone)
  15262 {
  15263     JSValue obj;
  15264 
  15265     /* fast path for the built-in iterators (avoid creating the
  15266        intermediate result object) */
  15267     if (JS_IsObject(method)) {
  15268         JSObject *p = JS_VALUE_GET_OBJ(method);
  15269         if (p->class_id == JS_CLASS_C_FUNCTION &&
  15270             p->u.cfunc.cproto == JS_CFUNC_iterator_next) {
  15271             JSCFunctionType func;
  15272             JSValueConst args[1];
  15273 
  15274             /* in case the function expects one argument */
  15275             if (argc == 0) {
  15276                 args[0] = JS_UNDEFINED;
  15277                 argv = args;
  15278             }
  15279             func = p->u.cfunc.c_function;
  15280             return func.iterator_next(ctx, enum_obj, argc, argv,
  15281                                       pdone, p->u.cfunc.magic);
  15282         }
  15283     }
  15284     obj = JS_Call(ctx, method, enum_obj, argc, argv);
  15285     if (JS_IsException(obj))
  15286         goto fail;
  15287     if (!JS_IsObject(obj)) {
  15288         JS_FreeValue(ctx, obj);
  15289         JS_ThrowTypeError(ctx, "iterator must return an object");
  15290         goto fail;
  15291     }
  15292     *pdone = 2;
  15293     return obj;
  15294  fail:
  15295     *pdone = FALSE;
  15296     return JS_EXCEPTION;
  15297 }
  15298 
  15299 static JSValue JS_IteratorNext(JSContext *ctx, JSValueConst enum_obj,
  15300                                JSValueConst method,
  15301                                int argc, JSValueConst *argv, BOOL *pdone)
  15302 {
  15303     JSValue obj, value, done_val;
  15304     int done;
  15305 
  15306     obj = JS_IteratorNext2(ctx, enum_obj, method, argc, argv, &done);
  15307     if (JS_IsException(obj))
  15308         goto fail;
  15309     if (done != 2) {
  15310         *pdone = done;
  15311         return obj;
  15312     } else {
  15313         done_val = JS_GetProperty(ctx, obj, JS_ATOM_done);
  15314         if (JS_IsException(done_val))
  15315             goto fail;
  15316         *pdone = JS_ToBoolFree(ctx, done_val);
  15317         value = JS_UNDEFINED;
  15318         if (!*pdone) {
  15319             value = JS_GetProperty(ctx, obj, JS_ATOM_value);
  15320         }
  15321         JS_FreeValue(ctx, obj);
  15322         return value;
  15323     }
  15324  fail:
  15325     JS_FreeValue(ctx, obj);
  15326     *pdone = FALSE;
  15327     return JS_EXCEPTION;
  15328 }
  15329 
  15330 /* return < 0 in case of exception */
  15331 static int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj,
  15332                             BOOL is_exception_pending)
  15333 {
  15334     JSValue method, ret, ex_obj;
  15335     int res;
  15336 
  15337     if (is_exception_pending) {
  15338         ex_obj = ctx->rt->current_exception;
  15339         ctx->rt->current_exception = JS_UNINITIALIZED;
  15340         res = -1;
  15341     } else {
  15342         ex_obj = JS_UNDEFINED;
  15343         res = 0;
  15344     }
  15345     method = JS_GetProperty(ctx, enum_obj, JS_ATOM_return);
  15346     if (JS_IsException(method)) {
  15347         res = -1;
  15348         goto done;
  15349     }
  15350     if (JS_IsUndefined(method) || JS_IsNull(method)) {
  15351         goto done;
  15352     }
  15353     ret = JS_CallFree(ctx, method, enum_obj, 0, NULL);
  15354     if (!is_exception_pending) {
  15355         if (JS_IsException(ret)) {
  15356             res = -1;
  15357         } else if (!JS_IsObject(ret)) {
  15358             JS_ThrowTypeErrorNotAnObject(ctx);
  15359             res = -1;
  15360         }
  15361     }
  15362     JS_FreeValue(ctx, ret);
  15363  done:
  15364     if (is_exception_pending) {
  15365         JS_Throw(ctx, ex_obj);
  15366     }
  15367     return res;
  15368 }
  15369 
  15370 /* obj -> enum_rec (3 slots) */
  15371 static __exception int js_for_of_start(JSContext *ctx, JSValue *sp,
  15372                                        BOOL is_async)
  15373 {
  15374     JSValue op1, obj, method;
  15375     op1 = sp[-1];
  15376     obj = JS_GetIterator(ctx, op1, is_async);
  15377     if (JS_IsException(obj))
  15378         return -1;
  15379     JS_FreeValue(ctx, op1);
  15380     sp[-1] = obj;
  15381     method = JS_GetProperty(ctx, obj, JS_ATOM_next);
  15382     if (JS_IsException(method))
  15383         return -1;
  15384     sp[0] = method;
  15385     return 0;
  15386 }
  15387 
  15388 /* enum_rec [objs] -> enum_rec [objs] value done. There are 'offset'
  15389    objs. If 'done' is true or in case of exception, 'enum_rec' is set
  15390    to undefined. If 'done' is true, 'value' is always set to
  15391    undefined. */
  15392 static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset)
  15393 {
  15394     JSValue value = JS_UNDEFINED;
  15395     int done = 1;
  15396 
  15397     if (likely(!JS_IsUndefined(sp[offset]))) {
  15398         value = JS_IteratorNext(ctx, sp[offset], sp[offset + 1], 0, NULL, &done);
  15399         if (JS_IsException(value))
  15400             done = -1;
  15401         if (done) {
  15402             /* value is JS_UNDEFINED or JS_EXCEPTION */
  15403             /* replace the iteration object with undefined */
  15404             JS_FreeValue(ctx, sp[offset]);
  15405             sp[offset] = JS_UNDEFINED;
  15406             if (done < 0) {
  15407                 return -1;
  15408             } else {
  15409                 JS_FreeValue(ctx, value);
  15410                 value = JS_UNDEFINED;
  15411             }
  15412         }
  15413     }
  15414     sp[0] = value;
  15415     sp[1] = JS_NewBool(ctx, done);
  15416     return 0;
  15417 }
  15418 
  15419 static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValueConst obj,
  15420                                            BOOL *pdone)
  15421 {
  15422     JSValue done_val, value;
  15423     BOOL done;
  15424     done_val = JS_GetProperty(ctx, obj, JS_ATOM_done);
  15425     if (JS_IsException(done_val))
  15426         goto fail;
  15427     done = JS_ToBoolFree(ctx, done_val);
  15428     value = JS_GetProperty(ctx, obj, JS_ATOM_value);
  15429     if (JS_IsException(value))
  15430         goto fail;
  15431     *pdone = done;
  15432     return value;
  15433  fail:
  15434     *pdone = FALSE;
  15435     return JS_EXCEPTION;
  15436 }
  15437 
  15438 static __exception int js_iterator_get_value_done(JSContext *ctx, JSValue *sp)
  15439 {
  15440     JSValue obj, value;
  15441     BOOL done;
  15442     obj = sp[-1];
  15443     if (!JS_IsObject(obj)) {
  15444         JS_ThrowTypeError(ctx, "iterator must return an object");
  15445         return -1;
  15446     }
  15447     value = JS_IteratorGetCompleteValue(ctx, obj, &done);
  15448     if (JS_IsException(value))
  15449         return -1;
  15450     JS_FreeValue(ctx, obj);
  15451     sp[-1] = value;
  15452     sp[0] = JS_NewBool(ctx, done);
  15453     return 0;
  15454 }
  15455 
  15456 static JSValue js_create_iterator_result(JSContext *ctx,
  15457                                          JSValue val,
  15458                                          BOOL done)
  15459 {
  15460     JSValue obj;
  15461     obj = JS_NewObject(ctx);
  15462     if (JS_IsException(obj)) {
  15463         JS_FreeValue(ctx, val);
  15464         return obj;
  15465     }
  15466     if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_value,
  15467                                val, JS_PROP_C_W_E) < 0) {
  15468         goto fail;
  15469     }
  15470     if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_done,
  15471                                JS_NewBool(ctx, done), JS_PROP_C_W_E) < 0) {
  15472     fail:
  15473         JS_FreeValue(ctx, obj);
  15474         return JS_EXCEPTION;
  15475     }
  15476     return obj;
  15477 }
  15478 
  15479 static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val,
  15480                                       int argc, JSValueConst *argv,
  15481                                       BOOL *pdone, int magic);
  15482 
  15483 static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val,
  15484                                         int argc, JSValueConst *argv, int magic);
  15485 
  15486 static BOOL js_is_fast_array(JSContext *ctx, JSValueConst obj)
  15487 {
  15488     /* Try and handle fast arrays explicitly */
  15489     if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
  15490         JSObject *p = JS_VALUE_GET_OBJ(obj);
  15491         if (p->class_id == JS_CLASS_ARRAY && p->fast_array) {
  15492             return TRUE;
  15493         }
  15494     }
  15495     return FALSE;
  15496 }
  15497 
  15498 /* Access an Array's internal JSValue array if available */
  15499 static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj,
  15500                               JSValue **arrpp, uint32_t *countp)
  15501 {
  15502     /* Try and handle fast arrays explicitly */
  15503     if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
  15504         JSObject *p = JS_VALUE_GET_OBJ(obj);
  15505         if (p->class_id == JS_CLASS_ARRAY && p->fast_array) {
  15506             *countp = p->u.array.count;
  15507             *arrpp = p->u.array.u.values;
  15508             return TRUE;
  15509         }
  15510     }
  15511     return FALSE;
  15512 }
  15513 
  15514 static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp)
  15515 {
  15516     JSValue iterator, enumobj, method, value;
  15517     int is_array_iterator;
  15518     JSValue *arrp;
  15519     uint32_t i, count32, pos;
  15520 
  15521     if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) {
  15522         JS_ThrowInternalError(ctx, "invalid index for append");
  15523         return -1;
  15524     }
  15525 
  15526     pos = JS_VALUE_GET_INT(sp[-2]);
  15527 
  15528     /* XXX: further optimisations:
  15529        - use ctx->array_proto_values?
  15530        - check if array_iterator_prototype next method is built-in and
  15531          avoid constructing actual iterator object?
  15532        - build this into js_for_of_start and use in all `for (x of o)` loops
  15533      */
  15534     iterator = JS_GetProperty(ctx, sp[-1], JS_ATOM_Symbol_iterator);
  15535     if (JS_IsException(iterator))
  15536         return -1;
  15537     is_array_iterator = JS_IsCFunction(ctx, iterator,
  15538                                        (JSCFunction *)js_create_array_iterator,
  15539                                        JS_ITERATOR_KIND_VALUE);
  15540     JS_FreeValue(ctx, iterator);
  15541 
  15542     enumobj = JS_GetIterator(ctx, sp[-1], FALSE);
  15543     if (JS_IsException(enumobj))
  15544         return -1;
  15545     method = JS_GetProperty(ctx, enumobj, JS_ATOM_next);
  15546     if (JS_IsException(method)) {
  15547         JS_FreeValue(ctx, enumobj);
  15548         return -1;
  15549     }
  15550     if (is_array_iterator
  15551     &&  JS_IsCFunction(ctx, method, (JSCFunction *)js_array_iterator_next, 0)
  15552     &&  js_get_fast_array(ctx, sp[-1], &arrp, &count32)) {
  15553         uint32_t len;
  15554         if (js_get_length32(ctx, &len, sp[-1]))
  15555             goto exception;
  15556         /* if len > count32, the elements >= count32 might be read in
  15557            the prototypes and might have side effects */
  15558         if (len != count32)
  15559             goto general_case;
  15560         /* Handle fast arrays explicitly */
  15561         for (i = 0; i < count32; i++) {
  15562             if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++,
  15563                                              JS_DupValue(ctx, arrp[i]), JS_PROP_C_W_E) < 0)
  15564                 goto exception;
  15565         }
  15566     } else {
  15567     general_case:
  15568         for (;;) {
  15569             BOOL done;
  15570             value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done);
  15571             if (JS_IsException(value))
  15572                 goto exception;
  15573             if (done) {
  15574                 /* value is JS_UNDEFINED */
  15575                 break;
  15576             }
  15577             if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, value, JS_PROP_C_W_E) < 0)
  15578                 goto exception;
  15579         }
  15580     }
  15581     /* Note: could raise an error if too many elements */
  15582     sp[-2] = JS_NewInt32(ctx, pos);
  15583     JS_FreeValue(ctx, enumobj);
  15584     JS_FreeValue(ctx, method);
  15585     return 0;
  15586 
  15587 exception:
  15588     JS_IteratorClose(ctx, enumobj, TRUE);
  15589     JS_FreeValue(ctx, enumobj);
  15590     JS_FreeValue(ctx, method);
  15591     return -1;
  15592 }
  15593 
  15594 static __exception int JS_CopyDataProperties(JSContext *ctx,
  15595                                              JSValueConst target,
  15596                                              JSValueConst source,
  15597                                              JSValueConst excluded,
  15598                                              BOOL setprop)
  15599 {
  15600     JSPropertyEnum *tab_atom;
  15601     JSValue val;
  15602     uint32_t i, tab_atom_count;
  15603     JSObject *p;
  15604     JSObject *pexcl = NULL;
  15605     int ret, gpn_flags;
  15606     JSPropertyDescriptor desc;
  15607     BOOL is_enumerable;
  15608 
  15609     if (JS_VALUE_GET_TAG(source) != JS_TAG_OBJECT)
  15610         return 0;
  15611 
  15612     if (JS_VALUE_GET_TAG(excluded) == JS_TAG_OBJECT)
  15613         pexcl = JS_VALUE_GET_OBJ(excluded);
  15614 
  15615     p = JS_VALUE_GET_OBJ(source);
  15616 
  15617     gpn_flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK | JS_GPN_ENUM_ONLY;
  15618     if (p->is_exotic) {
  15619         const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
  15620         /* cannot use JS_GPN_ENUM_ONLY with e.g. proxies because it
  15621            introduces a visible change */
  15622         if (em && em->get_own_property_names) {
  15623             gpn_flags &= ~JS_GPN_ENUM_ONLY;
  15624         }
  15625     }
  15626     if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,
  15627                                        gpn_flags))
  15628         return -1;
  15629 
  15630     for (i = 0; i < tab_atom_count; i++) {
  15631         if (pexcl) {
  15632             ret = JS_GetOwnPropertyInternal(ctx, NULL, pexcl, tab_atom[i].atom);
  15633             if (ret) {
  15634                 if (ret < 0)
  15635                     goto exception;
  15636                 continue;
  15637             }
  15638         }
  15639         if (!(gpn_flags & JS_GPN_ENUM_ONLY)) {
  15640             /* test if the property is enumerable */
  15641             ret = JS_GetOwnPropertyInternal(ctx, &desc, p, tab_atom[i].atom);
  15642             if (ret < 0)
  15643                 goto exception;
  15644             if (!ret)
  15645                 continue;
  15646             is_enumerable = (desc.flags & JS_PROP_ENUMERABLE) != 0;
  15647             js_free_desc(ctx, &desc);
  15648             if (!is_enumerable)
  15649                 continue;
  15650         }
  15651         val = JS_GetProperty(ctx, source, tab_atom[i].atom);
  15652         if (JS_IsException(val))
  15653             goto exception;
  15654         if (setprop)
  15655             ret = JS_SetProperty(ctx, target, tab_atom[i].atom, val);
  15656         else
  15657             ret = JS_DefinePropertyValue(ctx, target, tab_atom[i].atom, val,
  15658                                          JS_PROP_C_W_E);
  15659         if (ret < 0)
  15660             goto exception;
  15661     }
  15662     js_free_prop_enum(ctx, tab_atom, tab_atom_count);
  15663     return 0;
  15664  exception:
  15665     js_free_prop_enum(ctx, tab_atom, tab_atom_count);
  15666     return -1;
  15667 }
  15668 
  15669 /* only valid inside C functions */
  15670 static JSValueConst JS_GetActiveFunction(JSContext *ctx)
  15671 {
  15672     return ctx->rt->current_stack_frame->cur_func;
  15673 }
  15674 
  15675 static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf,
  15676                              int var_idx, BOOL is_arg)
  15677 {
  15678     JSVarRef *var_ref;
  15679     struct list_head *el;
  15680 
  15681     list_for_each(el, &sf->var_ref_list) {
  15682         var_ref = list_entry(el, JSVarRef, var_ref_link);
  15683         if (var_ref->var_idx == var_idx && var_ref->is_arg == is_arg) {
  15684             var_ref->header.ref_count++;
  15685             return var_ref;
  15686         }
  15687     }
  15688     /* create a new one */
  15689     var_ref = js_malloc(ctx, sizeof(JSVarRef));
  15690     if (!var_ref)
  15691         return NULL;
  15692     var_ref->header.ref_count = 1;
  15693     add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
  15694     var_ref->is_detached = FALSE;
  15695     var_ref->is_arg = is_arg;
  15696     var_ref->var_idx = var_idx;
  15697     list_add_tail(&var_ref->var_ref_link, &sf->var_ref_list);
  15698     if (sf->js_mode & JS_MODE_ASYNC) {
  15699         /* The stack frame is detached and may be destroyed at any
  15700            time so its reference count must be increased. Calling
  15701            close_var_refs() when destroying the stack frame is not
  15702            possible because it would change the graph between the GC
  15703            objects. Another solution could be to temporarily detach
  15704            the JSVarRef of async functions during the GC. It would
  15705            have the advantage of allowing the release of unused stack
  15706            frames in a cycle. */
  15707         var_ref->async_func = container_of(sf, JSAsyncFunctionState, frame);
  15708         var_ref->async_func->header.ref_count++;
  15709     } else {
  15710         var_ref->async_func = NULL;
  15711     }
  15712     if (is_arg)
  15713         var_ref->pvalue = &sf->arg_buf[var_idx];
  15714     else
  15715         var_ref->pvalue = &sf->var_buf[var_idx];
  15716     return var_ref;
  15717 }
  15718 
  15719 static JSValue js_closure2(JSContext *ctx, JSValue func_obj,
  15720                            JSFunctionBytecode *b,
  15721                            JSVarRef **cur_var_refs,
  15722                            JSStackFrame *sf)
  15723 {
  15724     JSObject *p;
  15725     JSVarRef **var_refs;
  15726     int i;
  15727 
  15728     p = JS_VALUE_GET_OBJ(func_obj);
  15729     p->u.func.function_bytecode = b;
  15730     p->u.func.home_object = NULL;
  15731     p->u.func.var_refs = NULL;
  15732     if (b->closure_var_count) {
  15733         var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count);
  15734         if (!var_refs)
  15735             goto fail;
  15736         p->u.func.var_refs = var_refs;
  15737         for(i = 0; i < b->closure_var_count; i++) {
  15738             JSClosureVar *cv = &b->closure_var[i];
  15739             JSVarRef *var_ref;
  15740             if (cv->is_local) {
  15741                 /* reuse the existing variable reference if it already exists */
  15742                 var_ref = get_var_ref(ctx, sf, cv->var_idx, cv->is_arg);
  15743                 if (!var_ref)
  15744                     goto fail;
  15745             } else {
  15746                 var_ref = cur_var_refs[cv->var_idx];
  15747                 var_ref->header.ref_count++;
  15748             }
  15749             var_refs[i] = var_ref;
  15750         }
  15751     }
  15752     return func_obj;
  15753  fail:
  15754     /* bfunc is freed when func_obj is freed */
  15755     JS_FreeValue(ctx, func_obj);
  15756     return JS_EXCEPTION;
  15757 }
  15758 
  15759 static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque)
  15760 {
  15761     JSValue obj, this_val;
  15762     int ret;
  15763 
  15764     this_val = JS_MKPTR(JS_TAG_OBJECT, p);
  15765     obj = JS_NewObject(ctx);
  15766     if (JS_IsException(obj))
  15767         return JS_EXCEPTION;
  15768     set_cycle_flag(ctx, obj);
  15769     set_cycle_flag(ctx, this_val);
  15770     ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_constructor,
  15771                                  JS_DupValue(ctx, this_val),
  15772                                  JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
  15773     if (ret < 0) {
  15774         JS_FreeValue(ctx, obj);
  15775         return JS_EXCEPTION;
  15776     }
  15777     return obj;
  15778 }
  15779 
  15780 static const uint16_t func_kind_to_class_id[] = {
  15781     [JS_FUNC_NORMAL] = JS_CLASS_BYTECODE_FUNCTION,
  15782     [JS_FUNC_GENERATOR] = JS_CLASS_GENERATOR_FUNCTION,
  15783     [JS_FUNC_ASYNC] = JS_CLASS_ASYNC_FUNCTION,
  15784     [JS_FUNC_ASYNC_GENERATOR] = JS_CLASS_ASYNC_GENERATOR_FUNCTION,
  15785 };
  15786 
  15787 static JSValue js_closure(JSContext *ctx, JSValue bfunc,
  15788                           JSVarRef **cur_var_refs,
  15789                           JSStackFrame *sf)
  15790 {
  15791     JSFunctionBytecode *b;
  15792     JSValue func_obj;
  15793     JSAtom name_atom;
  15794 
  15795     b = JS_VALUE_GET_PTR(bfunc);
  15796     func_obj = JS_NewObjectClass(ctx, func_kind_to_class_id[b->func_kind]);
  15797     if (JS_IsException(func_obj)) {
  15798         JS_FreeValue(ctx, bfunc);
  15799         return JS_EXCEPTION;
  15800     }
  15801     func_obj = js_closure2(ctx, func_obj, b, cur_var_refs, sf);
  15802     if (JS_IsException(func_obj)) {
  15803         /* bfunc has been freed */
  15804         goto fail;
  15805     }
  15806     name_atom = b->func_name;
  15807     if (name_atom == JS_ATOM_NULL)
  15808         name_atom = JS_ATOM_empty_string;
  15809     js_function_set_properties(ctx, func_obj, name_atom,
  15810                                b->defined_arg_count);
  15811 
  15812     if (b->func_kind & JS_FUNC_GENERATOR) {
  15813         JSValue proto;
  15814         int proto_class_id;
  15815         /* generators have a prototype field which is used as
  15816            prototype for the generator object */
  15817         if (b->func_kind == JS_FUNC_ASYNC_GENERATOR)
  15818             proto_class_id = JS_CLASS_ASYNC_GENERATOR;
  15819         else
  15820             proto_class_id = JS_CLASS_GENERATOR;
  15821         proto = JS_NewObjectProto(ctx, ctx->class_proto[proto_class_id]);
  15822         if (JS_IsException(proto))
  15823             goto fail;
  15824         JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, proto,
  15825                                JS_PROP_WRITABLE);
  15826     } else if (b->has_prototype) {
  15827         /* add the 'prototype' property: delay instantiation to avoid
  15828            creating cycles for every javascript function. The prototype
  15829            object is created on the fly when first accessed */
  15830         JS_SetConstructorBit(ctx, func_obj, TRUE);
  15831         JS_DefineAutoInitProperty(ctx, func_obj, JS_ATOM_prototype,
  15832                                   JS_AUTOINIT_ID_PROTOTYPE, NULL,
  15833                                   JS_PROP_WRITABLE);
  15834     }
  15835     return func_obj;
  15836  fail:
  15837     /* bfunc is freed when func_obj is freed */
  15838     JS_FreeValue(ctx, func_obj);
  15839     return JS_EXCEPTION;
  15840 }
  15841 
  15842 #define JS_DEFINE_CLASS_HAS_HERITAGE     (1 << 0)
  15843 
  15844 static int js_op_define_class(JSContext *ctx, JSValue *sp,
  15845                               JSAtom class_name, int class_flags,
  15846                               JSVarRef **cur_var_refs,
  15847                               JSStackFrame *sf, BOOL is_computed_name)
  15848 {
  15849     JSValue bfunc, parent_class, proto = JS_UNDEFINED;
  15850     JSValue ctor = JS_UNDEFINED, parent_proto = JS_UNDEFINED;
  15851     JSFunctionBytecode *b;
  15852 
  15853     parent_class = sp[-2];
  15854     bfunc = sp[-1];
  15855 
  15856     if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) {
  15857         if (JS_IsNull(parent_class)) {
  15858             parent_proto = JS_NULL;
  15859             parent_class = JS_DupValue(ctx, ctx->function_proto);
  15860         } else {
  15861             if (!JS_IsConstructor(ctx, parent_class)) {
  15862                 JS_ThrowTypeError(ctx, "parent class must be constructor");
  15863                 goto fail;
  15864             }
  15865             parent_proto = JS_GetProperty(ctx, parent_class, JS_ATOM_prototype);
  15866             if (JS_IsException(parent_proto))
  15867                 goto fail;
  15868             if (!JS_IsNull(parent_proto) && !JS_IsObject(parent_proto)) {
  15869                 JS_ThrowTypeError(ctx, "parent prototype must be an object or null");
  15870                 goto fail;
  15871             }
  15872         }
  15873     } else {
  15874         /* parent_class is JS_UNDEFINED in this case */
  15875         parent_proto = JS_DupValue(ctx, ctx->class_proto[JS_CLASS_OBJECT]);
  15876         parent_class = JS_DupValue(ctx, ctx->function_proto);
  15877     }
  15878     proto = JS_NewObjectProto(ctx, parent_proto);
  15879     if (JS_IsException(proto))
  15880         goto fail;
  15881 
  15882     b = JS_VALUE_GET_PTR(bfunc);
  15883     assert(b->func_kind == JS_FUNC_NORMAL);
  15884     ctor = JS_NewObjectProtoClass(ctx, parent_class,
  15885                                   JS_CLASS_BYTECODE_FUNCTION);
  15886     if (JS_IsException(ctor))
  15887         goto fail;
  15888     ctor = js_closure2(ctx, ctor, b, cur_var_refs, sf);
  15889     bfunc = JS_UNDEFINED;
  15890     if (JS_IsException(ctor))
  15891         goto fail;
  15892     js_method_set_home_object(ctx, ctor, proto);
  15893     JS_SetConstructorBit(ctx, ctor, TRUE);
  15894 
  15895     JS_DefinePropertyValue(ctx, ctor, JS_ATOM_length,
  15896                            JS_NewInt32(ctx, b->defined_arg_count),
  15897                            JS_PROP_CONFIGURABLE);
  15898 
  15899     if (is_computed_name) {
  15900         if (JS_DefineObjectNameComputed(ctx, ctor, sp[-3],
  15901                                         JS_PROP_CONFIGURABLE) < 0)
  15902             goto fail;
  15903     } else {
  15904         if (JS_DefineObjectName(ctx, ctor, class_name, JS_PROP_CONFIGURABLE) < 0)
  15905             goto fail;
  15906     }
  15907 
  15908     /* the constructor property must be first. It can be overriden by
  15909        computed property names */
  15910     if (JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor,
  15911                                JS_DupValue(ctx, ctor),
  15912                                JS_PROP_CONFIGURABLE |
  15913                                JS_PROP_WRITABLE | JS_PROP_THROW) < 0)
  15914         goto fail;
  15915     /* set the prototype property */
  15916     if (JS_DefinePropertyValue(ctx, ctor, JS_ATOM_prototype,
  15917                                JS_DupValue(ctx, proto), JS_PROP_THROW) < 0)
  15918         goto fail;
  15919     set_cycle_flag(ctx, ctor);
  15920     set_cycle_flag(ctx, proto);
  15921 
  15922     JS_FreeValue(ctx, parent_proto);
  15923     JS_FreeValue(ctx, parent_class);
  15924 
  15925     sp[-2] = ctor;
  15926     sp[-1] = proto;
  15927     return 0;
  15928  fail:
  15929     JS_FreeValue(ctx, parent_class);
  15930     JS_FreeValue(ctx, parent_proto);
  15931     JS_FreeValue(ctx, bfunc);
  15932     JS_FreeValue(ctx, proto);
  15933     JS_FreeValue(ctx, ctor);
  15934     sp[-2] = JS_UNDEFINED;
  15935     sp[-1] = JS_UNDEFINED;
  15936     return -1;
  15937 }
  15938 
  15939 static void close_var_refs(JSRuntime *rt, JSStackFrame *sf)
  15940 {
  15941     struct list_head *el, *el1;
  15942     JSVarRef *var_ref;
  15943     int var_idx;
  15944 
  15945     list_for_each_safe(el, el1, &sf->var_ref_list) {
  15946         var_ref = list_entry(el, JSVarRef, var_ref_link);
  15947         /* no need to unlink var_ref->var_ref_link as the list is never used afterwards */
  15948         if (var_ref->async_func)
  15949             async_func_free(rt, var_ref->async_func);
  15950         var_idx = var_ref->var_idx;
  15951         if (var_ref->is_arg)
  15952             var_ref->value = JS_DupValueRT(rt, sf->arg_buf[var_idx]);
  15953         else
  15954             var_ref->value = JS_DupValueRT(rt, sf->var_buf[var_idx]);
  15955         var_ref->pvalue = &var_ref->value;
  15956         /* the reference is no longer to a local variable */
  15957         var_ref->is_detached = TRUE;
  15958     }
  15959 }
  15960 
  15961 static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int idx, int is_arg)
  15962 {
  15963     struct list_head *el, *el1;
  15964     JSVarRef *var_ref;
  15965     int var_idx = idx;
  15966 
  15967     list_for_each_safe(el, el1, &sf->var_ref_list) {
  15968         var_ref = list_entry(el, JSVarRef, var_ref_link);
  15969         if (var_idx == var_ref->var_idx && var_ref->is_arg == is_arg) {
  15970             list_del(&var_ref->var_ref_link);
  15971             if (var_ref->async_func)
  15972                 async_func_free(ctx->rt, var_ref->async_func);
  15973             var_ref->value = JS_DupValue(ctx, sf->var_buf[var_idx]);
  15974             var_ref->pvalue = &var_ref->value;
  15975             /* the reference is no longer to a local variable */
  15976             var_ref->is_detached = TRUE;
  15977         }
  15978     }
  15979 }
  15980 
  15981 #define JS_CALL_FLAG_COPY_ARGV   (1 << 1)
  15982 #define JS_CALL_FLAG_GENERATOR   (1 << 2)
  15983 
  15984 static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,
  15985                                   JSValueConst this_obj,
  15986                                   int argc, JSValueConst *argv, int flags)
  15987 {
  15988     JSRuntime *rt = ctx->rt;
  15989     JSCFunctionType func;
  15990     JSObject *p;
  15991     JSStackFrame sf_s, *sf = &sf_s, *prev_sf;
  15992     JSValue ret_val;
  15993     JSValueConst *arg_buf;
  15994     int arg_count, i;
  15995     JSCFunctionEnum cproto;
  15996 
  15997     p = JS_VALUE_GET_OBJ(func_obj);
  15998     cproto = p->u.cfunc.cproto;
  15999     arg_count = p->u.cfunc.length;
  16000 
  16001     /* better to always check stack overflow */
  16002     if (js_check_stack_overflow(rt, sizeof(arg_buf[0]) * arg_count))
  16003         return JS_ThrowStackOverflow(ctx);
  16004 
  16005     prev_sf = rt->current_stack_frame;
  16006     sf->prev_frame = prev_sf;
  16007     rt->current_stack_frame = sf;
  16008     ctx = p->u.cfunc.realm; /* change the current realm */
  16009 
  16010 #ifdef CONFIG_BIGNUM
  16011     /* we only propagate the bignum mode as some runtime functions
  16012        test it */
  16013     if (prev_sf)
  16014         sf->js_mode = prev_sf->js_mode & JS_MODE_MATH;
  16015     else
  16016         sf->js_mode = 0;
  16017 #else
  16018     sf->js_mode = 0;
  16019 #endif
  16020     sf->cur_func = (JSValue)func_obj;
  16021     sf->arg_count = argc;
  16022     arg_buf = argv;
  16023 
  16024     if (unlikely(argc < arg_count)) {
  16025         /* ensure that at least argc_count arguments are readable */
  16026         arg_buf = alloca(sizeof(arg_buf[0]) * arg_count);
  16027         for(i = 0; i < argc; i++)
  16028             arg_buf[i] = argv[i];
  16029         for(i = argc; i < arg_count; i++)
  16030             arg_buf[i] = JS_UNDEFINED;
  16031         sf->arg_count = arg_count;
  16032     }
  16033     sf->arg_buf = (JSValue*)arg_buf;
  16034 
  16035     func = p->u.cfunc.c_function;
  16036     switch(cproto) {
  16037     case JS_CFUNC_constructor:
  16038     case JS_CFUNC_constructor_or_func:
  16039         if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) {
  16040             if (cproto == JS_CFUNC_constructor) {
  16041             not_a_constructor:
  16042                 ret_val = JS_ThrowTypeError(ctx, "must be called with new");
  16043                 break;
  16044             } else {
  16045                 this_obj = JS_UNDEFINED;
  16046             }
  16047         }
  16048         /* here this_obj is new_target */
  16049         /* fall thru */
  16050     case JS_CFUNC_generic:
  16051         ret_val = func.generic(ctx, this_obj, argc, arg_buf);
  16052         break;
  16053     case JS_CFUNC_constructor_magic:
  16054     case JS_CFUNC_constructor_or_func_magic:
  16055         if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) {
  16056             if (cproto == JS_CFUNC_constructor_magic) {
  16057                 goto not_a_constructor;
  16058             } else {
  16059                 this_obj = JS_UNDEFINED;
  16060             }
  16061         }
  16062         /* fall thru */
  16063     case JS_CFUNC_generic_magic:
  16064         ret_val = func.generic_magic(ctx, this_obj, argc, arg_buf,
  16065                                      p->u.cfunc.magic);
  16066         break;
  16067     case JS_CFUNC_getter:
  16068         ret_val = func.getter(ctx, this_obj);
  16069         break;
  16070     case JS_CFUNC_setter:
  16071         ret_val = func.setter(ctx, this_obj, arg_buf[0]);
  16072         break;
  16073     case JS_CFUNC_getter_magic:
  16074         ret_val = func.getter_magic(ctx, this_obj, p->u.cfunc.magic);
  16075         break;
  16076     case JS_CFUNC_setter_magic:
  16077         ret_val = func.setter_magic(ctx, this_obj, arg_buf[0], p->u.cfunc.magic);
  16078         break;
  16079     case JS_CFUNC_f_f:
  16080         {
  16081             double d1;
  16082 
  16083             if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) {
  16084                 ret_val = JS_EXCEPTION;
  16085                 break;
  16086             }
  16087             ret_val = JS_NewFloat64(ctx, func.f_f(d1));
  16088         }
  16089         break;
  16090     case JS_CFUNC_f_f_f:
  16091         {
  16092             double d1, d2;
  16093 
  16094             if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) {
  16095                 ret_val = JS_EXCEPTION;
  16096                 break;
  16097             }
  16098             if (unlikely(JS_ToFloat64(ctx, &d2, arg_buf[1]))) {
  16099                 ret_val = JS_EXCEPTION;
  16100                 break;
  16101             }
  16102             ret_val = JS_NewFloat64(ctx, func.f_f_f(d1, d2));
  16103         }
  16104         break;
  16105     case JS_CFUNC_iterator_next:
  16106         {
  16107             int done;
  16108             ret_val = func.iterator_next(ctx, this_obj, argc, arg_buf,
  16109                                          &done, p->u.cfunc.magic);
  16110             if (!JS_IsException(ret_val) && done != 2) {
  16111                 ret_val = js_create_iterator_result(ctx, ret_val, done);
  16112             }
  16113         }
  16114         break;
  16115     default:
  16116         abort();
  16117     }
  16118 
  16119     rt->current_stack_frame = sf->prev_frame;
  16120     return ret_val;
  16121 }
  16122 
  16123 static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj,
  16124                                       JSValueConst this_obj,
  16125                                       int argc, JSValueConst *argv, int flags)
  16126 {
  16127     JSObject *p;
  16128     JSBoundFunction *bf;
  16129     JSValueConst *arg_buf, new_target;
  16130     int arg_count, i;
  16131 
  16132     p = JS_VALUE_GET_OBJ(func_obj);
  16133     bf = p->u.bound_function;
  16134     arg_count = bf->argc + argc;
  16135     if (js_check_stack_overflow(ctx->rt, sizeof(JSValue) * arg_count))
  16136         return JS_ThrowStackOverflow(ctx);
  16137     arg_buf = alloca(sizeof(JSValue) * arg_count);
  16138     for(i = 0; i < bf->argc; i++) {
  16139         arg_buf[i] = bf->argv[i];
  16140     }
  16141     for(i = 0; i < argc; i++) {
  16142         arg_buf[bf->argc + i] = argv[i];
  16143     }
  16144     if (flags & JS_CALL_FLAG_CONSTRUCTOR) {
  16145         new_target = this_obj;
  16146         if (js_same_value(ctx, func_obj, new_target))
  16147             new_target = bf->func_obj;
  16148         return JS_CallConstructor2(ctx, bf->func_obj, new_target,
  16149                                    arg_count, arg_buf);
  16150     } else {
  16151         return JS_Call(ctx, bf->func_obj, bf->this_val,
  16152                        arg_count, arg_buf);
  16153     }
  16154 }
  16155 
  16156 /* argument of OP_special_object */
  16157 typedef enum {
  16158     OP_SPECIAL_OBJECT_ARGUMENTS,
  16159     OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS,
  16160     OP_SPECIAL_OBJECT_THIS_FUNC,
  16161     OP_SPECIAL_OBJECT_NEW_TARGET,
  16162     OP_SPECIAL_OBJECT_HOME_OBJECT,
  16163     OP_SPECIAL_OBJECT_VAR_OBJECT,
  16164     OP_SPECIAL_OBJECT_IMPORT_META,
  16165 } OPSpecialObjectEnum;
  16166 
  16167 #define FUNC_RET_AWAIT         0
  16168 #define FUNC_RET_YIELD         1
  16169 #define FUNC_RET_YIELD_STAR    2
  16170 #define FUNC_RET_INITIAL_YIELD 3
  16171 
  16172 /* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */
  16173 static JSValue __JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
  16174                                JSValueConst this_obj, JSValueConst new_target,
  16175                                int argc, JSValue *argv, int flags)
  16176 {
  16177     JSRuntime *rt = caller_ctx->rt;
  16178     JSContext *ctx;
  16179     JSObject *p;
  16180     JSFunctionBytecode *b;
  16181     JSStackFrame sf_s, *sf = &sf_s;
  16182     const uint8_t *pc;
  16183     int opcode, arg_allocated_size, i;
  16184     JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, *sp, ret_val, *pval;
  16185     JSVarRef **var_refs;
  16186     size_t alloca_size;
  16187 
  16188 #if !DIRECT_DISPATCH
  16189 #define SWITCH(pc)      switch (opcode = *pc++)
  16190 #define CASE(op)        case op
  16191 #define DEFAULT         default
  16192 #define BREAK           break
  16193 #else
  16194     static const void * const dispatch_table[256] = {
  16195 #define DEF(id, size, n_pop, n_push, f) && case_OP_ ## id,
  16196 #if SHORT_OPCODES
  16197 #define def(id, size, n_pop, n_push, f)
  16198 #else
  16199 #define def(id, size, n_pop, n_push, f) && case_default,
  16200 #endif
  16201 #include "quickjs-opcode.h"
  16202         [ OP_COUNT ... 255 ] = &&case_default
  16203     };
  16204 #define SWITCH(pc)      goto *dispatch_table[opcode = *pc++];
  16205 #define CASE(op)        case_ ## op
  16206 #define DEFAULT         case_default
  16207 #define BREAK           SWITCH(pc)
  16208 #endif
  16209 
  16210     if (js_poll_interrupts(caller_ctx))
  16211         return JS_EXCEPTION;
  16212     if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) {
  16213         if (flags & JS_CALL_FLAG_GENERATOR) {
  16214             JSAsyncFunctionState *s = JS_VALUE_GET_PTR(func_obj);
  16215             /* func_obj get contains a pointer to JSFuncAsyncState */
  16216             /* the stack frame is already allocated */
  16217             sf = &s->frame;
  16218             p = JS_VALUE_GET_OBJ(sf->cur_func);
  16219             b = p->u.func.function_bytecode;
  16220             ctx = b->realm;
  16221             var_refs = p->u.func.var_refs;
  16222             local_buf = arg_buf = sf->arg_buf;
  16223             var_buf = sf->var_buf;
  16224             stack_buf = sf->var_buf + b->var_count;
  16225             sp = sf->cur_sp;
  16226             sf->cur_sp = NULL; /* cur_sp is NULL if the function is running */
  16227             pc = sf->cur_pc;
  16228             sf->prev_frame = rt->current_stack_frame;
  16229             rt->current_stack_frame = sf;
  16230             if (s->throw_flag)
  16231                 goto exception;
  16232             else
  16233                 goto restart;
  16234         } else {
  16235             goto not_a_function;
  16236         }
  16237     }
  16238     p = JS_VALUE_GET_OBJ(func_obj);
  16239     if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) {
  16240         JSClassCall *call_func;
  16241         call_func = rt->class_array[p->class_id].call;
  16242         if (!call_func) {
  16243         not_a_function:
  16244             return JS_ThrowTypeError(caller_ctx, "not a function");
  16245         }
  16246         return call_func(caller_ctx, func_obj, this_obj, argc,
  16247                          (JSValueConst *)argv, flags);
  16248     }
  16249     b = p->u.func.function_bytecode;
  16250 
  16251     if (unlikely(argc < b->arg_count || (flags & JS_CALL_FLAG_COPY_ARGV))) {
  16252         arg_allocated_size = b->arg_count;
  16253     } else {
  16254         arg_allocated_size = 0;
  16255     }
  16256 
  16257     alloca_size = sizeof(JSValue) * (arg_allocated_size + b->var_count +
  16258                                      b->stack_size);
  16259     if (js_check_stack_overflow(rt, alloca_size))
  16260         return JS_ThrowStackOverflow(caller_ctx);
  16261 
  16262     sf->js_mode = b->js_mode;
  16263     arg_buf = argv;
  16264     sf->arg_count = argc;
  16265     sf->cur_func = (JSValue)func_obj;
  16266     init_list_head(&sf->var_ref_list);
  16267     var_refs = p->u.func.var_refs;
  16268 
  16269     local_buf = alloca(alloca_size);
  16270     if (unlikely(arg_allocated_size)) {
  16271         int n = min_int(argc, b->arg_count);
  16272         arg_buf = local_buf;
  16273         for(i = 0; i < n; i++)
  16274             arg_buf[i] = JS_DupValue(caller_ctx, argv[i]);
  16275         for(; i < b->arg_count; i++)
  16276             arg_buf[i] = JS_UNDEFINED;
  16277         sf->arg_count = b->arg_count;
  16278     }
  16279     var_buf = local_buf + arg_allocated_size;
  16280     sf->var_buf = var_buf;
  16281     sf->arg_buf = arg_buf;
  16282 
  16283     for(i = 0; i < b->var_count; i++)
  16284         var_buf[i] = JS_UNDEFINED;
  16285 
  16286     stack_buf = var_buf + b->var_count;
  16287     sp = stack_buf;
  16288     pc = b->byte_code_buf;
  16289     sf->prev_frame = rt->current_stack_frame;
  16290     rt->current_stack_frame = sf;
  16291     ctx = b->realm; /* set the current realm */
  16292 
  16293  restart:
  16294     for(;;) {
  16295         int call_argc;
  16296         JSValue *call_argv;
  16297 
  16298         SWITCH(pc) {
  16299         CASE(OP_push_i32):
  16300             *sp++ = JS_NewInt32(ctx, get_u32(pc));
  16301             pc += 4;
  16302             BREAK;
  16303         CASE(OP_push_const):
  16304             *sp++ = JS_DupValue(ctx, b->cpool[get_u32(pc)]);
  16305             pc += 4;
  16306             BREAK;
  16307 #if SHORT_OPCODES
  16308         CASE(OP_push_minus1):
  16309         CASE(OP_push_0):
  16310         CASE(OP_push_1):
  16311         CASE(OP_push_2):
  16312         CASE(OP_push_3):
  16313         CASE(OP_push_4):
  16314         CASE(OP_push_5):
  16315         CASE(OP_push_6):
  16316         CASE(OP_push_7):
  16317             *sp++ = JS_NewInt32(ctx, opcode - OP_push_0);
  16318             BREAK;
  16319         CASE(OP_push_i8):
  16320             *sp++ = JS_NewInt32(ctx, get_i8(pc));
  16321             pc += 1;
  16322             BREAK;
  16323         CASE(OP_push_i16):
  16324             *sp++ = JS_NewInt32(ctx, get_i16(pc));
  16325             pc += 2;
  16326             BREAK;
  16327         CASE(OP_push_const8):
  16328             *sp++ = JS_DupValue(ctx, b->cpool[*pc++]);
  16329             BREAK;
  16330         CASE(OP_fclosure8):
  16331             *sp++ = js_closure(ctx, JS_DupValue(ctx, b->cpool[*pc++]), var_refs, sf);
  16332             if (unlikely(JS_IsException(sp[-1])))
  16333                 goto exception;
  16334             BREAK;
  16335         CASE(OP_push_empty_string):
  16336             *sp++ = JS_AtomToString(ctx, JS_ATOM_empty_string);
  16337             BREAK;
  16338         CASE(OP_get_length):
  16339             {
  16340                 JSValue val;
  16341 
  16342                 val = JS_GetProperty(ctx, sp[-1], JS_ATOM_length);
  16343                 if (unlikely(JS_IsException(val)))
  16344                     goto exception;
  16345                 JS_FreeValue(ctx, sp[-1]);
  16346                 sp[-1] = val;
  16347             }
  16348             BREAK;
  16349 #endif
  16350         CASE(OP_push_atom_value):
  16351             *sp++ = JS_AtomToValue(ctx, get_u32(pc));
  16352             pc += 4;
  16353             BREAK;
  16354         CASE(OP_undefined):
  16355             *sp++ = JS_UNDEFINED;
  16356             BREAK;
  16357         CASE(OP_null):
  16358             *sp++ = JS_NULL;
  16359             BREAK;
  16360         CASE(OP_push_this):
  16361             /* OP_push_this is only called at the start of a function */
  16362             {
  16363                 JSValue val;
  16364                 if (!(b->js_mode & JS_MODE_STRICT)) {
  16365                     uint32_t tag = JS_VALUE_GET_TAG(this_obj);
  16366                     if (likely(tag == JS_TAG_OBJECT))
  16367                         goto normal_this;
  16368                     if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) {
  16369                         val = JS_DupValue(ctx, ctx->global_obj);
  16370                     } else {
  16371                         val = JS_ToObject(ctx, this_obj);
  16372                         if (JS_IsException(val))
  16373                             goto exception;
  16374                     }
  16375                 } else {
  16376                 normal_this:
  16377                     val = JS_DupValue(ctx, this_obj);
  16378                 }
  16379                 *sp++ = val;
  16380             }
  16381             BREAK;
  16382         CASE(OP_push_false):
  16383             *sp++ = JS_FALSE;
  16384             BREAK;
  16385         CASE(OP_push_true):
  16386             *sp++ = JS_TRUE;
  16387             BREAK;
  16388         CASE(OP_object):
  16389             *sp++ = JS_NewObject(ctx);
  16390             if (unlikely(JS_IsException(sp[-1])))
  16391                 goto exception;
  16392             BREAK;
  16393         CASE(OP_special_object):
  16394             {
  16395                 int arg = *pc++;
  16396                 switch(arg) {
  16397                 case OP_SPECIAL_OBJECT_ARGUMENTS:
  16398                     *sp++ = js_build_arguments(ctx, argc, (JSValueConst *)argv);
  16399                     if (unlikely(JS_IsException(sp[-1])))
  16400                         goto exception;
  16401                     break;
  16402                 case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS:
  16403                     *sp++ = js_build_mapped_arguments(ctx, argc, (JSValueConst *)argv,
  16404                                                       sf, min_int(argc, b->arg_count));
  16405                     if (unlikely(JS_IsException(sp[-1])))
  16406                         goto exception;
  16407                     break;
  16408                 case OP_SPECIAL_OBJECT_THIS_FUNC:
  16409                     *sp++ = JS_DupValue(ctx, sf->cur_func);
  16410                     break;
  16411                 case OP_SPECIAL_OBJECT_NEW_TARGET:
  16412                     *sp++ = JS_DupValue(ctx, new_target);
  16413                     break;
  16414                 case OP_SPECIAL_OBJECT_HOME_OBJECT:
  16415                     {
  16416                         JSObject *p1;
  16417                         p1 = p->u.func.home_object;
  16418                         if (unlikely(!p1))
  16419                             *sp++ = JS_UNDEFINED;
  16420                         else
  16421                             *sp++ = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
  16422                     }
  16423                     break;
  16424                 case OP_SPECIAL_OBJECT_VAR_OBJECT:
  16425                     *sp++ = JS_NewObjectProto(ctx, JS_NULL);
  16426                     if (unlikely(JS_IsException(sp[-1])))
  16427                         goto exception;
  16428                     break;
  16429                 case OP_SPECIAL_OBJECT_IMPORT_META:
  16430                     *sp++ = js_import_meta(ctx);
  16431                     if (unlikely(JS_IsException(sp[-1])))
  16432                         goto exception;
  16433                     break;
  16434                 default:
  16435                     abort();
  16436                 }
  16437             }
  16438             BREAK;
  16439         CASE(OP_rest):
  16440             {
  16441                 int first = get_u16(pc);
  16442                 pc += 2;
  16443                 *sp++ = js_build_rest(ctx, first, argc, (JSValueConst *)argv);
  16444                 if (unlikely(JS_IsException(sp[-1])))
  16445                     goto exception;
  16446             }
  16447             BREAK;
  16448 
  16449         CASE(OP_drop):
  16450             JS_FreeValue(ctx, sp[-1]);
  16451             sp--;
  16452             BREAK;
  16453         CASE(OP_nip):
  16454             JS_FreeValue(ctx, sp[-2]);
  16455             sp[-2] = sp[-1];
  16456             sp--;
  16457             BREAK;
  16458         CASE(OP_nip1): /* a b c -> b c */
  16459             JS_FreeValue(ctx, sp[-3]);
  16460             sp[-3] = sp[-2];
  16461             sp[-2] = sp[-1];
  16462             sp--;
  16463             BREAK;
  16464         CASE(OP_dup):
  16465             sp[0] = JS_DupValue(ctx, sp[-1]);
  16466             sp++;
  16467             BREAK;
  16468         CASE(OP_dup2): /* a b -> a b a b */
  16469             sp[0] = JS_DupValue(ctx, sp[-2]);
  16470             sp[1] = JS_DupValue(ctx, sp[-1]);
  16471             sp += 2;
  16472             BREAK;
  16473         CASE(OP_dup3): /* a b c -> a b c a b c */
  16474             sp[0] = JS_DupValue(ctx, sp[-3]);
  16475             sp[1] = JS_DupValue(ctx, sp[-2]);
  16476             sp[2] = JS_DupValue(ctx, sp[-1]);
  16477             sp += 3;
  16478             BREAK;
  16479         CASE(OP_dup1): /* a b -> a a b */
  16480             sp[0] = sp[-1];
  16481             sp[-1] = JS_DupValue(ctx, sp[-2]);
  16482             sp++;
  16483             BREAK;
  16484         CASE(OP_insert2): /* obj a -> a obj a (dup_x1) */
  16485             sp[0] = sp[-1];
  16486             sp[-1] = sp[-2];
  16487             sp[-2] = JS_DupValue(ctx, sp[0]);
  16488             sp++;
  16489             BREAK;
  16490         CASE(OP_insert3): /* obj prop a -> a obj prop a (dup_x2) */
  16491             sp[0] = sp[-1];
  16492             sp[-1] = sp[-2];
  16493             sp[-2] = sp[-3];
  16494             sp[-3] = JS_DupValue(ctx, sp[0]);
  16495             sp++;
  16496             BREAK;
  16497         CASE(OP_insert4): /* this obj prop a -> a this obj prop a */
  16498             sp[0] = sp[-1];
  16499             sp[-1] = sp[-2];
  16500             sp[-2] = sp[-3];
  16501             sp[-3] = sp[-4];
  16502             sp[-4] = JS_DupValue(ctx, sp[0]);
  16503             sp++;
  16504             BREAK;
  16505         CASE(OP_perm3): /* obj a b -> a obj b (213) */
  16506             {
  16507                 JSValue tmp;
  16508                 tmp = sp[-2];
  16509                 sp[-2] = sp[-3];
  16510                 sp[-3] = tmp;
  16511             }
  16512             BREAK;
  16513         CASE(OP_rot3l): /* x a b -> a b x (231) */
  16514             {
  16515                 JSValue tmp;
  16516                 tmp = sp[-3];
  16517                 sp[-3] = sp[-2];
  16518                 sp[-2] = sp[-1];
  16519                 sp[-1] = tmp;
  16520             }
  16521             BREAK;
  16522         CASE(OP_rot4l): /* x a b c -> a b c x */
  16523             {
  16524                 JSValue tmp;
  16525                 tmp = sp[-4];
  16526                 sp[-4] = sp[-3];
  16527                 sp[-3] = sp[-2];
  16528                 sp[-2] = sp[-1];
  16529                 sp[-1] = tmp;
  16530             }
  16531             BREAK;
  16532         CASE(OP_rot5l): /* x a b c d -> a b c d x */
  16533             {
  16534                 JSValue tmp;
  16535                 tmp = sp[-5];
  16536                 sp[-5] = sp[-4];
  16537                 sp[-4] = sp[-3];
  16538                 sp[-3] = sp[-2];
  16539                 sp[-2] = sp[-1];
  16540                 sp[-1] = tmp;
  16541             }
  16542             BREAK;
  16543         CASE(OP_rot3r): /* a b x -> x a b (312) */
  16544             {
  16545                 JSValue tmp;
  16546                 tmp = sp[-1];
  16547                 sp[-1] = sp[-2];
  16548                 sp[-2] = sp[-3];
  16549                 sp[-3] = tmp;
  16550             }
  16551             BREAK;
  16552         CASE(OP_perm4): /* obj prop a b -> a obj prop b */
  16553             {
  16554                 JSValue tmp;
  16555                 tmp = sp[-2];
  16556                 sp[-2] = sp[-3];
  16557                 sp[-3] = sp[-4];
  16558                 sp[-4] = tmp;
  16559             }
  16560             BREAK;
  16561         CASE(OP_perm5): /* this obj prop a b -> a this obj prop b */
  16562             {
  16563                 JSValue tmp;
  16564                 tmp = sp[-2];
  16565                 sp[-2] = sp[-3];
  16566                 sp[-3] = sp[-4];
  16567                 sp[-4] = sp[-5];
  16568                 sp[-5] = tmp;
  16569             }
  16570             BREAK;
  16571         CASE(OP_swap): /* a b -> b a */
  16572             {
  16573                 JSValue tmp;
  16574                 tmp = sp[-2];
  16575                 sp[-2] = sp[-1];
  16576                 sp[-1] = tmp;
  16577             }
  16578             BREAK;
  16579         CASE(OP_swap2): /* a b c d -> c d a b */
  16580             {
  16581                 JSValue tmp1, tmp2;
  16582                 tmp1 = sp[-4];
  16583                 tmp2 = sp[-3];
  16584                 sp[-4] = sp[-2];
  16585                 sp[-3] = sp[-1];
  16586                 sp[-2] = tmp1;
  16587                 sp[-1] = tmp2;
  16588             }
  16589             BREAK;
  16590 
  16591         CASE(OP_fclosure):
  16592             {
  16593                 JSValue bfunc = JS_DupValue(ctx, b->cpool[get_u32(pc)]);
  16594                 pc += 4;
  16595                 *sp++ = js_closure(ctx, bfunc, var_refs, sf);
  16596                 if (unlikely(JS_IsException(sp[-1])))
  16597                     goto exception;
  16598             }
  16599             BREAK;
  16600 #if SHORT_OPCODES
  16601         CASE(OP_call0):
  16602         CASE(OP_call1):
  16603         CASE(OP_call2):
  16604         CASE(OP_call3):
  16605             call_argc = opcode - OP_call0;
  16606             goto has_call_argc;
  16607 #endif
  16608         CASE(OP_call):
  16609         CASE(OP_tail_call):
  16610             {
  16611                 call_argc = get_u16(pc);
  16612                 pc += 2;
  16613                 goto has_call_argc;
  16614             has_call_argc:
  16615                 call_argv = sp - call_argc;
  16616                 sf->cur_pc = pc;
  16617                 ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED,
  16618                                           JS_UNDEFINED, call_argc, call_argv, 0);
  16619                 if (unlikely(JS_IsException(ret_val)))
  16620                     goto exception;
  16621                 if (opcode == OP_tail_call)
  16622                     goto done;
  16623                 for(i = -1; i < call_argc; i++)
  16624                     JS_FreeValue(ctx, call_argv[i]);
  16625                 sp -= call_argc + 1;
  16626                 *sp++ = ret_val;
  16627             }
  16628             BREAK;
  16629         CASE(OP_call_constructor):
  16630             {
  16631                 call_argc = get_u16(pc);
  16632                 pc += 2;
  16633                 call_argv = sp - call_argc;
  16634                 sf->cur_pc = pc;
  16635                 ret_val = JS_CallConstructorInternal(ctx, call_argv[-2],
  16636                                                      call_argv[-1],
  16637                                                      call_argc, call_argv, 0);
  16638                 if (unlikely(JS_IsException(ret_val)))
  16639                     goto exception;
  16640                 for(i = -2; i < call_argc; i++)
  16641                     JS_FreeValue(ctx, call_argv[i]);
  16642                 sp -= call_argc + 2;
  16643                 *sp++ = ret_val;
  16644             }
  16645             BREAK;
  16646         CASE(OP_call_method):
  16647         CASE(OP_tail_call_method):
  16648             {
  16649                 call_argc = get_u16(pc);
  16650                 pc += 2;
  16651                 call_argv = sp - call_argc;
  16652                 sf->cur_pc = pc;
  16653                 ret_val = JS_CallInternal(ctx, call_argv[-1], call_argv[-2],
  16654                                           JS_UNDEFINED, call_argc, call_argv, 0);
  16655                 if (unlikely(JS_IsException(ret_val)))
  16656                     goto exception;
  16657                 if (opcode == OP_tail_call_method)
  16658                     goto done;
  16659                 for(i = -2; i < call_argc; i++)
  16660                     JS_FreeValue(ctx, call_argv[i]);
  16661                 sp -= call_argc + 2;
  16662                 *sp++ = ret_val;
  16663             }
  16664             BREAK;
  16665         CASE(OP_array_from):
  16666             {
  16667                 int i, ret;
  16668 
  16669                 call_argc = get_u16(pc);
  16670                 pc += 2;
  16671                 ret_val = JS_NewArray(ctx);
  16672                 if (unlikely(JS_IsException(ret_val)))
  16673                     goto exception;
  16674                 call_argv = sp - call_argc;
  16675                 for(i = 0; i < call_argc; i++) {
  16676                     ret = JS_DefinePropertyValue(ctx, ret_val, __JS_AtomFromUInt32(i), call_argv[i],
  16677                                                  JS_PROP_C_W_E | JS_PROP_THROW);
  16678                     call_argv[i] = JS_UNDEFINED;
  16679                     if (ret < 0) {
  16680                         JS_FreeValue(ctx, ret_val);
  16681                         goto exception;
  16682                     }
  16683                 }
  16684                 sp -= call_argc;
  16685                 *sp++ = ret_val;
  16686             }
  16687             BREAK;
  16688 
  16689         CASE(OP_apply):
  16690             {
  16691                 int magic;
  16692                 magic = get_u16(pc);
  16693                 pc += 2;
  16694 
  16695                 ret_val = js_function_apply(ctx, sp[-3], 2, (JSValueConst *)&sp[-2], magic);
  16696                 if (unlikely(JS_IsException(ret_val)))
  16697                     goto exception;
  16698                 JS_FreeValue(ctx, sp[-3]);
  16699                 JS_FreeValue(ctx, sp[-2]);
  16700                 JS_FreeValue(ctx, sp[-1]);
  16701                 sp -= 3;
  16702                 *sp++ = ret_val;
  16703             }
  16704             BREAK;
  16705         CASE(OP_return):
  16706             ret_val = *--sp;
  16707             goto done;
  16708         CASE(OP_return_undef):
  16709             ret_val = JS_UNDEFINED;
  16710             goto done;
  16711 
  16712         CASE(OP_check_ctor_return):
  16713             /* return TRUE if 'this' should be returned */
  16714             if (!JS_IsObject(sp[-1])) {
  16715                 if (!JS_IsUndefined(sp[-1])) {
  16716                     JS_ThrowTypeError(caller_ctx, "derived class constructor must return an object or undefined");
  16717                     goto exception;
  16718                 }
  16719                 sp[0] = JS_TRUE;
  16720             } else {
  16721                 sp[0] = JS_FALSE;
  16722             }
  16723             sp++;
  16724             BREAK;
  16725         CASE(OP_check_ctor):
  16726             if (JS_IsUndefined(new_target)) {
  16727                 JS_ThrowTypeError(ctx, "class constructors must be invoked with 'new'");
  16728                 goto exception;
  16729             }
  16730             BREAK;
  16731         CASE(OP_check_brand):
  16732             {
  16733                 int ret = JS_CheckBrand(ctx, sp[-2], sp[-1]);
  16734                 if (ret < 0)
  16735                     goto exception;
  16736                 if (!ret) {
  16737                     JS_ThrowTypeError(ctx, "invalid brand on object");
  16738                     goto exception;
  16739                 }
  16740             }
  16741             BREAK;
  16742         CASE(OP_add_brand):
  16743             if (JS_AddBrand(ctx, sp[-2], sp[-1]) < 0)
  16744                 goto exception;
  16745             JS_FreeValue(ctx, sp[-2]);
  16746             JS_FreeValue(ctx, sp[-1]);
  16747             sp -= 2;
  16748             BREAK;
  16749 
  16750         CASE(OP_throw):
  16751             JS_Throw(ctx, *--sp);
  16752             goto exception;
  16753 
  16754         CASE(OP_throw_error):
  16755 #define JS_THROW_VAR_RO             0
  16756 #define JS_THROW_VAR_REDECL         1
  16757 #define JS_THROW_VAR_UNINITIALIZED  2
  16758 #define JS_THROW_ERROR_DELETE_SUPER   3
  16759 #define JS_THROW_ERROR_ITERATOR_THROW 4
  16760             {
  16761                 JSAtom atom;
  16762                 int type;
  16763                 atom = get_u32(pc);
  16764                 type = pc[4];
  16765                 pc += 5;
  16766                 if (type == JS_THROW_VAR_RO)
  16767                     JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, atom);
  16768                 else
  16769                 if (type == JS_THROW_VAR_REDECL)
  16770                     JS_ThrowSyntaxErrorVarRedeclaration(ctx, atom);
  16771                 else
  16772                 if (type == JS_THROW_VAR_UNINITIALIZED)
  16773                     JS_ThrowReferenceErrorUninitialized(ctx, atom);
  16774                 else
  16775                 if (type == JS_THROW_ERROR_DELETE_SUPER)
  16776                     JS_ThrowReferenceError(ctx, "unsupported reference to 'super'");
  16777                 else
  16778                 if (type == JS_THROW_ERROR_ITERATOR_THROW)
  16779                     JS_ThrowTypeError(ctx, "iterator does not have a throw method");
  16780                 else
  16781                     JS_ThrowInternalError(ctx, "invalid throw var type %d", type);
  16782             }
  16783             goto exception;
  16784 
  16785         CASE(OP_eval):
  16786             {
  16787                 JSValueConst obj;
  16788                 int scope_idx;
  16789                 call_argc = get_u16(pc);
  16790                 scope_idx = get_u16(pc + 2) - 1;
  16791                 pc += 4;
  16792                 call_argv = sp - call_argc;
  16793                 sf->cur_pc = pc;
  16794                 if (js_same_value(ctx, call_argv[-1], ctx->eval_obj)) {
  16795                     if (call_argc >= 1)
  16796                         obj = call_argv[0];
  16797                     else
  16798                         obj = JS_UNDEFINED;
  16799                     ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj,
  16800                                             JS_EVAL_TYPE_DIRECT, scope_idx);
  16801                 } else {
  16802                     ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED,
  16803                                               JS_UNDEFINED, call_argc, call_argv, 0);
  16804                 }
  16805                 if (unlikely(JS_IsException(ret_val)))
  16806                     goto exception;
  16807                 for(i = -1; i < call_argc; i++)
  16808                     JS_FreeValue(ctx, call_argv[i]);
  16809                 sp -= call_argc + 1;
  16810                 *sp++ = ret_val;
  16811             }
  16812             BREAK;
  16813             /* could merge with OP_apply */
  16814         CASE(OP_apply_eval):
  16815             {
  16816                 int scope_idx;
  16817                 uint32_t len;
  16818                 JSValue *tab;
  16819                 JSValueConst obj;
  16820 
  16821                 scope_idx = get_u16(pc) - 1;
  16822                 pc += 2;
  16823                 tab = build_arg_list(ctx, &len, sp[-1]);
  16824                 if (!tab)
  16825                     goto exception;
  16826                 if (js_same_value(ctx, sp[-2], ctx->eval_obj)) {
  16827                     if (len >= 1)
  16828                         obj = tab[0];
  16829                     else
  16830                         obj = JS_UNDEFINED;
  16831                     ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj,
  16832                                             JS_EVAL_TYPE_DIRECT, scope_idx);
  16833                 } else {
  16834                     ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len,
  16835                                       (JSValueConst *)tab);
  16836                 }
  16837                 free_arg_list(ctx, tab, len);
  16838                 if (unlikely(JS_IsException(ret_val)))
  16839                     goto exception;
  16840                 JS_FreeValue(ctx, sp[-2]);
  16841                 JS_FreeValue(ctx, sp[-1]);
  16842                 sp -= 2;
  16843                 *sp++ = ret_val;
  16844             }
  16845             BREAK;
  16846 
  16847         CASE(OP_regexp):
  16848             {
  16849                 sp[-2] = js_regexp_constructor_internal(ctx, JS_UNDEFINED,
  16850                                                         sp[-2], sp[-1]);
  16851                 sp--;
  16852             }
  16853             BREAK;
  16854 
  16855         CASE(OP_get_super):
  16856             {
  16857                 JSValue proto;
  16858                 proto = JS_GetPrototype(ctx, sp[-1]);
  16859                 if (JS_IsException(proto))
  16860                     goto exception;
  16861                 JS_FreeValue(ctx, sp[-1]);
  16862                 sp[-1] = proto;
  16863             }
  16864             BREAK;
  16865 
  16866         CASE(OP_import):
  16867             {
  16868                 JSValue val;
  16869                 val = js_dynamic_import(ctx, sp[-1]);
  16870                 if (JS_IsException(val))
  16871                     goto exception;
  16872                 JS_FreeValue(ctx, sp[-1]);
  16873                 sp[-1] = val;
  16874             }
  16875             BREAK;
  16876 
  16877         CASE(OP_check_var):
  16878             {
  16879                 int ret;
  16880                 JSAtom atom;
  16881                 atom = get_u32(pc);
  16882                 pc += 4;
  16883 
  16884                 ret = JS_CheckGlobalVar(ctx, atom);
  16885                 if (ret < 0)
  16886                     goto exception;
  16887                 *sp++ = JS_NewBool(ctx, ret);
  16888             }
  16889             BREAK;
  16890 
  16891         CASE(OP_get_var_undef):
  16892         CASE(OP_get_var):
  16893             {
  16894                 JSValue val;
  16895                 JSAtom atom;
  16896                 atom = get_u32(pc);
  16897                 pc += 4;
  16898 
  16899                 val = JS_GetGlobalVar(ctx, atom, opcode - OP_get_var_undef);
  16900                 if (unlikely(JS_IsException(val)))
  16901                     goto exception;
  16902                 *sp++ = val;
  16903             }
  16904             BREAK;
  16905 
  16906         CASE(OP_put_var):
  16907         CASE(OP_put_var_init):
  16908             {
  16909                 int ret;
  16910                 JSAtom atom;
  16911                 atom = get_u32(pc);
  16912                 pc += 4;
  16913 
  16914                 ret = JS_SetGlobalVar(ctx, atom, sp[-1], opcode - OP_put_var);
  16915                 sp--;
  16916                 if (unlikely(ret < 0))
  16917                     goto exception;
  16918             }
  16919             BREAK;
  16920 
  16921         CASE(OP_put_var_strict):
  16922             {
  16923                 int ret;
  16924                 JSAtom atom;
  16925                 atom = get_u32(pc);
  16926                 pc += 4;
  16927 
  16928                 /* sp[-2] is JS_TRUE or JS_FALSE */
  16929                 if (unlikely(!JS_VALUE_GET_INT(sp[-2]))) {
  16930                     JS_ThrowReferenceErrorNotDefined(ctx, atom);
  16931                     goto exception;
  16932                 }
  16933                 ret = JS_SetGlobalVar(ctx, atom, sp[-1], 2);
  16934                 sp -= 2;
  16935                 if (unlikely(ret < 0))
  16936                     goto exception;
  16937             }
  16938             BREAK;
  16939 
  16940         CASE(OP_check_define_var):
  16941             {
  16942                 JSAtom atom;
  16943                 int flags;
  16944                 atom = get_u32(pc);
  16945                 flags = pc[4];
  16946                 pc += 5;
  16947                 if (JS_CheckDefineGlobalVar(ctx, atom, flags))
  16948                     goto exception;
  16949             }
  16950             BREAK;
  16951         CASE(OP_define_var):
  16952             {
  16953                 JSAtom atom;
  16954                 int flags;
  16955                 atom = get_u32(pc);
  16956                 flags = pc[4];
  16957                 pc += 5;
  16958                 if (JS_DefineGlobalVar(ctx, atom, flags))
  16959                     goto exception;
  16960             }
  16961             BREAK;
  16962         CASE(OP_define_func):
  16963             {
  16964                 JSAtom atom;
  16965                 int flags;
  16966                 atom = get_u32(pc);
  16967                 flags = pc[4];
  16968                 pc += 5;
  16969                 if (JS_DefineGlobalFunction(ctx, atom, sp[-1], flags))
  16970                     goto exception;
  16971                 JS_FreeValue(ctx, sp[-1]);
  16972                 sp--;
  16973             }
  16974             BREAK;
  16975 
  16976         CASE(OP_get_loc):
  16977             {
  16978                 int idx;
  16979                 idx = get_u16(pc);
  16980                 pc += 2;
  16981                 sp[0] = JS_DupValue(ctx, var_buf[idx]);
  16982                 sp++;
  16983             }
  16984             BREAK;
  16985         CASE(OP_put_loc):
  16986             {
  16987                 int idx;
  16988                 idx = get_u16(pc);
  16989                 pc += 2;
  16990                 set_value(ctx, &var_buf[idx], sp[-1]);
  16991                 sp--;
  16992             }
  16993             BREAK;
  16994         CASE(OP_set_loc):
  16995             {
  16996                 int idx;
  16997                 idx = get_u16(pc);
  16998                 pc += 2;
  16999                 set_value(ctx, &var_buf[idx], JS_DupValue(ctx, sp[-1]));
  17000             }
  17001             BREAK;
  17002         CASE(OP_get_arg):
  17003             {
  17004                 int idx;
  17005                 idx = get_u16(pc);
  17006                 pc += 2;
  17007                 sp[0] = JS_DupValue(ctx, arg_buf[idx]);
  17008                 sp++;
  17009             }
  17010             BREAK;
  17011         CASE(OP_put_arg):
  17012             {
  17013                 int idx;
  17014                 idx = get_u16(pc);
  17015                 pc += 2;
  17016                 set_value(ctx, &arg_buf[idx], sp[-1]);
  17017                 sp--;
  17018             }
  17019             BREAK;
  17020         CASE(OP_set_arg):
  17021             {
  17022                 int idx;
  17023                 idx = get_u16(pc);
  17024                 pc += 2;
  17025                 set_value(ctx, &arg_buf[idx], JS_DupValue(ctx, sp[-1]));
  17026             }
  17027             BREAK;
  17028 
  17029 #if SHORT_OPCODES
  17030         CASE(OP_get_loc8): *sp++ = JS_DupValue(ctx, var_buf[*pc++]); BREAK;
  17031         CASE(OP_put_loc8): set_value(ctx, &var_buf[*pc++], *--sp); BREAK;
  17032         CASE(OP_set_loc8): set_value(ctx, &var_buf[*pc++], JS_DupValue(ctx, sp[-1])); BREAK;
  17033 
  17034         CASE(OP_get_loc0): *sp++ = JS_DupValue(ctx, var_buf[0]); BREAK;
  17035         CASE(OP_get_loc1): *sp++ = JS_DupValue(ctx, var_buf[1]); BREAK;
  17036         CASE(OP_get_loc2): *sp++ = JS_DupValue(ctx, var_buf[2]); BREAK;
  17037         CASE(OP_get_loc3): *sp++ = JS_DupValue(ctx, var_buf[3]); BREAK;
  17038         CASE(OP_put_loc0): set_value(ctx, &var_buf[0], *--sp); BREAK;
  17039         CASE(OP_put_loc1): set_value(ctx, &var_buf[1], *--sp); BREAK;
  17040         CASE(OP_put_loc2): set_value(ctx, &var_buf[2], *--sp); BREAK;
  17041         CASE(OP_put_loc3): set_value(ctx, &var_buf[3], *--sp); BREAK;
  17042         CASE(OP_set_loc0): set_value(ctx, &var_buf[0], JS_DupValue(ctx, sp[-1])); BREAK;
  17043         CASE(OP_set_loc1): set_value(ctx, &var_buf[1], JS_DupValue(ctx, sp[-1])); BREAK;
  17044         CASE(OP_set_loc2): set_value(ctx, &var_buf[2], JS_DupValue(ctx, sp[-1])); BREAK;
  17045         CASE(OP_set_loc3): set_value(ctx, &var_buf[3], JS_DupValue(ctx, sp[-1])); BREAK;
  17046         CASE(OP_get_arg0): *sp++ = JS_DupValue(ctx, arg_buf[0]); BREAK;
  17047         CASE(OP_get_arg1): *sp++ = JS_DupValue(ctx, arg_buf[1]); BREAK;
  17048         CASE(OP_get_arg2): *sp++ = JS_DupValue(ctx, arg_buf[2]); BREAK;
  17049         CASE(OP_get_arg3): *sp++ = JS_DupValue(ctx, arg_buf[3]); BREAK;
  17050         CASE(OP_put_arg0): set_value(ctx, &arg_buf[0], *--sp); BREAK;
  17051         CASE(OP_put_arg1): set_value(ctx, &arg_buf[1], *--sp); BREAK;
  17052         CASE(OP_put_arg2): set_value(ctx, &arg_buf[2], *--sp); BREAK;
  17053         CASE(OP_put_arg3): set_value(ctx, &arg_buf[3], *--sp); BREAK;
  17054         CASE(OP_set_arg0): set_value(ctx, &arg_buf[0], JS_DupValue(ctx, sp[-1])); BREAK;
  17055         CASE(OP_set_arg1): set_value(ctx, &arg_buf[1], JS_DupValue(ctx, sp[-1])); BREAK;
  17056         CASE(OP_set_arg2): set_value(ctx, &arg_buf[2], JS_DupValue(ctx, sp[-1])); BREAK;
  17057         CASE(OP_set_arg3): set_value(ctx, &arg_buf[3], JS_DupValue(ctx, sp[-1])); BREAK;
  17058         CASE(OP_get_var_ref0): *sp++ = JS_DupValue(ctx, *var_refs[0]->pvalue); BREAK;
  17059         CASE(OP_get_var_ref1): *sp++ = JS_DupValue(ctx, *var_refs[1]->pvalue); BREAK;
  17060         CASE(OP_get_var_ref2): *sp++ = JS_DupValue(ctx, *var_refs[2]->pvalue); BREAK;
  17061         CASE(OP_get_var_ref3): *sp++ = JS_DupValue(ctx, *var_refs[3]->pvalue); BREAK;
  17062         CASE(OP_put_var_ref0): set_value(ctx, var_refs[0]->pvalue, *--sp); BREAK;
  17063         CASE(OP_put_var_ref1): set_value(ctx, var_refs[1]->pvalue, *--sp); BREAK;
  17064         CASE(OP_put_var_ref2): set_value(ctx, var_refs[2]->pvalue, *--sp); BREAK;
  17065         CASE(OP_put_var_ref3): set_value(ctx, var_refs[3]->pvalue, *--sp); BREAK;
  17066         CASE(OP_set_var_ref0): set_value(ctx, var_refs[0]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
  17067         CASE(OP_set_var_ref1): set_value(ctx, var_refs[1]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
  17068         CASE(OP_set_var_ref2): set_value(ctx, var_refs[2]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
  17069         CASE(OP_set_var_ref3): set_value(ctx, var_refs[3]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
  17070 #endif
  17071 
  17072         CASE(OP_get_var_ref):
  17073             {
  17074                 int idx;
  17075                 JSValue val;
  17076                 idx = get_u16(pc);
  17077                 pc += 2;
  17078                 val = *var_refs[idx]->pvalue;
  17079                 sp[0] = JS_DupValue(ctx, val);
  17080                 sp++;
  17081             }
  17082             BREAK;
  17083         CASE(OP_put_var_ref):
  17084             {
  17085                 int idx;
  17086                 idx = get_u16(pc);
  17087                 pc += 2;
  17088                 set_value(ctx, var_refs[idx]->pvalue, sp[-1]);
  17089                 sp--;
  17090             }
  17091             BREAK;
  17092         CASE(OP_set_var_ref):
  17093             {
  17094                 int idx;
  17095                 idx = get_u16(pc);
  17096                 pc += 2;
  17097                 set_value(ctx, var_refs[idx]->pvalue, JS_DupValue(ctx, sp[-1]));
  17098             }
  17099             BREAK;
  17100         CASE(OP_get_var_ref_check):
  17101             {
  17102                 int idx;
  17103                 JSValue val;
  17104                 idx = get_u16(pc);
  17105                 pc += 2;
  17106                 val = *var_refs[idx]->pvalue;
  17107                 if (unlikely(JS_IsUninitialized(val))) {
  17108                     JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);
  17109                     goto exception;
  17110                 }
  17111                 sp[0] = JS_DupValue(ctx, val);
  17112                 sp++;
  17113             }
  17114             BREAK;
  17115         CASE(OP_put_var_ref_check):
  17116             {
  17117                 int idx;
  17118                 idx = get_u16(pc);
  17119                 pc += 2;
  17120                 if (unlikely(JS_IsUninitialized(*var_refs[idx]->pvalue))) {
  17121                     JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);
  17122                     goto exception;
  17123                 }
  17124                 set_value(ctx, var_refs[idx]->pvalue, sp[-1]);
  17125                 sp--;
  17126             }
  17127             BREAK;
  17128         CASE(OP_put_var_ref_check_init):
  17129             {
  17130                 int idx;
  17131                 idx = get_u16(pc);
  17132                 pc += 2;
  17133                 if (unlikely(!JS_IsUninitialized(*var_refs[idx]->pvalue))) {
  17134                     JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);
  17135                     goto exception;
  17136                 }
  17137                 set_value(ctx, var_refs[idx]->pvalue, sp[-1]);
  17138                 sp--;
  17139             }
  17140             BREAK;
  17141         CASE(OP_set_loc_uninitialized):
  17142             {
  17143                 int idx;
  17144                 idx = get_u16(pc);
  17145                 pc += 2;
  17146                 set_value(ctx, &var_buf[idx], JS_UNINITIALIZED);
  17147             }
  17148             BREAK;
  17149         CASE(OP_get_loc_check):
  17150             {
  17151                 int idx;
  17152                 idx = get_u16(pc);
  17153                 pc += 2;
  17154                 if (unlikely(JS_IsUninitialized(var_buf[idx]))) {
  17155                     JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE);
  17156                     goto exception;
  17157                 }
  17158                 sp[0] = JS_DupValue(ctx, var_buf[idx]);
  17159                 sp++;
  17160             }
  17161             BREAK;
  17162         CASE(OP_get_loc_checkthis):
  17163             {
  17164                 int idx;
  17165                 idx = get_u16(pc);
  17166                 pc += 2;
  17167                 if (unlikely(JS_IsUninitialized(var_buf[idx]))) {
  17168                     JS_ThrowReferenceErrorUninitialized2(caller_ctx, b, idx, FALSE);
  17169                     goto exception;
  17170                 }
  17171                 sp[0] = JS_DupValue(ctx, var_buf[idx]);
  17172                 sp++;
  17173             }
  17174             BREAK;
  17175         CASE(OP_put_loc_check):
  17176             {
  17177                 int idx;
  17178                 idx = get_u16(pc);
  17179                 pc += 2;
  17180                 if (unlikely(JS_IsUninitialized(var_buf[idx]))) {
  17181                     JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE);
  17182                     goto exception;
  17183                 }
  17184                 set_value(ctx, &var_buf[idx], sp[-1]);
  17185                 sp--;
  17186             }
  17187             BREAK;
  17188         CASE(OP_put_loc_check_init):
  17189             {
  17190                 int idx;
  17191                 idx = get_u16(pc);
  17192                 pc += 2;
  17193                 if (unlikely(!JS_IsUninitialized(var_buf[idx]))) {
  17194                     JS_ThrowReferenceError(ctx, "'this' can be initialized only once");
  17195                     goto exception;
  17196                 }
  17197                 set_value(ctx, &var_buf[idx], sp[-1]);
  17198                 sp--;
  17199             }
  17200             BREAK;
  17201         CASE(OP_close_loc):
  17202             {
  17203                 int idx;
  17204                 idx = get_u16(pc);
  17205                 pc += 2;
  17206                 close_lexical_var(ctx, sf, idx, FALSE);
  17207             }
  17208             BREAK;
  17209 
  17210         CASE(OP_make_loc_ref):
  17211         CASE(OP_make_arg_ref):
  17212         CASE(OP_make_var_ref_ref):
  17213             {
  17214                 JSVarRef *var_ref;
  17215                 JSProperty *pr;
  17216                 JSAtom atom;
  17217                 int idx;
  17218                 atom = get_u32(pc);
  17219                 idx = get_u16(pc + 4);
  17220                 pc += 6;
  17221                 *sp++ = JS_NewObjectProto(ctx, JS_NULL);
  17222                 if (unlikely(JS_IsException(sp[-1])))
  17223                     goto exception;
  17224                 if (opcode == OP_make_var_ref_ref) {
  17225                     var_ref = var_refs[idx];
  17226                     var_ref->header.ref_count++;
  17227                 } else {
  17228                     var_ref = get_var_ref(ctx, sf, idx, opcode == OP_make_arg_ref);
  17229                     if (!var_ref)
  17230                         goto exception;
  17231                 }
  17232                 pr = add_property(ctx, JS_VALUE_GET_OBJ(sp[-1]), atom,
  17233                                   JS_PROP_WRITABLE | JS_PROP_VARREF);
  17234                 if (!pr) {
  17235                     free_var_ref(rt, var_ref);
  17236                     goto exception;
  17237                 }
  17238                 pr->u.var_ref = var_ref;
  17239                 *sp++ = JS_AtomToValue(ctx, atom);
  17240             }
  17241             BREAK;
  17242         CASE(OP_make_var_ref):
  17243             {
  17244                 JSAtom atom;
  17245                 atom = get_u32(pc);
  17246                 pc += 4;
  17247 
  17248                 if (JS_GetGlobalVarRef(ctx, atom, sp))
  17249                     goto exception;
  17250                 sp += 2;
  17251             }
  17252             BREAK;
  17253 
  17254         CASE(OP_goto):
  17255             pc += (int32_t)get_u32(pc);
  17256             if (unlikely(js_poll_interrupts(ctx)))
  17257                 goto exception;
  17258             BREAK;
  17259 #if SHORT_OPCODES
  17260         CASE(OP_goto16):
  17261             pc += (int16_t)get_u16(pc);
  17262             if (unlikely(js_poll_interrupts(ctx)))
  17263                 goto exception;
  17264             BREAK;
  17265         CASE(OP_goto8):
  17266             pc += (int8_t)pc[0];
  17267             if (unlikely(js_poll_interrupts(ctx)))
  17268                 goto exception;
  17269             BREAK;
  17270 #endif
  17271         CASE(OP_if_true):
  17272             {
  17273                 int res;
  17274                 JSValue op1;
  17275 
  17276                 op1 = sp[-1];
  17277                 pc += 4;
  17278                 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
  17279                     res = JS_VALUE_GET_INT(op1);
  17280                 } else {
  17281                     res = JS_ToBoolFree(ctx, op1);
  17282                 }
  17283                 sp--;
  17284                 if (res) {
  17285                     pc += (int32_t)get_u32(pc - 4) - 4;
  17286                 }
  17287                 if (unlikely(js_poll_interrupts(ctx)))
  17288                     goto exception;
  17289             }
  17290             BREAK;
  17291         CASE(OP_if_false):
  17292             {
  17293                 int res;
  17294                 JSValue op1;
  17295 
  17296                 op1 = sp[-1];
  17297                 pc += 4;
  17298                 /* quick and dirty test for JS_TAG_INT, JS_TAG_BOOL, JS_TAG_NULL and JS_TAG_UNDEFINED */
  17299                 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
  17300                     res = JS_VALUE_GET_INT(op1);
  17301                 } else {
  17302                     res = JS_ToBoolFree(ctx, op1);
  17303                 }
  17304                 sp--;
  17305                 if (!res) {
  17306                     pc += (int32_t)get_u32(pc - 4) - 4;
  17307                 }
  17308                 if (unlikely(js_poll_interrupts(ctx)))
  17309                     goto exception;
  17310             }
  17311             BREAK;
  17312 #if SHORT_OPCODES
  17313         CASE(OP_if_true8):
  17314             {
  17315                 int res;
  17316                 JSValue op1;
  17317 
  17318                 op1 = sp[-1];
  17319                 pc += 1;
  17320                 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
  17321                     res = JS_VALUE_GET_INT(op1);
  17322                 } else {
  17323                     res = JS_ToBoolFree(ctx, op1);
  17324                 }
  17325                 sp--;
  17326                 if (res) {
  17327                     pc += (int8_t)pc[-1] - 1;
  17328                 }
  17329                 if (unlikely(js_poll_interrupts(ctx)))
  17330                     goto exception;
  17331             }
  17332             BREAK;
  17333         CASE(OP_if_false8):
  17334             {
  17335                 int res;
  17336                 JSValue op1;
  17337 
  17338                 op1 = sp[-1];
  17339                 pc += 1;
  17340                 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
  17341                     res = JS_VALUE_GET_INT(op1);
  17342                 } else {
  17343                     res = JS_ToBoolFree(ctx, op1);
  17344                 }
  17345                 sp--;
  17346                 if (!res) {
  17347                     pc += (int8_t)pc[-1] - 1;
  17348                 }
  17349                 if (unlikely(js_poll_interrupts(ctx)))
  17350                     goto exception;
  17351             }
  17352             BREAK;
  17353 #endif
  17354         CASE(OP_catch):
  17355             {
  17356                 int32_t diff;
  17357                 diff = get_u32(pc);
  17358                 sp[0] = JS_NewCatchOffset(ctx, pc + diff - b->byte_code_buf);
  17359                 sp++;
  17360                 pc += 4;
  17361             }
  17362             BREAK;
  17363         CASE(OP_gosub):
  17364             {
  17365                 int32_t diff;
  17366                 diff = get_u32(pc);
  17367                 /* XXX: should have a different tag to avoid security flaw */
  17368                 sp[0] = JS_NewInt32(ctx, pc + 4 - b->byte_code_buf);
  17369                 sp++;
  17370                 pc += diff;
  17371             }
  17372             BREAK;
  17373         CASE(OP_ret):
  17374             {
  17375                 JSValue op1;
  17376                 uint32_t pos;
  17377                 op1 = sp[-1];
  17378                 if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_INT))
  17379                     goto ret_fail;
  17380                 pos = JS_VALUE_GET_INT(op1);
  17381                 if (unlikely(pos >= b->byte_code_len)) {
  17382                 ret_fail:
  17383                     JS_ThrowInternalError(ctx, "invalid ret value");
  17384                     goto exception;
  17385                 }
  17386                 sp--;
  17387                 pc = b->byte_code_buf + pos;
  17388             }
  17389             BREAK;
  17390 
  17391         CASE(OP_for_in_start):
  17392             if (js_for_in_start(ctx, sp))
  17393                 goto exception;
  17394             BREAK;
  17395         CASE(OP_for_in_next):
  17396             if (js_for_in_next(ctx, sp))
  17397                 goto exception;
  17398             sp += 2;
  17399             BREAK;
  17400         CASE(OP_for_of_start):
  17401             if (js_for_of_start(ctx, sp, FALSE))
  17402                 goto exception;
  17403             sp += 1;
  17404             *sp++ = JS_NewCatchOffset(ctx, 0);
  17405             BREAK;
  17406         CASE(OP_for_of_next):
  17407             {
  17408                 int offset = -3 - pc[0];
  17409                 pc += 1;
  17410                 if (js_for_of_next(ctx, sp, offset))
  17411                     goto exception;
  17412                 sp += 2;
  17413             }
  17414             BREAK;
  17415         CASE(OP_for_await_of_start):
  17416             if (js_for_of_start(ctx, sp, TRUE))
  17417                 goto exception;
  17418             sp += 1;
  17419             *sp++ = JS_NewCatchOffset(ctx, 0);
  17420             BREAK;
  17421         CASE(OP_iterator_get_value_done):
  17422             if (js_iterator_get_value_done(ctx, sp))
  17423                 goto exception;
  17424             sp += 1;
  17425             BREAK;
  17426         CASE(OP_iterator_check_object):
  17427             if (unlikely(!JS_IsObject(sp[-1]))) {
  17428                 JS_ThrowTypeError(ctx, "iterator must return an object");
  17429                 goto exception;
  17430             }
  17431             BREAK;
  17432 
  17433         CASE(OP_iterator_close):
  17434             /* iter_obj next catch_offset -> */
  17435             sp--; /* drop the catch offset to avoid getting caught by exception */
  17436             JS_FreeValue(ctx, sp[-1]); /* drop the next method */
  17437             sp--;
  17438             if (!JS_IsUndefined(sp[-1])) {
  17439                 if (JS_IteratorClose(ctx, sp[-1], FALSE))
  17440                     goto exception;
  17441                 JS_FreeValue(ctx, sp[-1]);
  17442             }
  17443             sp--;
  17444             BREAK;
  17445         CASE(OP_nip_catch):
  17446             {
  17447                 JSValue ret_val;
  17448                 /* catch_offset ... ret_val -> ret_eval */
  17449                 ret_val = *--sp;
  17450                 while (sp > stack_buf &&
  17451                        JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_CATCH_OFFSET) {
  17452                     JS_FreeValue(ctx, *--sp);
  17453                 }
  17454                 if (unlikely(sp == stack_buf)) {
  17455                     JS_ThrowInternalError(ctx, "nip_catch");
  17456                     JS_FreeValue(ctx, ret_val);
  17457                     goto exception;
  17458                 }
  17459                 sp[-1] = ret_val;
  17460             }
  17461             BREAK;
  17462 
  17463         CASE(OP_iterator_next):
  17464             /* stack: iter_obj next catch_offset val */
  17465             {
  17466                 JSValue ret;
  17467                 ret = JS_Call(ctx, sp[-3], sp[-4],
  17468                               1, (JSValueConst *)(sp - 1));
  17469                 if (JS_IsException(ret))
  17470                     goto exception;
  17471                 JS_FreeValue(ctx, sp[-1]);
  17472                 sp[-1] = ret;
  17473             }
  17474             BREAK;
  17475 
  17476         CASE(OP_iterator_call):
  17477             /* stack: iter_obj next catch_offset val */
  17478             {
  17479                 JSValue method, ret;
  17480                 BOOL ret_flag;
  17481                 int flags;
  17482                 flags = *pc++;
  17483                 method = JS_GetProperty(ctx, sp[-4], (flags & 1) ?
  17484                                         JS_ATOM_throw : JS_ATOM_return);
  17485                 if (JS_IsException(method))
  17486                     goto exception;
  17487                 if (JS_IsUndefined(method) || JS_IsNull(method)) {
  17488                     ret_flag = TRUE;
  17489                 } else {
  17490                     if (flags & 2) {
  17491                         /* no argument */
  17492                         ret = JS_CallFree(ctx, method, sp[-4],
  17493                                           0, NULL);
  17494                     } else {
  17495                         ret = JS_CallFree(ctx, method, sp[-4],
  17496                                           1, (JSValueConst *)(sp - 1));
  17497                     }
  17498                     if (JS_IsException(ret))
  17499                         goto exception;
  17500                     JS_FreeValue(ctx, sp[-1]);
  17501                     sp[-1] = ret;
  17502                     ret_flag = FALSE;
  17503                 }
  17504                 sp[0] = JS_NewBool(ctx, ret_flag);
  17505                 sp += 1;
  17506             }
  17507             BREAK;
  17508 
  17509         CASE(OP_lnot):
  17510             {
  17511                 int res;
  17512                 JSValue op1;
  17513 
  17514                 op1 = sp[-1];
  17515                 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
  17516                     res = JS_VALUE_GET_INT(op1) != 0;
  17517                 } else {
  17518                     res = JS_ToBoolFree(ctx, op1);
  17519                 }
  17520                 sp[-1] = JS_NewBool(ctx, !res);
  17521             }
  17522             BREAK;
  17523 
  17524         CASE(OP_get_field):
  17525             {
  17526                 JSValue val;
  17527                 JSAtom atom;
  17528                 atom = get_u32(pc);
  17529                 pc += 4;
  17530 
  17531                 val = JS_GetProperty(ctx, sp[-1], atom);
  17532                 if (unlikely(JS_IsException(val)))
  17533                     goto exception;
  17534                 JS_FreeValue(ctx, sp[-1]);
  17535                 sp[-1] = val;
  17536             }
  17537             BREAK;
  17538 
  17539         CASE(OP_get_field2):
  17540             {
  17541                 JSValue val;
  17542                 JSAtom atom;
  17543                 atom = get_u32(pc);
  17544                 pc += 4;
  17545 
  17546                 val = JS_GetProperty(ctx, sp[-1], atom);
  17547                 if (unlikely(JS_IsException(val)))
  17548                     goto exception;
  17549                 *sp++ = val;
  17550             }
  17551             BREAK;
  17552 
  17553         CASE(OP_put_field):
  17554             {
  17555                 int ret;
  17556                 JSAtom atom;
  17557                 atom = get_u32(pc);
  17558                 pc += 4;
  17559 
  17560                 ret = JS_SetPropertyInternal(ctx, sp[-2], atom, sp[-1], sp[-2],
  17561                                              JS_PROP_THROW_STRICT);
  17562                 JS_FreeValue(ctx, sp[-2]);
  17563                 sp -= 2;
  17564                 if (unlikely(ret < 0))
  17565                     goto exception;
  17566             }
  17567             BREAK;
  17568 
  17569         CASE(OP_private_symbol):
  17570             {
  17571                 JSAtom atom;
  17572                 JSValue val;
  17573 
  17574                 atom = get_u32(pc);
  17575                 pc += 4;
  17576                 val = JS_NewSymbolFromAtom(ctx, atom, JS_ATOM_TYPE_PRIVATE);
  17577                 if (JS_IsException(val))
  17578                     goto exception;
  17579                 *sp++ = val;
  17580             }
  17581             BREAK;
  17582 
  17583         CASE(OP_get_private_field):
  17584             {
  17585                 JSValue val;
  17586 
  17587                 val = JS_GetPrivateField(ctx, sp[-2], sp[-1]);
  17588                 JS_FreeValue(ctx, sp[-1]);
  17589                 JS_FreeValue(ctx, sp[-2]);
  17590                 sp[-2] = val;
  17591                 sp--;
  17592                 if (unlikely(JS_IsException(val)))
  17593                     goto exception;
  17594             }
  17595             BREAK;
  17596 
  17597         CASE(OP_put_private_field):
  17598             {
  17599                 int ret;
  17600                 ret = JS_SetPrivateField(ctx, sp[-3], sp[-1], sp[-2]);
  17601                 JS_FreeValue(ctx, sp[-3]);
  17602                 JS_FreeValue(ctx, sp[-1]);
  17603                 sp -= 3;
  17604                 if (unlikely(ret < 0))
  17605                     goto exception;
  17606             }
  17607             BREAK;
  17608 
  17609         CASE(OP_define_private_field):
  17610             {
  17611                 int ret;
  17612                 ret = JS_DefinePrivateField(ctx, sp[-3], sp[-2], sp[-1]);
  17613                 JS_FreeValue(ctx, sp[-2]);
  17614                 sp -= 2;
  17615                 if (unlikely(ret < 0))
  17616                     goto exception;
  17617             }
  17618             BREAK;
  17619 
  17620         CASE(OP_define_field):
  17621             {
  17622                 int ret;
  17623                 JSAtom atom;
  17624                 atom = get_u32(pc);
  17625                 pc += 4;
  17626 
  17627                 ret = JS_DefinePropertyValue(ctx, sp[-2], atom, sp[-1],
  17628                                              JS_PROP_C_W_E | JS_PROP_THROW);
  17629                 sp--;
  17630                 if (unlikely(ret < 0))
  17631                     goto exception;
  17632             }
  17633             BREAK;
  17634 
  17635         CASE(OP_set_name):
  17636             {
  17637                 int ret;
  17638                 JSAtom atom;
  17639                 atom = get_u32(pc);
  17640                 pc += 4;
  17641 
  17642                 ret = JS_DefineObjectName(ctx, sp[-1], atom, JS_PROP_CONFIGURABLE);
  17643                 if (unlikely(ret < 0))
  17644                     goto exception;
  17645             }
  17646             BREAK;
  17647         CASE(OP_set_name_computed):
  17648             {
  17649                 int ret;
  17650                 ret = JS_DefineObjectNameComputed(ctx, sp[-1], sp[-2], JS_PROP_CONFIGURABLE);
  17651                 if (unlikely(ret < 0))
  17652                     goto exception;
  17653             }
  17654             BREAK;
  17655         CASE(OP_set_proto):
  17656             {
  17657                 JSValue proto;
  17658                 proto = sp[-1];
  17659                 if (JS_IsObject(proto) || JS_IsNull(proto)) {
  17660                     if (JS_SetPrototypeInternal(ctx, sp[-2], proto, TRUE) < 0)
  17661                         goto exception;
  17662                 }
  17663                 JS_FreeValue(ctx, proto);
  17664                 sp--;
  17665             }
  17666             BREAK;
  17667         CASE(OP_set_home_object):
  17668             js_method_set_home_object(ctx, sp[-1], sp[-2]);
  17669             BREAK;
  17670         CASE(OP_define_method):
  17671         CASE(OP_define_method_computed):
  17672             {
  17673                 JSValue getter, setter, value;
  17674                 JSValueConst obj;
  17675                 JSAtom atom;
  17676                 int flags, ret, op_flags;
  17677                 BOOL is_computed;
  17678 #define OP_DEFINE_METHOD_METHOD 0
  17679 #define OP_DEFINE_METHOD_GETTER 1
  17680 #define OP_DEFINE_METHOD_SETTER 2
  17681 #define OP_DEFINE_METHOD_ENUMERABLE 4
  17682 
  17683                 is_computed = (opcode == OP_define_method_computed);
  17684                 if (is_computed) {
  17685                     atom = JS_ValueToAtom(ctx, sp[-2]);
  17686                     if (unlikely(atom == JS_ATOM_NULL))
  17687                         goto exception;
  17688                     opcode += OP_define_method - OP_define_method_computed;
  17689                 } else {
  17690                     atom = get_u32(pc);
  17691                     pc += 4;
  17692                 }
  17693                 op_flags = *pc++;
  17694 
  17695                 obj = sp[-2 - is_computed];
  17696                 flags = JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE |
  17697                     JS_PROP_HAS_ENUMERABLE | JS_PROP_THROW;
  17698                 if (op_flags & OP_DEFINE_METHOD_ENUMERABLE)
  17699                     flags |= JS_PROP_ENUMERABLE;
  17700                 op_flags &= 3;
  17701                 value = JS_UNDEFINED;
  17702                 getter = JS_UNDEFINED;
  17703                 setter = JS_UNDEFINED;
  17704                 if (op_flags == OP_DEFINE_METHOD_METHOD) {
  17705                     value = sp[-1];
  17706                     flags |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE;
  17707                 } else if (op_flags == OP_DEFINE_METHOD_GETTER) {
  17708                     getter = sp[-1];
  17709                     flags |= JS_PROP_HAS_GET;
  17710                 } else {
  17711                     setter = sp[-1];
  17712                     flags |= JS_PROP_HAS_SET;
  17713                 }
  17714                 ret = js_method_set_properties(ctx, sp[-1], atom, flags, obj);
  17715                 if (ret >= 0) {
  17716                     ret = JS_DefineProperty(ctx, obj, atom, value,
  17717                                             getter, setter, flags);
  17718                 }
  17719                 JS_FreeValue(ctx, sp[-1]);
  17720                 if (is_computed) {
  17721                     JS_FreeAtom(ctx, atom);
  17722                     JS_FreeValue(ctx, sp[-2]);
  17723                 }
  17724                 sp -= 1 + is_computed;
  17725                 if (unlikely(ret < 0))
  17726                     goto exception;
  17727             }
  17728             BREAK;
  17729 
  17730         CASE(OP_define_class):
  17731         CASE(OP_define_class_computed):
  17732             {
  17733                 int class_flags;
  17734                 JSAtom atom;
  17735 
  17736                 atom = get_u32(pc);
  17737                 class_flags = pc[4];
  17738                 pc += 5;
  17739                 if (js_op_define_class(ctx, sp, atom, class_flags,
  17740                                        var_refs, sf,
  17741                                        (opcode == OP_define_class_computed)) < 0)
  17742                     goto exception;
  17743             }
  17744             BREAK;
  17745 
  17746         CASE(OP_get_array_el):
  17747             {
  17748                 JSValue val;
  17749 
  17750                 val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]);
  17751                 JS_FreeValue(ctx, sp[-2]);
  17752                 sp[-2] = val;
  17753                 sp--;
  17754                 if (unlikely(JS_IsException(val)))
  17755                     goto exception;
  17756             }
  17757             BREAK;
  17758 
  17759         CASE(OP_get_array_el2):
  17760             {
  17761                 JSValue val;
  17762 
  17763                 val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]);
  17764                 sp[-1] = val;
  17765                 if (unlikely(JS_IsException(val)))
  17766                     goto exception;
  17767             }
  17768             BREAK;
  17769 
  17770         CASE(OP_get_ref_value):
  17771             {
  17772                 JSValue val;
  17773                 if (unlikely(JS_IsUndefined(sp[-2]))) {
  17774                     JSAtom atom = JS_ValueToAtom(ctx, sp[-1]);
  17775                     if (atom != JS_ATOM_NULL) {
  17776                         JS_ThrowReferenceErrorNotDefined(ctx, atom);
  17777                         JS_FreeAtom(ctx, atom);
  17778                     }
  17779                     goto exception;
  17780                 }
  17781                 val = JS_GetPropertyValue(ctx, sp[-2],
  17782                                           JS_DupValue(ctx, sp[-1]));
  17783                 if (unlikely(JS_IsException(val)))
  17784                     goto exception;
  17785                 sp[0] = val;
  17786                 sp++;
  17787             }
  17788             BREAK;
  17789 
  17790         CASE(OP_get_super_value):
  17791             {
  17792                 JSValue val;
  17793                 JSAtom atom;
  17794                 atom = JS_ValueToAtom(ctx, sp[-1]);
  17795                 if (unlikely(atom == JS_ATOM_NULL))
  17796                     goto exception;
  17797                 val = JS_GetPropertyInternal(ctx, sp[-2], atom, sp[-3], FALSE);
  17798                 JS_FreeAtom(ctx, atom);
  17799                 if (unlikely(JS_IsException(val)))
  17800                     goto exception;
  17801                 JS_FreeValue(ctx, sp[-1]);
  17802                 JS_FreeValue(ctx, sp[-2]);
  17803                 JS_FreeValue(ctx, sp[-3]);
  17804                 sp[-3] = val;
  17805                 sp -= 2;
  17806             }
  17807             BREAK;
  17808 
  17809         CASE(OP_put_array_el):
  17810             {
  17811                 int ret;
  17812 
  17813                 ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT);
  17814                 JS_FreeValue(ctx, sp[-3]);
  17815                 sp -= 3;
  17816                 if (unlikely(ret < 0))
  17817                     goto exception;
  17818             }
  17819             BREAK;
  17820 
  17821         CASE(OP_put_ref_value):
  17822             {
  17823                 int ret, flags;
  17824                 flags = JS_PROP_THROW_STRICT;
  17825                 if (unlikely(JS_IsUndefined(sp[-3]))) {
  17826                     if (is_strict_mode(ctx)) {
  17827                         JSAtom atom = JS_ValueToAtom(ctx, sp[-2]);
  17828                         if (atom != JS_ATOM_NULL) {
  17829                             JS_ThrowReferenceErrorNotDefined(ctx, atom);
  17830                             JS_FreeAtom(ctx, atom);
  17831                         }
  17832                         goto exception;
  17833                     } else {
  17834                         sp[-3] = JS_DupValue(ctx, ctx->global_obj);
  17835                     }
  17836                 } else {
  17837                     if (is_strict_mode(ctx))
  17838                         flags |= JS_PROP_NO_ADD;
  17839                 }
  17840                 ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], flags);
  17841                 JS_FreeValue(ctx, sp[-3]);
  17842                 sp -= 3;
  17843                 if (unlikely(ret < 0))
  17844                     goto exception;
  17845             }
  17846             BREAK;
  17847 
  17848         CASE(OP_put_super_value):
  17849             {
  17850                 int ret;
  17851                 JSAtom atom;
  17852                 if (JS_VALUE_GET_TAG(sp[-3]) != JS_TAG_OBJECT) {
  17853                     JS_ThrowTypeErrorNotAnObject(ctx);
  17854                     goto exception;
  17855                 }
  17856                 atom = JS_ValueToAtom(ctx, sp[-2]);
  17857                 if (unlikely(atom == JS_ATOM_NULL))
  17858                     goto exception;
  17859                 ret = JS_SetPropertyInternal(ctx, sp[-3], atom, sp[-1], sp[-4],
  17860                                              JS_PROP_THROW_STRICT);
  17861                 JS_FreeAtom(ctx, atom);
  17862                 JS_FreeValue(ctx, sp[-4]);
  17863                 JS_FreeValue(ctx, sp[-3]);
  17864                 JS_FreeValue(ctx, sp[-2]);
  17865                 sp -= 4;
  17866                 if (ret < 0)
  17867                     goto exception;
  17868             }
  17869             BREAK;
  17870 
  17871         CASE(OP_define_array_el):
  17872             {
  17873                 int ret;
  17874                 ret = JS_DefinePropertyValueValue(ctx, sp[-3], JS_DupValue(ctx, sp[-2]), sp[-1],
  17875                                                   JS_PROP_C_W_E | JS_PROP_THROW);
  17876                 sp -= 1;
  17877                 if (unlikely(ret < 0))
  17878                     goto exception;
  17879             }
  17880             BREAK;
  17881 
  17882         CASE(OP_append):    /* array pos enumobj -- array pos */
  17883             {
  17884                 if (js_append_enumerate(ctx, sp))
  17885                     goto exception;
  17886                 JS_FreeValue(ctx, *--sp);
  17887             }
  17888             BREAK;
  17889 
  17890         CASE(OP_copy_data_properties):    /* target source excludeList */
  17891             {
  17892                 /* stack offsets (-1 based):
  17893                    2 bits for target,
  17894                    3 bits for source,
  17895                    2 bits for exclusionList */
  17896                 int mask;
  17897 
  17898                 mask = *pc++;
  17899                 if (JS_CopyDataProperties(ctx, sp[-1 - (mask & 3)],
  17900                                           sp[-1 - ((mask >> 2) & 7)],
  17901                                           sp[-1 - ((mask >> 5) & 7)], 0))
  17902                     goto exception;
  17903             }
  17904             BREAK;
  17905 
  17906         CASE(OP_add):
  17907             {
  17908                 JSValue op1, op2;
  17909                 op1 = sp[-2];
  17910                 op2 = sp[-1];
  17911                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
  17912                     int64_t r;
  17913                     r = (int64_t)JS_VALUE_GET_INT(op1) + JS_VALUE_GET_INT(op2);
  17914                     if (unlikely((int)r != r))
  17915                         goto add_slow;
  17916                     sp[-2] = JS_NewInt32(ctx, r);
  17917                     sp--;
  17918                 } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
  17919                     sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) +
  17920                                              JS_VALUE_GET_FLOAT64(op2));
  17921                     sp--;
  17922                 } else if (JS_IsString(op1) && JS_IsString(op2)) {
  17923                     sp[-2] = JS_ConcatString(ctx, op1, op2);
  17924                     sp--;
  17925                     if (JS_IsException(sp[-1]))
  17926                         goto exception;
  17927                 } else {
  17928                 add_slow:
  17929                     if (js_add_slow(ctx, sp))
  17930                         goto exception;
  17931                     sp--;
  17932                 }
  17933             }
  17934             BREAK;
  17935         CASE(OP_add_loc):
  17936             {
  17937                 JSValue op2;
  17938                 JSValue *pv;
  17939                 int idx;
  17940                 idx = *pc;
  17941                 pc += 1;
  17942 
  17943                 op2 = sp[-1];
  17944                 pv = &var_buf[idx];
  17945                 if (likely(JS_VALUE_IS_BOTH_INT(*pv, op2))) {
  17946                     int64_t r;
  17947                     r = (int64_t)JS_VALUE_GET_INT(*pv) + JS_VALUE_GET_INT(op2);
  17948                     if (unlikely((int)r != r))
  17949                         goto add_loc_slow;
  17950                     *pv = JS_NewInt32(ctx, r);
  17951                     sp--;
  17952                 } else if (JS_VALUE_IS_BOTH_FLOAT(*pv, op2)) {
  17953                     *pv = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(*pv) +
  17954                                                JS_VALUE_GET_FLOAT64(op2));
  17955                     sp--;
  17956                 } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING) {
  17957                     sp--;
  17958                     op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);
  17959                     if (JS_IsException(op2))
  17960                         goto exception;
  17961                     if (JS_ConcatStringInPlace(ctx, JS_VALUE_GET_STRING(*pv), op2)) {
  17962                         JS_FreeValue(ctx, op2);
  17963                     } else {
  17964                         op2 = JS_ConcatString(ctx, JS_DupValue(ctx, *pv), op2);
  17965                         if (JS_IsException(op2))
  17966                             goto exception;
  17967                         set_value(ctx, pv, op2);
  17968                     }
  17969                 } else {
  17970                     JSValue ops[2];
  17971                 add_loc_slow:
  17972                     /* In case of exception, js_add_slow frees ops[0]
  17973                        and ops[1], so we must duplicate *pv */
  17974                     ops[0] = JS_DupValue(ctx, *pv);
  17975                     ops[1] = op2;
  17976                     sp--;
  17977                     if (js_add_slow(ctx, ops + 2))
  17978                         goto exception;
  17979                     set_value(ctx, pv, ops[0]);
  17980                 }
  17981             }
  17982             BREAK;
  17983         CASE(OP_sub):
  17984             {
  17985                 JSValue op1, op2;
  17986                 op1 = sp[-2];
  17987                 op2 = sp[-1];
  17988                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
  17989                     int64_t r;
  17990                     r = (int64_t)JS_VALUE_GET_INT(op1) - JS_VALUE_GET_INT(op2);
  17991                     if (unlikely((int)r != r))
  17992                         goto binary_arith_slow;
  17993                     sp[-2] = JS_NewInt32(ctx, r);
  17994                     sp--;
  17995                 } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
  17996                     sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) -
  17997                                              JS_VALUE_GET_FLOAT64(op2));
  17998                     sp--;
  17999                 } else {
  18000                     goto binary_arith_slow;
  18001                 }
  18002             }
  18003             BREAK;
  18004         CASE(OP_mul):
  18005             {
  18006                 JSValue op1, op2;
  18007                 double d;
  18008                 op1 = sp[-2];
  18009                 op2 = sp[-1];
  18010                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
  18011                     int32_t v1, v2;
  18012                     int64_t r;
  18013                     v1 = JS_VALUE_GET_INT(op1);
  18014                     v2 = JS_VALUE_GET_INT(op2);
  18015                     r = (int64_t)v1 * v2;
  18016                     if (unlikely((int)r != r)) {
  18017 #ifdef CONFIG_BIGNUM
  18018                         if (unlikely(sf->js_mode & JS_MODE_MATH) &&
  18019                             (r < -MAX_SAFE_INTEGER || r > MAX_SAFE_INTEGER))
  18020                             goto binary_arith_slow;
  18021 #endif
  18022                         d = (double)r;
  18023                         goto mul_fp_res;
  18024                     }
  18025                     /* need to test zero case for -0 result */
  18026                     if (unlikely(r == 0 && (v1 | v2) < 0)) {
  18027                         d = -0.0;
  18028                         goto mul_fp_res;
  18029                     }
  18030                     sp[-2] = JS_NewInt32(ctx, r);
  18031                     sp--;
  18032                 } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
  18033 #ifdef CONFIG_BIGNUM
  18034                     if (unlikely(sf->js_mode & JS_MODE_MATH))
  18035                         goto binary_arith_slow;
  18036 #endif
  18037                     d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2);
  18038                 mul_fp_res:
  18039                     sp[-2] = __JS_NewFloat64(ctx, d);
  18040                     sp--;
  18041                 } else {
  18042                     goto binary_arith_slow;
  18043                 }
  18044             }
  18045             BREAK;
  18046         CASE(OP_div):
  18047             {
  18048                 JSValue op1, op2;
  18049                 op1 = sp[-2];
  18050                 op2 = sp[-1];
  18051                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
  18052                     int v1, v2;
  18053                     if (unlikely(sf->js_mode & JS_MODE_MATH))
  18054                         goto binary_arith_slow;
  18055                     v1 = JS_VALUE_GET_INT(op1);
  18056                     v2 = JS_VALUE_GET_INT(op2);
  18057                     sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2);
  18058                     sp--;
  18059                 } else {
  18060                     goto binary_arith_slow;
  18061                 }
  18062             }
  18063             BREAK;
  18064         CASE(OP_mod):
  18065 #ifdef CONFIG_BIGNUM
  18066         CASE(OP_math_mod):
  18067 #endif
  18068             {
  18069                 JSValue op1, op2;
  18070                 op1 = sp[-2];
  18071                 op2 = sp[-1];
  18072                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
  18073                     int v1, v2, r;
  18074                     v1 = JS_VALUE_GET_INT(op1);
  18075                     v2 = JS_VALUE_GET_INT(op2);
  18076                     /* We must avoid v2 = 0, v1 = INT32_MIN and v2 =
  18077                        -1 and the cases where the result is -0. */
  18078                     if (unlikely(v1 < 0 || v2 <= 0))
  18079                         goto binary_arith_slow;
  18080                     r = v1 % v2;
  18081                     sp[-2] = JS_NewInt32(ctx, r);
  18082                     sp--;
  18083                 } else {
  18084                     goto binary_arith_slow;
  18085                 }
  18086             }
  18087             BREAK;
  18088         CASE(OP_pow):
  18089         binary_arith_slow:
  18090             if (js_binary_arith_slow(ctx, sp, opcode))
  18091                 goto exception;
  18092             sp--;
  18093             BREAK;
  18094 
  18095         CASE(OP_plus):
  18096             {
  18097                 JSValue op1;
  18098                 uint32_t tag;
  18099                 op1 = sp[-1];
  18100                 tag = JS_VALUE_GET_TAG(op1);
  18101                 if (tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag)) {
  18102                 } else {
  18103                     if (js_unary_arith_slow(ctx, sp, opcode))
  18104                         goto exception;
  18105                 }
  18106             }
  18107             BREAK;
  18108         CASE(OP_neg):
  18109             {
  18110                 JSValue op1;
  18111                 uint32_t tag;
  18112                 int val;
  18113                 double d;
  18114                 op1 = sp[-1];
  18115                 tag = JS_VALUE_GET_TAG(op1);
  18116                 if (tag == JS_TAG_INT) {
  18117                     val = JS_VALUE_GET_INT(op1);
  18118                     /* Note: -0 cannot be expressed as integer */
  18119                     if (unlikely(val == 0)) {
  18120                         d = -0.0;
  18121                         goto neg_fp_res;
  18122                     }
  18123                     if (unlikely(val == INT32_MIN)) {
  18124                         d = -(double)val;
  18125                         goto neg_fp_res;
  18126                     }
  18127                     sp[-1] = JS_NewInt32(ctx, -val);
  18128                 } else if (JS_TAG_IS_FLOAT64(tag)) {
  18129                     d = -JS_VALUE_GET_FLOAT64(op1);
  18130                 neg_fp_res:
  18131                     sp[-1] = __JS_NewFloat64(ctx, d);
  18132                 } else {
  18133                     if (js_unary_arith_slow(ctx, sp, opcode))
  18134                         goto exception;
  18135                 }
  18136             }
  18137             BREAK;
  18138         CASE(OP_inc):
  18139             {
  18140                 JSValue op1;
  18141                 int val;
  18142                 op1 = sp[-1];
  18143                 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
  18144                     val = JS_VALUE_GET_INT(op1);
  18145                     if (unlikely(val == INT32_MAX))
  18146                         goto inc_slow;
  18147                     sp[-1] = JS_NewInt32(ctx, val + 1);
  18148                 } else {
  18149                 inc_slow:
  18150                     if (js_unary_arith_slow(ctx, sp, opcode))
  18151                         goto exception;
  18152                 }
  18153             }
  18154             BREAK;
  18155         CASE(OP_dec):
  18156             {
  18157                 JSValue op1;
  18158                 int val;
  18159                 op1 = sp[-1];
  18160                 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
  18161                     val = JS_VALUE_GET_INT(op1);
  18162                     if (unlikely(val == INT32_MIN))
  18163                         goto dec_slow;
  18164                     sp[-1] = JS_NewInt32(ctx, val - 1);
  18165                 } else {
  18166                 dec_slow:
  18167                     if (js_unary_arith_slow(ctx, sp, opcode))
  18168                         goto exception;
  18169                 }
  18170             }
  18171             BREAK;
  18172         CASE(OP_post_inc):
  18173         CASE(OP_post_dec):
  18174             if (js_post_inc_slow(ctx, sp, opcode))
  18175                 goto exception;
  18176             sp++;
  18177             BREAK;
  18178         CASE(OP_inc_loc):
  18179             {
  18180                 JSValue op1;
  18181                 int val;
  18182                 int idx;
  18183                 idx = *pc;
  18184                 pc += 1;
  18185 
  18186                 op1 = var_buf[idx];
  18187                 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
  18188                     val = JS_VALUE_GET_INT(op1);
  18189                     if (unlikely(val == INT32_MAX))
  18190                         goto inc_loc_slow;
  18191                     var_buf[idx] = JS_NewInt32(ctx, val + 1);
  18192                 } else {
  18193                 inc_loc_slow:
  18194                     /* must duplicate otherwise the variable value may
  18195                        be destroyed before JS code accesses it */
  18196                     op1 = JS_DupValue(ctx, op1);
  18197                     if (js_unary_arith_slow(ctx, &op1 + 1, OP_inc))
  18198                         goto exception;
  18199                     set_value(ctx, &var_buf[idx], op1);
  18200                 }
  18201             }
  18202             BREAK;
  18203         CASE(OP_dec_loc):
  18204             {
  18205                 JSValue op1;
  18206                 int val;
  18207                 int idx;
  18208                 idx = *pc;
  18209                 pc += 1;
  18210 
  18211                 op1 = var_buf[idx];
  18212                 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
  18213                     val = JS_VALUE_GET_INT(op1);
  18214                     if (unlikely(val == INT32_MIN))
  18215                         goto dec_loc_slow;
  18216                     var_buf[idx] = JS_NewInt32(ctx, val - 1);
  18217                 } else {
  18218                 dec_loc_slow:
  18219                     /* must duplicate otherwise the variable value may
  18220                        be destroyed before JS code accesses it */
  18221                     op1 = JS_DupValue(ctx, op1);
  18222                     if (js_unary_arith_slow(ctx, &op1 + 1, OP_dec))
  18223                         goto exception;
  18224                     set_value(ctx, &var_buf[idx], op1);
  18225                 }
  18226             }
  18227             BREAK;
  18228         CASE(OP_not):
  18229             {
  18230                 JSValue op1;
  18231                 op1 = sp[-1];
  18232                 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
  18233                     sp[-1] = JS_NewInt32(ctx, ~JS_VALUE_GET_INT(op1));
  18234                 } else {
  18235                     if (js_not_slow(ctx, sp))
  18236                         goto exception;
  18237                 }
  18238             }
  18239             BREAK;
  18240 
  18241         CASE(OP_shl):
  18242             {
  18243                 JSValue op1, op2;
  18244                 op1 = sp[-2];
  18245                 op2 = sp[-1];
  18246                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
  18247                     uint32_t v1, v2;
  18248                     v1 = JS_VALUE_GET_INT(op1);
  18249                     v2 = JS_VALUE_GET_INT(op2);
  18250 #ifdef CONFIG_BIGNUM
  18251                     {
  18252                         int64_t r;
  18253                         if (unlikely(sf->js_mode & JS_MODE_MATH)) {
  18254                             if (v2 > 0x1f)
  18255                                 goto shl_slow;
  18256                             r = (int64_t)v1 << v2;
  18257                             if ((int)r != r)
  18258                                 goto shl_slow;
  18259                         } else {
  18260                             v2 &= 0x1f;
  18261                         }
  18262                     }
  18263 #else
  18264                     v2 &= 0x1f;
  18265 #endif
  18266                     sp[-2] = JS_NewInt32(ctx, v1 << v2);
  18267                     sp--;
  18268                 } else {
  18269 #ifdef CONFIG_BIGNUM
  18270                 shl_slow:
  18271 #endif
  18272                     if (js_binary_logic_slow(ctx, sp, opcode))
  18273                         goto exception;
  18274                     sp--;
  18275                 }
  18276             }
  18277             BREAK;
  18278         CASE(OP_shr):
  18279             {
  18280                 JSValue op1, op2;
  18281                 op1 = sp[-2];
  18282                 op2 = sp[-1];
  18283                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
  18284                     uint32_t v2;
  18285                     v2 = JS_VALUE_GET_INT(op2);
  18286                     /* v1 >>> v2 retains its JS semantics if CONFIG_BIGNUM */
  18287                     v2 &= 0x1f;
  18288                     sp[-2] = JS_NewUint32(ctx,
  18289                                           (uint32_t)JS_VALUE_GET_INT(op1) >>
  18290                                           v2);
  18291                     sp--;
  18292                 } else {
  18293                     if (js_shr_slow(ctx, sp))
  18294                         goto exception;
  18295                     sp--;
  18296                 }
  18297             }
  18298             BREAK;
  18299         CASE(OP_sar):
  18300             {
  18301                 JSValue op1, op2;
  18302                 op1 = sp[-2];
  18303                 op2 = sp[-1];
  18304                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
  18305                     uint32_t v2;
  18306                     v2 = JS_VALUE_GET_INT(op2);
  18307 #ifdef CONFIG_BIGNUM
  18308                     if (unlikely(v2 > 0x1f)) {
  18309                         if (unlikely(sf->js_mode & JS_MODE_MATH))
  18310                             goto sar_slow;
  18311                         else
  18312                             v2 &= 0x1f;
  18313                     }
  18314 #else
  18315                     v2 &= 0x1f;
  18316 #endif
  18317                     sp[-2] = JS_NewInt32(ctx,
  18318                                           (int)JS_VALUE_GET_INT(op1) >> v2);
  18319                     sp--;
  18320                 } else {
  18321 #ifdef CONFIG_BIGNUM
  18322                 sar_slow:
  18323 #endif
  18324                     if (js_binary_logic_slow(ctx, sp, opcode))
  18325                         goto exception;
  18326                     sp--;
  18327                 }
  18328             }
  18329             BREAK;
  18330         CASE(OP_and):
  18331             {
  18332                 JSValue op1, op2;
  18333                 op1 = sp[-2];
  18334                 op2 = sp[-1];
  18335                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
  18336                     sp[-2] = JS_NewInt32(ctx,
  18337                                          JS_VALUE_GET_INT(op1) &
  18338                                          JS_VALUE_GET_INT(op2));
  18339                     sp--;
  18340                 } else {
  18341                     if (js_binary_logic_slow(ctx, sp, opcode))
  18342                         goto exception;
  18343                     sp--;
  18344                 }
  18345             }
  18346             BREAK;
  18347         CASE(OP_or):
  18348             {
  18349                 JSValue op1, op2;
  18350                 op1 = sp[-2];
  18351                 op2 = sp[-1];
  18352                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
  18353                     sp[-2] = JS_NewInt32(ctx,
  18354                                          JS_VALUE_GET_INT(op1) |
  18355                                          JS_VALUE_GET_INT(op2));
  18356                     sp--;
  18357                 } else {
  18358                     if (js_binary_logic_slow(ctx, sp, opcode))
  18359                         goto exception;
  18360                     sp--;
  18361                 }
  18362             }
  18363             BREAK;
  18364         CASE(OP_xor):
  18365             {
  18366                 JSValue op1, op2;
  18367                 op1 = sp[-2];
  18368                 op2 = sp[-1];
  18369                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
  18370                     sp[-2] = JS_NewInt32(ctx,
  18371                                          JS_VALUE_GET_INT(op1) ^
  18372                                          JS_VALUE_GET_INT(op2));
  18373                     sp--;
  18374                 } else {
  18375                     if (js_binary_logic_slow(ctx, sp, opcode))
  18376                         goto exception;
  18377                     sp--;
  18378                 }
  18379             }
  18380             BREAK;
  18381 
  18382 
  18383 #define OP_CMP(opcode, binary_op, slow_call)              \
  18384             CASE(opcode):                                 \
  18385                 {                                         \
  18386                 JSValue op1, op2;                         \
  18387                 op1 = sp[-2];                             \
  18388                 op2 = sp[-1];                                   \
  18389                 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {           \
  18390                     sp[-2] = JS_NewBool(ctx, JS_VALUE_GET_INT(op1) binary_op JS_VALUE_GET_INT(op2)); \
  18391                     sp--;                                               \
  18392                 } else {                                                \
  18393                     if (slow_call)                                      \
  18394                         goto exception;                                 \
  18395                     sp--;                                               \
  18396                 }                                                       \
  18397                 }                                                       \
  18398             BREAK
  18399 
  18400             OP_CMP(OP_lt, <, js_relational_slow(ctx, sp, opcode));
  18401             OP_CMP(OP_lte, <=, js_relational_slow(ctx, sp, opcode));
  18402             OP_CMP(OP_gt, >, js_relational_slow(ctx, sp, opcode));
  18403             OP_CMP(OP_gte, >=, js_relational_slow(ctx, sp, opcode));
  18404             OP_CMP(OP_eq, ==, js_eq_slow(ctx, sp, 0));
  18405             OP_CMP(OP_neq, !=, js_eq_slow(ctx, sp, 1));
  18406             OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, sp, 0));
  18407             OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, sp, 1));
  18408 
  18409 #ifdef CONFIG_BIGNUM
  18410         CASE(OP_mul_pow10):
  18411             if (rt->bigfloat_ops.mul_pow10(ctx, sp))
  18412                 goto exception;
  18413             sp--;
  18414             BREAK;
  18415 #endif
  18416         CASE(OP_in):
  18417             if (js_operator_in(ctx, sp))
  18418                 goto exception;
  18419             sp--;
  18420             BREAK;
  18421         CASE(OP_private_in):
  18422             if (js_operator_private_in(ctx, sp))
  18423                 goto exception;
  18424             sp--;
  18425             BREAK;
  18426         CASE(OP_instanceof):
  18427             if (js_operator_instanceof(ctx, sp))
  18428                 goto exception;
  18429             sp--;
  18430             BREAK;
  18431         CASE(OP_typeof):
  18432             {
  18433                 JSValue op1;
  18434                 JSAtom atom;
  18435 
  18436                 op1 = sp[-1];
  18437                 atom = js_operator_typeof(ctx, op1);
  18438                 JS_FreeValue(ctx, op1);
  18439                 sp[-1] = JS_AtomToString(ctx, atom);
  18440             }
  18441             BREAK;
  18442         CASE(OP_delete):
  18443             if (js_operator_delete(ctx, sp))
  18444                 goto exception;
  18445             sp--;
  18446             BREAK;
  18447         CASE(OP_delete_var):
  18448             {
  18449                 JSAtom atom;
  18450                 int ret;
  18451 
  18452                 atom = get_u32(pc);
  18453                 pc += 4;
  18454 
  18455                 ret = JS_DeleteProperty(ctx, ctx->global_obj, atom, 0);
  18456                 if (unlikely(ret < 0))
  18457                     goto exception;
  18458                 *sp++ = JS_NewBool(ctx, ret);
  18459             }
  18460             BREAK;
  18461 
  18462         CASE(OP_to_object):
  18463             if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_OBJECT) {
  18464                 ret_val = JS_ToObject(ctx, sp[-1]);
  18465                 if (JS_IsException(ret_val))
  18466                     goto exception;
  18467                 JS_FreeValue(ctx, sp[-1]);
  18468                 sp[-1] = ret_val;
  18469             }
  18470             BREAK;
  18471 
  18472         CASE(OP_to_propkey):
  18473             switch (JS_VALUE_GET_TAG(sp[-1])) {
  18474             case JS_TAG_INT:
  18475             case JS_TAG_STRING:
  18476             case JS_TAG_SYMBOL:
  18477                 break;
  18478             default:
  18479                 ret_val = JS_ToPropertyKey(ctx, sp[-1]);
  18480                 if (JS_IsException(ret_val))
  18481                     goto exception;
  18482                 JS_FreeValue(ctx, sp[-1]);
  18483                 sp[-1] = ret_val;
  18484                 break;
  18485             }
  18486             BREAK;
  18487 
  18488         CASE(OP_to_propkey2):
  18489             /* must be tested first */
  18490             if (unlikely(JS_IsUndefined(sp[-2]) || JS_IsNull(sp[-2]))) {
  18491                 JS_ThrowTypeError(ctx, "value has no property");
  18492                 goto exception;
  18493             }
  18494             switch (JS_VALUE_GET_TAG(sp[-1])) {
  18495             case JS_TAG_INT:
  18496             case JS_TAG_STRING:
  18497             case JS_TAG_SYMBOL:
  18498                 break;
  18499             default:
  18500                 ret_val = JS_ToPropertyKey(ctx, sp[-1]);
  18501                 if (JS_IsException(ret_val))
  18502                     goto exception;
  18503                 JS_FreeValue(ctx, sp[-1]);
  18504                 sp[-1] = ret_val;
  18505                 break;
  18506             }
  18507             BREAK;
  18508 #if 0
  18509         CASE(OP_to_string):
  18510             if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_STRING) {
  18511                 ret_val = JS_ToString(ctx, sp[-1]);
  18512                 if (JS_IsException(ret_val))
  18513                     goto exception;
  18514                 JS_FreeValue(ctx, sp[-1]);
  18515                 sp[-1] = ret_val;
  18516             }
  18517             BREAK;
  18518 #endif
  18519         CASE(OP_with_get_var):
  18520         CASE(OP_with_put_var):
  18521         CASE(OP_with_delete_var):
  18522         CASE(OP_with_make_ref):
  18523         CASE(OP_with_get_ref):
  18524         CASE(OP_with_get_ref_undef):
  18525             {
  18526                 JSAtom atom;
  18527                 int32_t diff;
  18528                 JSValue obj, val;
  18529                 int ret, is_with;
  18530                 atom = get_u32(pc);
  18531                 diff = get_u32(pc + 4);
  18532                 is_with = pc[8];
  18533                 pc += 9;
  18534 
  18535                 obj = sp[-1];
  18536                 ret = JS_HasProperty(ctx, obj, atom);
  18537                 if (unlikely(ret < 0))
  18538                     goto exception;
  18539                 if (ret) {
  18540                     if (is_with) {
  18541                         ret = js_has_unscopable(ctx, obj, atom);
  18542                         if (unlikely(ret < 0))
  18543                             goto exception;
  18544                         if (ret)
  18545                             goto no_with;
  18546                     }
  18547                     switch (opcode) {
  18548                     case OP_with_get_var:
  18549                         val = JS_GetProperty(ctx, obj, atom);
  18550                         if (unlikely(JS_IsException(val)))
  18551                             goto exception;
  18552                         set_value(ctx, &sp[-1], val);
  18553                         break;
  18554                     case OP_with_put_var:
  18555                         /* XXX: check if strict mode */
  18556                         ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2], obj,
  18557                                                      JS_PROP_THROW_STRICT);
  18558                         JS_FreeValue(ctx, sp[-1]);
  18559                         sp -= 2;
  18560                         if (unlikely(ret < 0))
  18561                             goto exception;
  18562                         break;
  18563                     case OP_with_delete_var:
  18564                         ret = JS_DeleteProperty(ctx, obj, atom, 0);
  18565                         if (unlikely(ret < 0))
  18566                             goto exception;
  18567                         JS_FreeValue(ctx, sp[-1]);
  18568                         sp[-1] = JS_NewBool(ctx, ret);
  18569                         break;
  18570                     case OP_with_make_ref:
  18571                         /* produce a pair object/propname on the stack */
  18572                         *sp++ = JS_AtomToValue(ctx, atom);
  18573                         break;
  18574                     case OP_with_get_ref:
  18575                         /* produce a pair object/method on the stack */
  18576                         val = JS_GetProperty(ctx, obj, atom);
  18577                         if (unlikely(JS_IsException(val)))
  18578                             goto exception;
  18579                         *sp++ = val;
  18580                         break;
  18581                     case OP_with_get_ref_undef:
  18582                         /* produce a pair undefined/function on the stack */
  18583                         val = JS_GetProperty(ctx, obj, atom);
  18584                         if (unlikely(JS_IsException(val)))
  18585                             goto exception;
  18586                         JS_FreeValue(ctx, sp[-1]);
  18587                         sp[-1] = JS_UNDEFINED;
  18588                         *sp++ = val;
  18589                         break;
  18590                     }
  18591                     pc += diff - 5;
  18592                 } else {
  18593                 no_with:
  18594                     /* if not jumping, drop the object argument */
  18595                     JS_FreeValue(ctx, sp[-1]);
  18596                     sp--;
  18597                 }
  18598             }
  18599             BREAK;
  18600 
  18601         CASE(OP_await):
  18602             ret_val = JS_NewInt32(ctx, FUNC_RET_AWAIT);
  18603             goto done_generator;
  18604         CASE(OP_yield):
  18605             ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD);
  18606             goto done_generator;
  18607         CASE(OP_yield_star):
  18608         CASE(OP_async_yield_star):
  18609             ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD_STAR);
  18610             goto done_generator;
  18611         CASE(OP_return_async):
  18612             ret_val = JS_UNDEFINED;
  18613             goto done_generator;
  18614         CASE(OP_initial_yield):
  18615             ret_val = JS_NewInt32(ctx, FUNC_RET_INITIAL_YIELD);
  18616             goto done_generator;
  18617 
  18618         CASE(OP_nop):
  18619             BREAK;
  18620         CASE(OP_is_undefined_or_null):
  18621             if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED ||
  18622                 JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) {
  18623                 goto set_true;
  18624             } else {
  18625                 goto free_and_set_false;
  18626             }
  18627 #if SHORT_OPCODES
  18628         CASE(OP_is_undefined):
  18629             if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED) {
  18630                 goto set_true;
  18631             } else {
  18632                 goto free_and_set_false;
  18633             }
  18634         CASE(OP_is_null):
  18635             if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) {
  18636                 goto set_true;
  18637             } else {
  18638                 goto free_and_set_false;
  18639             }
  18640             /* XXX: could merge to a single opcode */
  18641         CASE(OP_typeof_is_undefined):
  18642             /* different from OP_is_undefined because of isHTMLDDA */
  18643             if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_undefined) {
  18644                 goto free_and_set_true;
  18645             } else {
  18646                 goto free_and_set_false;
  18647             }
  18648         CASE(OP_typeof_is_function):
  18649             if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) {
  18650                 goto free_and_set_true;
  18651             } else {
  18652                 goto free_and_set_false;
  18653             }
  18654         free_and_set_true:
  18655             JS_FreeValue(ctx, sp[-1]);
  18656 #endif
  18657         set_true:
  18658             sp[-1] = JS_TRUE;
  18659             BREAK;
  18660         free_and_set_false:
  18661             JS_FreeValue(ctx, sp[-1]);
  18662             sp[-1] = JS_FALSE;
  18663             BREAK;
  18664         CASE(OP_invalid):
  18665         DEFAULT:
  18666             JS_ThrowInternalError(ctx, "invalid opcode: pc=%u opcode=0x%02x",
  18667                                   (int)(pc - b->byte_code_buf - 1), opcode);
  18668             goto exception;
  18669         }
  18670     }
  18671  exception:
  18672     if (is_backtrace_needed(ctx, rt->current_exception)) {
  18673         /* add the backtrace information now (it is not done
  18674            before if the exception happens in a bytecode
  18675            operation */
  18676         sf->cur_pc = pc;
  18677         build_backtrace(ctx, rt->current_exception, NULL, 0, 0);
  18678     }
  18679     if (!JS_IsUncatchableError(ctx, rt->current_exception)) {
  18680         while (sp > stack_buf) {
  18681             JSValue val = *--sp;
  18682             JS_FreeValue(ctx, val);
  18683             if (JS_VALUE_GET_TAG(val) == JS_TAG_CATCH_OFFSET) {
  18684                 int pos = JS_VALUE_GET_INT(val);
  18685                 if (pos == 0) {
  18686                     /* enumerator: close it with a throw */
  18687                     JS_FreeValue(ctx, sp[-1]); /* drop the next method */
  18688                     sp--;
  18689                     JS_IteratorClose(ctx, sp[-1], TRUE);
  18690                 } else {
  18691                     *sp++ = rt->current_exception;
  18692                     rt->current_exception = JS_UNINITIALIZED;
  18693                     pc = b->byte_code_buf + pos;
  18694                     goto restart;
  18695                 }
  18696             }
  18697         }
  18698     }
  18699     ret_val = JS_EXCEPTION;
  18700     /* the local variables are freed by the caller in the generator
  18701        case. Hence the label 'done' should never be reached in a
  18702        generator function. */
  18703     if (b->func_kind != JS_FUNC_NORMAL) {
  18704     done_generator:
  18705         sf->cur_pc = pc;
  18706         sf->cur_sp = sp;
  18707     } else {
  18708     done:
  18709         if (unlikely(!list_empty(&sf->var_ref_list))) {
  18710             /* variable references reference the stack: must close them */
  18711             close_var_refs(rt, sf);
  18712         }
  18713         /* free the local variables and stack */
  18714         for(pval = local_buf; pval < sp; pval++) {
  18715             JS_FreeValue(ctx, *pval);
  18716         }
  18717     }
  18718     rt->current_stack_frame = sf->prev_frame;
  18719     return ret_val;
  18720 }
  18721 
  18722 #ifdef PERF_TRAMPOLINE
  18723 
  18724 #include <sys/mman.h>
  18725 #include <fcntl.h>
  18726 #include <unistd.h>
  18727 
  18728 static FILE *
  18729 perf_map_get_file(void)
  18730 {
  18731     static FILE *perf_map_file = NULL;
  18732     if (perf_map_file) {
  18733         return perf_map_file;
  18734     }
  18735     char filename[100];
  18736     pid_t pid = getpid();
  18737     // Location and file name of perf map is hard-coded in perf tool.
  18738     // Use exclusive create flag wit nofollow to prevent symlink attacks.
  18739     int flags = O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW | O_CLOEXEC;
  18740     snprintf(filename, sizeof(filename) - 1, "/tmp/perf-%jd.map",
  18741              (intmax_t)pid);
  18742     int fd = open(filename, flags, 0600);
  18743     if (fd == -1) {
  18744         return NULL;
  18745     }
  18746     perf_map_file = fdopen(fd, "w");
  18747     if (!perf_map_file) {
  18748         close(fd);
  18749         return NULL;
  18750     }
  18751     return perf_map_file;
  18752 }
  18753 
  18754 static void
  18755 perf_map_write_entry(JSContext *ctx, const void *code_addr, unsigned int code_size, JSFunctionBytecode *b)
  18756 {
  18757     FILE *method_file = perf_map_get_file();
  18758     const char *atom_entry = NULL;
  18759     const char *atom_filename = NULL;
  18760     const char *filename = NULL;
  18761     int line = 0;
  18762     if (b->has_debug) {
  18763       line = b->debug.line_num;
  18764     }
  18765     if (b->func_name != JS_ATOM_NULL) {
  18766       atom_entry = JS_AtomToCString(ctx, b->func_name);
  18767     }
  18768     if (b->has_debug && b->debug.filename != JS_ATOM_NULL) {
  18769       atom_filename = JS_AtomToCString(ctx, b->debug.filename);
  18770     }
  18771     if (NULL == atom_filename) {
  18772       filename = "<unknown>";
  18773     } else {
  18774       filename = atom_filename;
  18775     }
  18776     if (NULL == atom_entry) {
  18777       fprintf(method_file, "%p %x js@%s:%u\n", code_addr, code_size, filename, line);
  18778     } else {
  18779       fprintf(method_file, "%p %x js::%s@%s:%u\n", code_addr, code_size, atom_entry, filename, line);
  18780     }
  18781     fflush(method_file);
  18782     JS_FreeCString(ctx, atom_entry);
  18783     JS_FreeCString(ctx, atom_filename);
  18784 }
  18785 
  18786 typedef struct {
  18787   JSContext *caller_ctx;
  18788   JSValueConst func_obj;
  18789   JSValueConst this_obj;
  18790   JSValueConst new_target;
  18791   int argc;
  18792   JSValue *argv;
  18793   int flags;
  18794 } CallInternalArgs;
  18795 
  18796 typedef JSValue CallFn(CallInternalArgs *args);
  18797 typedef JSValue TrampolineFn(CallInternalArgs *args, CallFn fn);
  18798 
  18799 /**
  18800  * For x86-64:
  18801  * push %rbp; mov %rsp,%rbp; call *%rsi; pop %rbp; ret;
  18802 */
  18803 char perf_trampoline_code[] = {0x55, 0x48, 0x89, 0xe5, 0xff, 0xd6, 0x5d, 0xc3};
  18804 
  18805 void *compile_trampoline()
  18806 {
  18807   size_t mem_size = 4096 * 16;
  18808   char *memory =
  18809         mmap(NULL,  // address
  18810              mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS,
  18811              -1,  // fd (not used here)
  18812              0);  // offset (not used here)
  18813   memcpy(memory, perf_trampoline_code, 8);
  18814   mprotect(memory, mem_size, PROT_READ | PROT_EXEC);
  18815   return memory;
  18816 }
  18817 
  18818 static JSValue fallback_trampoline(CallInternalArgs *ci_args, CallFn fn)
  18819 {
  18820   JSValue value;
  18821   value = fn(ci_args);
  18822   return value;
  18823 }
  18824 
  18825 static JSValue JS_CallInternalStruct(CallInternalArgs *ci_args)
  18826 {
  18827     return __JS_CallInternal(ci_args->caller_ctx, ci_args->func_obj,
  18828                            ci_args->this_obj, ci_args->new_target,
  18829                            ci_args->argc, ci_args->argv, ci_args->flags);
  18830 }
  18831 
  18832 static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
  18833                                JSValueConst this_obj, JSValueConst new_target,
  18834                                int argc, JSValue *argv, int flags)
  18835 {
  18836     CallInternalArgs ci_args;
  18837     JSObject *p;
  18838     JSFunctionBytecode *b = NULL;
  18839 
  18840     ci_args.caller_ctx = caller_ctx;
  18841     ci_args.func_obj = func_obj;
  18842     ci_args.this_obj = this_obj;
  18843     ci_args.new_target = new_target;
  18844     ci_args.argc = argc;
  18845     ci_args.argv = argv;
  18846     ci_args.flags = flags;
  18847 
  18848     if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) {
  18849         if (flags & JS_CALL_FLAG_GENERATOR) {
  18850             JSAsyncFunctionState *s = JS_VALUE_GET_PTR(func_obj);
  18851             JSStackFrame *sf;
  18852             /* func_obj get contains a pointer to JSFuncAsyncState */
  18853             /* the stack frame is already allocated */
  18854             sf = &s->frame;
  18855             p = JS_VALUE_GET_OBJ(sf->cur_func);
  18856             b = p->u.func.function_bytecode;
  18857         }
  18858     } else {
  18859       p = JS_VALUE_GET_OBJ(func_obj);
  18860       if (p->class_id == JS_CLASS_BYTECODE_FUNCTION) {
  18861         b = p->u.func.function_bytecode;
  18862       }
  18863     }
  18864 
  18865     if (b) {
  18866       TrampolineFn *fn;
  18867       if (!b->perf_trampoline) {
  18868         b->perf_trampoline = compile_trampoline();
  18869         if (b->perf_trampoline) {
  18870           perf_map_write_entry(caller_ctx, b->perf_trampoline, 8, b);
  18871         }
  18872       }
  18873       fn = b->perf_trampoline;
  18874       if (fn) {
  18875         return fn(&ci_args, JS_CallInternalStruct);
  18876       }
  18877     }
  18878 
  18879     return fallback_trampoline(&ci_args, JS_CallInternalStruct);
  18880     //return __JS_CallInternal(caller_ctx, func_obj, this_obj, new_target, argc, argv, flags);
  18881 }
  18882 
  18883 #else
  18884 
  18885 static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
  18886                                JSValueConst this_obj, JSValueConst new_target,
  18887                                int argc, JSValue *argv, int flags)
  18888 {
  18889   return __JS_CallInternal(caller_ctx, func_obj, this_obj, new_target, argc, argv, flags);
  18890 }
  18891 
  18892 #endif /* PERF_TRAMPOLINE */
  18893 
  18894 JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj,
  18895                 int argc, JSValueConst *argv)
  18896 {
  18897     return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED,
  18898                            argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV);
  18899 }
  18900 
  18901 static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj,
  18902                            int argc, JSValueConst *argv)
  18903 {
  18904     JSValue res = JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED,
  18905                                   argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV);
  18906     JS_FreeValue(ctx, func_obj);
  18907     return res;
  18908 }
  18909 
  18910 /* warning: the refcount of the context is not incremented. Return
  18911    NULL in case of exception (case of revoked proxy only) */
  18912 static JSContext *JS_GetFunctionRealm(JSContext *ctx, JSValueConst func_obj)
  18913 {
  18914     JSObject *p;
  18915     JSContext *realm;
  18916 
  18917     if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)
  18918         return ctx;
  18919     p = JS_VALUE_GET_OBJ(func_obj);
  18920     switch(p->class_id) {
  18921     case JS_CLASS_C_FUNCTION:
  18922         realm = p->u.cfunc.realm;
  18923         break;
  18924     case JS_CLASS_BYTECODE_FUNCTION:
  18925     case JS_CLASS_GENERATOR_FUNCTION:
  18926     case JS_CLASS_ASYNC_FUNCTION:
  18927     case JS_CLASS_ASYNC_GENERATOR_FUNCTION:
  18928         {
  18929             JSFunctionBytecode *b;
  18930             b = p->u.func.function_bytecode;
  18931             realm = b->realm;
  18932         }
  18933         break;
  18934     case JS_CLASS_PROXY:
  18935         {
  18936             JSProxyData *s = p->u.opaque;
  18937             if (!s)
  18938                 return ctx;
  18939             if (s->is_revoked) {
  18940                 JS_ThrowTypeErrorRevokedProxy(ctx);
  18941                 return NULL;
  18942             } else {
  18943                 realm = JS_GetFunctionRealm(ctx, s->target);
  18944             }
  18945         }
  18946         break;
  18947     case JS_CLASS_BOUND_FUNCTION:
  18948         {
  18949             JSBoundFunction *bf = p->u.bound_function;
  18950             realm = JS_GetFunctionRealm(ctx, bf->func_obj);
  18951         }
  18952         break;
  18953     default:
  18954         realm = ctx;
  18955         break;
  18956     }
  18957     return realm;
  18958 }
  18959 
  18960 static JSValue js_create_from_ctor(JSContext *ctx, JSValueConst ctor,
  18961                                    int class_id)
  18962 {
  18963     JSValue proto, obj;
  18964     JSContext *realm;
  18965 
  18966     if (JS_IsUndefined(ctor)) {
  18967         proto = JS_DupValue(ctx, ctx->class_proto[class_id]);
  18968     } else {
  18969         proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype);
  18970         if (JS_IsException(proto))
  18971             return proto;
  18972         if (!JS_IsObject(proto)) {
  18973             JS_FreeValue(ctx, proto);
  18974             realm = JS_GetFunctionRealm(ctx, ctor);
  18975             if (!realm)
  18976                 return JS_EXCEPTION;
  18977             proto = JS_DupValue(ctx, realm->class_proto[class_id]);
  18978         }
  18979     }
  18980     obj = JS_NewObjectProtoClass(ctx, proto, class_id);
  18981     JS_FreeValue(ctx, proto);
  18982     return obj;
  18983 }
  18984 
  18985 /* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */
  18986 static JSValue JS_CallConstructorInternal(JSContext *ctx,
  18987                                           JSValueConst func_obj,
  18988                                           JSValueConst new_target,
  18989                                           int argc, JSValue *argv, int flags)
  18990 {
  18991     JSObject *p;
  18992     JSFunctionBytecode *b;
  18993 
  18994     if (js_poll_interrupts(ctx))
  18995         return JS_EXCEPTION;
  18996     flags |= JS_CALL_FLAG_CONSTRUCTOR;
  18997     if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT))
  18998         goto not_a_function;
  18999     p = JS_VALUE_GET_OBJ(func_obj);
  19000     if (unlikely(!p->is_constructor))
  19001         return JS_ThrowTypeError(ctx, "not a constructor");
  19002     if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) {
  19003         JSClassCall *call_func;
  19004         call_func = ctx->rt->class_array[p->class_id].call;
  19005         if (!call_func) {
  19006         not_a_function:
  19007             return JS_ThrowTypeError(ctx, "not a function");
  19008         }
  19009         return call_func(ctx, func_obj, new_target, argc,
  19010                          (JSValueConst *)argv, flags);
  19011     }
  19012 
  19013     b = p->u.func.function_bytecode;
  19014     if (b->is_derived_class_constructor) {
  19015         return JS_CallInternal(ctx, func_obj, JS_UNDEFINED, new_target, argc, argv, flags);
  19016     } else {
  19017         JSValue obj, ret;
  19018         /* legacy constructor behavior */
  19019         obj = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);
  19020         if (JS_IsException(obj))
  19021             return JS_EXCEPTION;
  19022         ret = JS_CallInternal(ctx, func_obj, obj, new_target, argc, argv, flags);
  19023         if (JS_VALUE_GET_TAG(ret) == JS_TAG_OBJECT ||
  19024             JS_IsException(ret)) {
  19025             JS_FreeValue(ctx, obj);
  19026             return ret;
  19027         } else {
  19028             JS_FreeValue(ctx, ret);
  19029             return obj;
  19030         }
  19031     }
  19032 }
  19033 
  19034 JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj,
  19035                             JSValueConst new_target,
  19036                             int argc, JSValueConst *argv)
  19037 {
  19038     return JS_CallConstructorInternal(ctx, func_obj, new_target,
  19039                                       argc, (JSValue *)argv,
  19040                                       JS_CALL_FLAG_COPY_ARGV);
  19041 }
  19042 
  19043 JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj,
  19044                            int argc, JSValueConst *argv)
  19045 {
  19046     return JS_CallConstructorInternal(ctx, func_obj, func_obj,
  19047                                       argc, (JSValue *)argv,
  19048                                       JS_CALL_FLAG_COPY_ARGV);
  19049 }
  19050 
  19051 JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom,
  19052                   int argc, JSValueConst *argv)
  19053 {
  19054     JSValue func_obj;
  19055     func_obj = JS_GetProperty(ctx, this_val, atom);
  19056     if (JS_IsException(func_obj))
  19057         return func_obj;
  19058     return JS_CallFree(ctx, func_obj, this_val, argc, argv);
  19059 }
  19060 
  19061 static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,
  19062                              int argc, JSValueConst *argv)
  19063 {
  19064     JSValue res = JS_Invoke(ctx, this_val, atom, argc, argv);
  19065     JS_FreeValue(ctx, this_val);
  19066     return res;
  19067 }
  19068 
  19069 /* JSAsyncFunctionState (used by generator and async functions) */
  19070 static JSAsyncFunctionState *async_func_init(JSContext *ctx,
  19071                                              JSValueConst func_obj, JSValueConst this_obj,
  19072                                              int argc, JSValueConst *argv)
  19073 {
  19074     JSAsyncFunctionState *s;
  19075     JSObject *p;
  19076     JSFunctionBytecode *b;
  19077     JSStackFrame *sf;
  19078     int local_count, i, arg_buf_len, n;
  19079 
  19080     s = js_mallocz(ctx, sizeof(*s));
  19081     if (!s)
  19082         return NULL;
  19083     s->header.ref_count = 1;
  19084     add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION);
  19085 
  19086     sf = &s->frame;
  19087     init_list_head(&sf->var_ref_list);
  19088     p = JS_VALUE_GET_OBJ(func_obj);
  19089     b = p->u.func.function_bytecode;
  19090     sf->js_mode = b->js_mode | JS_MODE_ASYNC;
  19091     sf->cur_pc = b->byte_code_buf;
  19092     arg_buf_len = max_int(b->arg_count, argc);
  19093     local_count = arg_buf_len + b->var_count + b->stack_size;
  19094     sf->arg_buf = js_malloc(ctx, sizeof(JSValue) * max_int(local_count, 1));
  19095     if (!sf->arg_buf) {
  19096         js_free(ctx, s);
  19097         return NULL;
  19098     }
  19099     sf->cur_func = JS_DupValue(ctx, func_obj);
  19100     s->this_val = JS_DupValue(ctx, this_obj);
  19101     s->argc = argc;
  19102     sf->arg_count = arg_buf_len;
  19103     sf->var_buf = sf->arg_buf + arg_buf_len;
  19104     sf->cur_sp = sf->var_buf + b->var_count;
  19105     for(i = 0; i < argc; i++)
  19106         sf->arg_buf[i] = JS_DupValue(ctx, argv[i]);
  19107     n = arg_buf_len + b->var_count;
  19108     for(i = argc; i < n; i++)
  19109         sf->arg_buf[i] = JS_UNDEFINED;
  19110     s->resolving_funcs[0] = JS_UNDEFINED;
  19111     s->resolving_funcs[1] = JS_UNDEFINED;
  19112     s->is_completed = FALSE;
  19113     return s;
  19114 }
  19115 
  19116 static void async_func_free_frame(JSRuntime *rt, JSAsyncFunctionState *s)
  19117 {
  19118     JSStackFrame *sf = &s->frame;
  19119     JSValue *sp;
  19120 
  19121     if (sf->arg_buf) {
  19122         /* cannot free the function if it is running */
  19123         assert(sf->cur_sp != NULL);
  19124         for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) {
  19125             JS_FreeValueRT(rt, *sp);
  19126         }
  19127         js_free_rt(rt, sf->arg_buf);
  19128         sf->arg_buf = NULL;
  19129     }
  19130     JS_FreeValueRT(rt, sf->cur_func);
  19131     JS_FreeValueRT(rt, s->this_val);
  19132 }
  19133 
  19134 static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s)
  19135 {
  19136     JSRuntime *rt = ctx->rt;
  19137     JSStackFrame *sf = &s->frame;
  19138     JSValue func_obj, ret;
  19139 
  19140     assert(!s->is_completed);
  19141     if (js_check_stack_overflow(ctx->rt, 0)) {
  19142         ret = JS_ThrowStackOverflow(ctx);
  19143     } else {
  19144         /* the tag does not matter provided it is not an object */
  19145         func_obj = JS_MKPTR(JS_TAG_INT, s);
  19146         ret = JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED,
  19147                               s->argc, sf->arg_buf, JS_CALL_FLAG_GENERATOR);
  19148     }
  19149     if (JS_IsException(ret) || JS_IsUndefined(ret)) {
  19150         if (JS_IsUndefined(ret)) {
  19151             ret = sf->cur_sp[-1];
  19152             sf->cur_sp[-1] = JS_UNDEFINED;
  19153         }
  19154         /* end of execution */
  19155         s->is_completed = TRUE;
  19156 
  19157         /* close the closure variables. */
  19158         close_var_refs(rt, sf);
  19159 
  19160         async_func_free_frame(rt, s);
  19161     }
  19162     return ret;
  19163 }
  19164 
  19165 static void __async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)
  19166 {
  19167     /* cannot close the closure variables here because it would
  19168        potentially modify the object graph */
  19169     if (!s->is_completed) {
  19170         async_func_free_frame(rt, s);
  19171     }
  19172 
  19173     JS_FreeValueRT(rt, s->resolving_funcs[0]);
  19174     JS_FreeValueRT(rt, s->resolving_funcs[1]);
  19175 
  19176     remove_gc_object(&s->header);
  19177     if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && s->header.ref_count != 0) {
  19178         list_add_tail(&s->header.link, &rt->gc_zero_ref_count_list);
  19179     } else {
  19180         js_free_rt(rt, s);
  19181     }
  19182 }
  19183 
  19184 static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)
  19185 {
  19186     if (--s->header.ref_count == 0) {
  19187         if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) {
  19188             list_del(&s->header.link);
  19189             list_add(&s->header.link, &rt->gc_zero_ref_count_list);
  19190             if (rt->gc_phase == JS_GC_PHASE_NONE) {
  19191                 free_zero_refcount(rt);
  19192             }
  19193         }
  19194     }
  19195 }
  19196 
  19197 /* Generators */
  19198 
  19199 typedef enum JSGeneratorStateEnum {
  19200     JS_GENERATOR_STATE_SUSPENDED_START,
  19201     JS_GENERATOR_STATE_SUSPENDED_YIELD,
  19202     JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR,
  19203     JS_GENERATOR_STATE_EXECUTING,
  19204     JS_GENERATOR_STATE_COMPLETED,
  19205 } JSGeneratorStateEnum;
  19206 
  19207 typedef struct JSGeneratorData {
  19208     JSGeneratorStateEnum state;
  19209     JSAsyncFunctionState *func_state;
  19210 } JSGeneratorData;
  19211 
  19212 static void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s)
  19213 {
  19214     if (s->state == JS_GENERATOR_STATE_COMPLETED)
  19215         return;
  19216     if (s->func_state) {
  19217         async_func_free(rt, s->func_state);
  19218         s->func_state = NULL;
  19219     }
  19220     s->state = JS_GENERATOR_STATE_COMPLETED;
  19221 }
  19222 
  19223 static void js_generator_finalizer(JSRuntime *rt, JSValue obj)
  19224 {
  19225     JSGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_GENERATOR);
  19226 
  19227     if (s) {
  19228         free_generator_stack_rt(rt, s);
  19229         js_free_rt(rt, s);
  19230     }
  19231 }
  19232 
  19233 static void free_generator_stack(JSContext *ctx, JSGeneratorData *s)
  19234 {
  19235     free_generator_stack_rt(ctx->rt, s);
  19236 }
  19237 
  19238 static void js_generator_mark(JSRuntime *rt, JSValueConst val,
  19239                               JS_MarkFunc *mark_func)
  19240 {
  19241     JSObject *p = JS_VALUE_GET_OBJ(val);
  19242     JSGeneratorData *s = p->u.generator_data;
  19243 
  19244     if (!s || !s->func_state)
  19245         return;
  19246     mark_func(rt, &s->func_state->header);
  19247 }
  19248 
  19249 /* XXX: use enum */
  19250 #define GEN_MAGIC_NEXT   0
  19251 #define GEN_MAGIC_RETURN 1
  19252 #define GEN_MAGIC_THROW  2
  19253 
  19254 static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val,
  19255                                  int argc, JSValueConst *argv,
  19256                                  BOOL *pdone, int magic)
  19257 {
  19258     JSGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_GENERATOR);
  19259     JSStackFrame *sf;
  19260     JSValue ret, func_ret;
  19261 
  19262     *pdone = TRUE;
  19263     if (!s)
  19264         return JS_ThrowTypeError(ctx, "not a generator");
  19265     switch(s->state) {
  19266     default:
  19267     case JS_GENERATOR_STATE_SUSPENDED_START:
  19268         sf = &s->func_state->frame;
  19269         if (magic == GEN_MAGIC_NEXT) {
  19270             goto exec_no_arg;
  19271         } else {
  19272             free_generator_stack(ctx, s);
  19273             goto done;
  19274         }
  19275         break;
  19276     case JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR:
  19277     case JS_GENERATOR_STATE_SUSPENDED_YIELD:
  19278         sf = &s->func_state->frame;
  19279         /* cur_sp[-1] was set to JS_UNDEFINED in the previous call */
  19280         ret = JS_DupValue(ctx, argv[0]);
  19281         if (magic == GEN_MAGIC_THROW &&
  19282             s->state == JS_GENERATOR_STATE_SUSPENDED_YIELD) {
  19283             JS_Throw(ctx, ret);
  19284             s->func_state->throw_flag = TRUE;
  19285         } else {
  19286             sf->cur_sp[-1] = ret;
  19287             sf->cur_sp[0] = JS_NewInt32(ctx, magic);
  19288             sf->cur_sp++;
  19289         exec_no_arg:
  19290             s->func_state->throw_flag = FALSE;
  19291         }
  19292         s->state = JS_GENERATOR_STATE_EXECUTING;
  19293         func_ret = async_func_resume(ctx, s->func_state);
  19294         s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD;
  19295         if (s->func_state->is_completed) {
  19296             /* finalize the execution in case of exception or normal return */
  19297             free_generator_stack(ctx, s);
  19298             return func_ret;
  19299         } else {
  19300             assert(JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT);
  19301             /* get the returned yield value at the top of the stack */
  19302             ret = sf->cur_sp[-1];
  19303             sf->cur_sp[-1] = JS_UNDEFINED;
  19304             if (JS_VALUE_GET_INT(func_ret) == FUNC_RET_YIELD_STAR) {
  19305                 s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR;
  19306                 /* return (value, done) object */
  19307                 *pdone = 2;
  19308             } else {
  19309                 *pdone = FALSE;
  19310             }
  19311         }
  19312         break;
  19313     case JS_GENERATOR_STATE_COMPLETED:
  19314     done:
  19315         /* execution is finished */
  19316         switch(magic) {
  19317         default:
  19318         case GEN_MAGIC_NEXT:
  19319             ret = JS_UNDEFINED;
  19320             break;
  19321         case GEN_MAGIC_RETURN:
  19322             ret = JS_DupValue(ctx, argv[0]);
  19323             break;
  19324         case GEN_MAGIC_THROW:
  19325             ret = JS_Throw(ctx, JS_DupValue(ctx, argv[0]));
  19326             break;
  19327         }
  19328         break;
  19329     case JS_GENERATOR_STATE_EXECUTING:
  19330         ret = JS_ThrowTypeError(ctx, "cannot invoke a running generator");
  19331         break;
  19332     }
  19333     return ret;
  19334 }
  19335 
  19336 static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
  19337                                           JSValueConst this_obj,
  19338                                           int argc, JSValueConst *argv,
  19339                                           int flags)
  19340 {
  19341     JSValue obj, func_ret;
  19342     JSGeneratorData *s;
  19343 
  19344     s = js_mallocz(ctx, sizeof(*s));
  19345     if (!s)
  19346         return JS_EXCEPTION;
  19347     s->state = JS_GENERATOR_STATE_SUSPENDED_START;
  19348     s->func_state = async_func_init(ctx, func_obj, this_obj, argc, argv);
  19349     if (!s->func_state) {
  19350         s->state = JS_GENERATOR_STATE_COMPLETED;
  19351         goto fail;
  19352     }
  19353 
  19354     /* execute the function up to 'OP_initial_yield' */
  19355     func_ret = async_func_resume(ctx, s->func_state);
  19356     if (JS_IsException(func_ret))
  19357         goto fail;
  19358     JS_FreeValue(ctx, func_ret);
  19359 
  19360     obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_GENERATOR);
  19361     if (JS_IsException(obj))
  19362         goto fail;
  19363     JS_SetOpaque(obj, s);
  19364     return obj;
  19365  fail:
  19366     free_generator_stack_rt(ctx->rt, s);
  19367     js_free(ctx, s);
  19368     return JS_EXCEPTION;
  19369 }
  19370 
  19371 /* AsyncFunction */
  19372 
  19373 static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val)
  19374 {
  19375     JSObject *p = JS_VALUE_GET_OBJ(val);
  19376     JSAsyncFunctionState *s = p->u.async_function_data;
  19377     if (s) {
  19378         async_func_free(rt, s);
  19379     }
  19380 }
  19381 
  19382 static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val,
  19383                                            JS_MarkFunc *mark_func)
  19384 {
  19385     JSObject *p = JS_VALUE_GET_OBJ(val);
  19386     JSAsyncFunctionState *s = p->u.async_function_data;
  19387     if (s) {
  19388         mark_func(rt, &s->header);
  19389     }
  19390 }
  19391 
  19392 static int js_async_function_resolve_create(JSContext *ctx,
  19393                                             JSAsyncFunctionState *s,
  19394                                             JSValue *resolving_funcs)
  19395 {
  19396     int i;
  19397     JSObject *p;
  19398 
  19399     for(i = 0; i < 2; i++) {
  19400         resolving_funcs[i] =
  19401             JS_NewObjectProtoClass(ctx, ctx->function_proto,
  19402                                    JS_CLASS_ASYNC_FUNCTION_RESOLVE + i);
  19403         if (JS_IsException(resolving_funcs[i])) {
  19404             if (i == 1)
  19405                 JS_FreeValue(ctx, resolving_funcs[0]);
  19406             return -1;
  19407         }
  19408         p = JS_VALUE_GET_OBJ(resolving_funcs[i]);
  19409         s->header.ref_count++;
  19410         p->u.async_function_data = s;
  19411     }
  19412     return 0;
  19413 }
  19414 
  19415 static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionState *s)
  19416 {
  19417     JSValue func_ret, ret2;
  19418 
  19419     func_ret = async_func_resume(ctx, s);
  19420     if (s->is_completed) {
  19421         if (JS_IsException(func_ret)) {
  19422             JSValue error;
  19423         fail:
  19424             error = JS_GetException(ctx);
  19425             ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED,
  19426                            1, (JSValueConst *)&error);
  19427             JS_FreeValue(ctx, error);
  19428             JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
  19429         } else {
  19430             /* normal return */
  19431             ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED,
  19432                            1, (JSValueConst *)&func_ret);
  19433             JS_FreeValue(ctx, func_ret);
  19434             JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
  19435         }
  19436     } else {
  19437         JSValue value, promise, resolving_funcs[2], resolving_funcs1[2];
  19438         int i, res;
  19439 
  19440         value = s->frame.cur_sp[-1];
  19441         s->frame.cur_sp[-1] = JS_UNDEFINED;
  19442 
  19443         /* await */
  19444         JS_FreeValue(ctx, func_ret); /* not used */
  19445         promise = js_promise_resolve(ctx, ctx->promise_ctor,
  19446                                      1, (JSValueConst *)&value, 0);
  19447         JS_FreeValue(ctx, value);
  19448         if (JS_IsException(promise))
  19449             goto fail;
  19450         if (js_async_function_resolve_create(ctx, s, resolving_funcs)) {
  19451             JS_FreeValue(ctx, promise);
  19452             goto fail;
  19453         }
  19454 
  19455         /* Note: no need to create 'thrownawayCapability' as in
  19456            the spec */
  19457         for(i = 0; i < 2; i++)
  19458             resolving_funcs1[i] = JS_UNDEFINED;
  19459         res = perform_promise_then(ctx, promise,
  19460                                    (JSValueConst *)resolving_funcs,
  19461                                    (JSValueConst *)resolving_funcs1);
  19462         JS_FreeValue(ctx, promise);
  19463         for(i = 0; i < 2; i++)
  19464             JS_FreeValue(ctx, resolving_funcs[i]);
  19465         if (res)
  19466             goto fail;
  19467     }
  19468 }
  19469 
  19470 static JSValue js_async_function_resolve_call(JSContext *ctx,
  19471                                               JSValueConst func_obj,
  19472                                               JSValueConst this_obj,
  19473                                               int argc, JSValueConst *argv,
  19474                                               int flags)
  19475 {
  19476     JSObject *p = JS_VALUE_GET_OBJ(func_obj);
  19477     JSAsyncFunctionState *s = p->u.async_function_data;
  19478     BOOL is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE;
  19479     JSValueConst arg;
  19480 
  19481     if (argc > 0)
  19482         arg = argv[0];
  19483     else
  19484         arg = JS_UNDEFINED;
  19485     s->throw_flag = is_reject;
  19486     if (is_reject) {
  19487         JS_Throw(ctx, JS_DupValue(ctx, arg));
  19488     } else {
  19489         /* return value of await */
  19490         s->frame.cur_sp[-1] = JS_DupValue(ctx, arg);
  19491     }
  19492     js_async_function_resume(ctx, s);
  19493     return JS_UNDEFINED;
  19494 }
  19495 
  19496 static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj,
  19497                                       JSValueConst this_obj,
  19498                                       int argc, JSValueConst *argv, int flags)
  19499 {
  19500     JSValue promise;
  19501     JSAsyncFunctionState *s;
  19502 
  19503     s = async_func_init(ctx, func_obj, this_obj, argc, argv);
  19504     if (!s)
  19505         return JS_EXCEPTION;
  19506 
  19507     promise = JS_NewPromiseCapability(ctx, s->resolving_funcs);
  19508     if (JS_IsException(promise)) {
  19509         async_func_free(ctx->rt, s);
  19510         return JS_EXCEPTION;
  19511     }
  19512 
  19513     js_async_function_resume(ctx, s);
  19514 
  19515     async_func_free(ctx->rt, s);
  19516 
  19517     return promise;
  19518 }
  19519 
  19520 /* AsyncGenerator */
  19521 
  19522 typedef enum JSAsyncGeneratorStateEnum {
  19523     JS_ASYNC_GENERATOR_STATE_SUSPENDED_START,
  19524     JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD,
  19525     JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR,
  19526     JS_ASYNC_GENERATOR_STATE_EXECUTING,
  19527     JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN,
  19528     JS_ASYNC_GENERATOR_STATE_COMPLETED,
  19529 } JSAsyncGeneratorStateEnum;
  19530 
  19531 typedef struct JSAsyncGeneratorRequest {
  19532     struct list_head link;
  19533     /* completion */
  19534     int completion_type; /* GEN_MAGIC_x */
  19535     JSValue result;
  19536     /* promise capability */
  19537     JSValue promise;
  19538     JSValue resolving_funcs[2];
  19539 } JSAsyncGeneratorRequest;
  19540 
  19541 typedef struct JSAsyncGeneratorData {
  19542     JSObject *generator; /* back pointer to the object (const) */
  19543     JSAsyncGeneratorStateEnum state;
  19544     /* func_state is NULL is state AWAITING_RETURN and COMPLETED */
  19545     JSAsyncFunctionState *func_state;
  19546     struct list_head queue; /* list of JSAsyncGeneratorRequest.link */
  19547 } JSAsyncGeneratorData;
  19548 
  19549 static void js_async_generator_free(JSRuntime *rt,
  19550                                     JSAsyncGeneratorData *s)
  19551 {
  19552     struct list_head *el, *el1;
  19553     JSAsyncGeneratorRequest *req;
  19554 
  19555     list_for_each_safe(el, el1, &s->queue) {
  19556         req = list_entry(el, JSAsyncGeneratorRequest, link);
  19557         JS_FreeValueRT(rt, req->result);
  19558         JS_FreeValueRT(rt, req->promise);
  19559         JS_FreeValueRT(rt, req->resolving_funcs[0]);
  19560         JS_FreeValueRT(rt, req->resolving_funcs[1]);
  19561         js_free_rt(rt, req);
  19562     }
  19563     if (s->func_state)
  19564         async_func_free(rt, s->func_state);
  19565     js_free_rt(rt, s);
  19566 }
  19567 
  19568 static void js_async_generator_finalizer(JSRuntime *rt, JSValue obj)
  19569 {
  19570     JSAsyncGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_ASYNC_GENERATOR);
  19571 
  19572     if (s) {
  19573         js_async_generator_free(rt, s);
  19574     }
  19575 }
  19576 
  19577 static void js_async_generator_mark(JSRuntime *rt, JSValueConst val,
  19578                                     JS_MarkFunc *mark_func)
  19579 {
  19580     JSAsyncGeneratorData *s = JS_GetOpaque(val, JS_CLASS_ASYNC_GENERATOR);
  19581     struct list_head *el;
  19582     JSAsyncGeneratorRequest *req;
  19583     if (s) {
  19584         list_for_each(el, &s->queue) {
  19585             req = list_entry(el, JSAsyncGeneratorRequest, link);
  19586             JS_MarkValue(rt, req->result, mark_func);
  19587             JS_MarkValue(rt, req->promise, mark_func);
  19588             JS_MarkValue(rt, req->resolving_funcs[0], mark_func);
  19589             JS_MarkValue(rt, req->resolving_funcs[1], mark_func);
  19590         }
  19591         if (s->func_state) {
  19592             mark_func(rt, &s->func_state->header);
  19593         }
  19594     }
  19595 }
  19596 
  19597 static JSValue js_async_generator_resolve_function(JSContext *ctx,
  19598                                           JSValueConst this_obj,
  19599                                           int argc, JSValueConst *argv,
  19600                                           int magic, JSValue *func_data);
  19601 
  19602 static int js_async_generator_resolve_function_create(JSContext *ctx,
  19603                                                       JSValueConst generator,
  19604                                                       JSValue *resolving_funcs,
  19605                                                       BOOL is_resume_next)
  19606 {
  19607     int i;
  19608     JSValue func;
  19609 
  19610     for(i = 0; i < 2; i++) {
  19611         func = JS_NewCFunctionData(ctx, js_async_generator_resolve_function, 1,
  19612                                    i + is_resume_next * 2, 1, &generator);
  19613         if (JS_IsException(func)) {
  19614             if (i == 1)
  19615                 JS_FreeValue(ctx, resolving_funcs[0]);
  19616             return -1;
  19617         }
  19618         resolving_funcs[i] = func;
  19619     }
  19620     return 0;
  19621 }
  19622 
  19623 static int js_async_generator_await(JSContext *ctx,
  19624                                     JSAsyncGeneratorData *s,
  19625                                     JSValueConst value)
  19626 {
  19627     JSValue promise, resolving_funcs[2], resolving_funcs1[2];
  19628     int i, res;
  19629 
  19630     promise = js_promise_resolve(ctx, ctx->promise_ctor,
  19631                                  1, &value, 0);
  19632     if (JS_IsException(promise))
  19633         goto fail;
  19634 
  19635     if (js_async_generator_resolve_function_create(ctx, JS_MKPTR(JS_TAG_OBJECT, s->generator),
  19636                                                    resolving_funcs, FALSE)) {
  19637         JS_FreeValue(ctx, promise);
  19638         goto fail;
  19639     }
  19640 
  19641     /* Note: no need to create 'thrownawayCapability' as in
  19642        the spec */
  19643     for(i = 0; i < 2; i++)
  19644         resolving_funcs1[i] = JS_UNDEFINED;
  19645     res = perform_promise_then(ctx, promise,
  19646                                (JSValueConst *)resolving_funcs,
  19647                                (JSValueConst *)resolving_funcs1);
  19648     JS_FreeValue(ctx, promise);
  19649     for(i = 0; i < 2; i++)
  19650         JS_FreeValue(ctx, resolving_funcs[i]);
  19651     if (res)
  19652         goto fail;
  19653     return 0;
  19654  fail:
  19655     return -1;
  19656 }
  19657 
  19658 static void js_async_generator_resolve_or_reject(JSContext *ctx,
  19659                                                  JSAsyncGeneratorData *s,
  19660                                                  JSValueConst result,
  19661                                                  int is_reject)
  19662 {
  19663     JSAsyncGeneratorRequest *next;
  19664     JSValue ret;
  19665 
  19666     next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link);
  19667     list_del(&next->link);
  19668     ret = JS_Call(ctx, next->resolving_funcs[is_reject], JS_UNDEFINED, 1,
  19669                   &result);
  19670     JS_FreeValue(ctx, ret);
  19671     JS_FreeValue(ctx, next->result);
  19672     JS_FreeValue(ctx, next->promise);
  19673     JS_FreeValue(ctx, next->resolving_funcs[0]);
  19674     JS_FreeValue(ctx, next->resolving_funcs[1]);
  19675     js_free(ctx, next);
  19676 }
  19677 
  19678 static void js_async_generator_resolve(JSContext *ctx,
  19679                                        JSAsyncGeneratorData *s,
  19680                                        JSValueConst value,
  19681                                        BOOL done)
  19682 {
  19683     JSValue result;
  19684     result = js_create_iterator_result(ctx, JS_DupValue(ctx, value), done);
  19685     /* XXX: better exception handling ? */
  19686     js_async_generator_resolve_or_reject(ctx, s, result, 0);
  19687     JS_FreeValue(ctx, result);
  19688  }
  19689 
  19690 static void js_async_generator_reject(JSContext *ctx,
  19691                                        JSAsyncGeneratorData *s,
  19692                                        JSValueConst exception)
  19693 {
  19694     js_async_generator_resolve_or_reject(ctx, s, exception, 1);
  19695 }
  19696 
  19697 static void js_async_generator_complete(JSContext *ctx,
  19698                                         JSAsyncGeneratorData *s)
  19699 {
  19700     if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) {
  19701         s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
  19702         async_func_free(ctx->rt, s->func_state);
  19703         s->func_state = NULL;
  19704     }
  19705 }
  19706 
  19707 static int js_async_generator_completed_return(JSContext *ctx,
  19708                                                JSAsyncGeneratorData *s,
  19709                                                JSValueConst value)
  19710 {
  19711     JSValue promise, resolving_funcs[2], resolving_funcs1[2];
  19712     int res;
  19713 
  19714     // Can fail looking up JS_ATOM_constructor when is_reject==0.
  19715     promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, &value,
  19716                                  /*is_reject*/0);
  19717     // A poisoned .constructor property is observable and the resulting
  19718     // exception should be delivered to the catch handler.
  19719     if (JS_IsException(promise)) {
  19720         JSValue err = JS_GetException(ctx);
  19721         promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, (JSValueConst *)&err,
  19722                                      /*is_reject*/1);
  19723         JS_FreeValue(ctx, err);
  19724         if (JS_IsException(promise))
  19725             return -1;
  19726     }
  19727     if (js_async_generator_resolve_function_create(ctx,
  19728                                                    JS_MKPTR(JS_TAG_OBJECT, s->generator),
  19729                                                    resolving_funcs1,
  19730                                                    TRUE)) {
  19731         JS_FreeValue(ctx, promise);
  19732         return -1;
  19733     }
  19734     resolving_funcs[0] = JS_UNDEFINED;
  19735     resolving_funcs[1] = JS_UNDEFINED;
  19736     res = perform_promise_then(ctx, promise,
  19737                                (JSValueConst *)resolving_funcs1,
  19738                                (JSValueConst *)resolving_funcs);
  19739     JS_FreeValue(ctx, resolving_funcs1[0]);
  19740     JS_FreeValue(ctx, resolving_funcs1[1]);
  19741     JS_FreeValue(ctx, promise);
  19742     return res;
  19743 }
  19744 
  19745 static void js_async_generator_resume_next(JSContext *ctx,
  19746                                            JSAsyncGeneratorData *s)
  19747 {
  19748     JSAsyncGeneratorRequest *next;
  19749     JSValue func_ret, value;
  19750 
  19751     for(;;) {
  19752         if (list_empty(&s->queue))
  19753             break;
  19754         next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link);
  19755         switch(s->state) {
  19756         case JS_ASYNC_GENERATOR_STATE_EXECUTING:
  19757             /* only happens when restarting execution after await() */
  19758             goto resume_exec;
  19759         case JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN:
  19760             goto done;
  19761         case JS_ASYNC_GENERATOR_STATE_SUSPENDED_START:
  19762             if (next->completion_type == GEN_MAGIC_NEXT) {
  19763                 goto exec_no_arg;
  19764             } else {
  19765                 js_async_generator_complete(ctx, s);
  19766             }
  19767             break;
  19768         case JS_ASYNC_GENERATOR_STATE_COMPLETED:
  19769             if (next->completion_type == GEN_MAGIC_NEXT) {
  19770                 js_async_generator_resolve(ctx, s, JS_UNDEFINED, TRUE);
  19771             } else if (next->completion_type == GEN_MAGIC_RETURN) {
  19772                 s->state = JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN;
  19773                 js_async_generator_completed_return(ctx, s, next->result);
  19774             } else {
  19775                 js_async_generator_reject(ctx, s, next->result);
  19776             }
  19777             goto done;
  19778         case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD:
  19779         case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR:
  19780             value = JS_DupValue(ctx, next->result);
  19781             if (next->completion_type == GEN_MAGIC_THROW &&
  19782                 s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) {
  19783                 JS_Throw(ctx, value);
  19784                 s->func_state->throw_flag = TRUE;
  19785             } else {
  19786                 /* 'yield' returns a value. 'yield *' also returns a value
  19787                    in case the 'throw' method is called */
  19788                 s->func_state->frame.cur_sp[-1] = value;
  19789                 s->func_state->frame.cur_sp[0] =
  19790                     JS_NewInt32(ctx, next->completion_type);
  19791                 s->func_state->frame.cur_sp++;
  19792             exec_no_arg:
  19793                 s->func_state->throw_flag = FALSE;
  19794             }
  19795             s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING;
  19796         resume_exec:
  19797             func_ret = async_func_resume(ctx, s->func_state);
  19798             if (s->func_state->is_completed) {
  19799                 if (JS_IsException(func_ret)) {
  19800                     value = JS_GetException(ctx);
  19801                     js_async_generator_complete(ctx, s);
  19802                     js_async_generator_reject(ctx, s, value);
  19803                     JS_FreeValue(ctx, value);
  19804                 } else {
  19805                     /* end of function */
  19806                     js_async_generator_complete(ctx, s);
  19807                     js_async_generator_resolve(ctx, s, func_ret, TRUE);
  19808                     JS_FreeValue(ctx, func_ret);
  19809                 }
  19810             } else {
  19811                 int func_ret_code, ret;
  19812                 assert(JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT);
  19813                 func_ret_code = JS_VALUE_GET_INT(func_ret);
  19814                 value = s->func_state->frame.cur_sp[-1];
  19815                 s->func_state->frame.cur_sp[-1] = JS_UNDEFINED;
  19816                 switch(func_ret_code) {
  19817                 case FUNC_RET_YIELD:
  19818                 case FUNC_RET_YIELD_STAR:
  19819                     if (func_ret_code == FUNC_RET_YIELD_STAR)
  19820                         s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR;
  19821                     else
  19822                         s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD;
  19823                     js_async_generator_resolve(ctx, s, value, FALSE);
  19824                     JS_FreeValue(ctx, value);
  19825                     break;
  19826                 case FUNC_RET_AWAIT:
  19827                     ret = js_async_generator_await(ctx, s, value);
  19828                     JS_FreeValue(ctx, value);
  19829                     if (ret < 0) {
  19830                         /* exception: throw it */
  19831                         s->func_state->throw_flag = TRUE;
  19832                         goto resume_exec;
  19833                     }
  19834                     goto done;
  19835                 default:
  19836                     abort();
  19837                 }
  19838             }
  19839             break;
  19840         default:
  19841             abort();
  19842         }
  19843     }
  19844  done: ;
  19845 }
  19846 
  19847 static JSValue js_async_generator_resolve_function(JSContext *ctx,
  19848                                                    JSValueConst this_obj,
  19849                                                    int argc, JSValueConst *argv,
  19850                                                    int magic, JSValue *func_data)
  19851 {
  19852     BOOL is_reject = magic & 1;
  19853     JSAsyncGeneratorData *s = JS_GetOpaque(func_data[0], JS_CLASS_ASYNC_GENERATOR);
  19854     JSValueConst arg = argv[0];
  19855 
  19856     /* XXX: what if s == NULL */
  19857 
  19858     if (magic >= 2) {
  19859         /* resume next case in AWAITING_RETURN state */
  19860         assert(s->state == JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN ||
  19861                s->state == JS_ASYNC_GENERATOR_STATE_COMPLETED);
  19862         s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
  19863         if (is_reject) {
  19864             js_async_generator_reject(ctx, s, arg);
  19865         } else {
  19866             js_async_generator_resolve(ctx, s, arg, TRUE);
  19867         }
  19868     } else {
  19869         /* restart function execution after await() */
  19870         assert(s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING);
  19871         s->func_state->throw_flag = is_reject;
  19872         if (is_reject) {
  19873             JS_Throw(ctx, JS_DupValue(ctx, arg));
  19874         } else {
  19875             /* return value of await */
  19876             s->func_state->frame.cur_sp[-1] = JS_DupValue(ctx, arg);
  19877         }
  19878         js_async_generator_resume_next(ctx, s);
  19879     }
  19880     return JS_UNDEFINED;
  19881 }
  19882 
  19883 /* magic = GEN_MAGIC_x */
  19884 static JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val,
  19885                                        int argc, JSValueConst *argv,
  19886                                        int magic)
  19887 {
  19888     JSAsyncGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_GENERATOR);
  19889     JSValue promise, resolving_funcs[2];
  19890     JSAsyncGeneratorRequest *req;
  19891 
  19892     promise = JS_NewPromiseCapability(ctx, resolving_funcs);
  19893     if (JS_IsException(promise))
  19894         return JS_EXCEPTION;
  19895     if (!s) {
  19896         JSValue err, res2;
  19897         JS_ThrowTypeError(ctx, "not an AsyncGenerator object");
  19898         err = JS_GetException(ctx);
  19899         res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
  19900                        1, (JSValueConst *)&err);
  19901         JS_FreeValue(ctx, err);
  19902         JS_FreeValue(ctx, res2);
  19903         JS_FreeValue(ctx, resolving_funcs[0]);
  19904         JS_FreeValue(ctx, resolving_funcs[1]);
  19905         return promise;
  19906     }
  19907     req = js_mallocz(ctx, sizeof(*req));
  19908     if (!req)
  19909         goto fail;
  19910     req->completion_type = magic;
  19911     req->result = JS_DupValue(ctx, argv[0]);
  19912     req->promise = JS_DupValue(ctx, promise);
  19913     req->resolving_funcs[0] = resolving_funcs[0];
  19914     req->resolving_funcs[1] = resolving_funcs[1];
  19915     list_add_tail(&req->link, &s->queue);
  19916     if (s->state != JS_ASYNC_GENERATOR_STATE_EXECUTING) {
  19917         js_async_generator_resume_next(ctx, s);
  19918     }
  19919     return promise;
  19920  fail:
  19921     JS_FreeValue(ctx, resolving_funcs[0]);
  19922     JS_FreeValue(ctx, resolving_funcs[1]);
  19923     JS_FreeValue(ctx, promise);
  19924     return JS_EXCEPTION;
  19925 }
  19926 
  19927 static JSValue js_async_generator_function_call(JSContext *ctx, JSValueConst func_obj,
  19928                                                 JSValueConst this_obj,
  19929                                                 int argc, JSValueConst *argv,
  19930                                                 int flags)
  19931 {
  19932     JSValue obj, func_ret;
  19933     JSAsyncGeneratorData *s;
  19934 
  19935     s = js_mallocz(ctx, sizeof(*s));
  19936     if (!s)
  19937         return JS_EXCEPTION;
  19938     s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START;
  19939     init_list_head(&s->queue);
  19940     s->func_state = async_func_init(ctx, func_obj, this_obj, argc, argv);
  19941     if (!s->func_state)
  19942         goto fail;
  19943     /* execute the function up to 'OP_initial_yield' (no yield nor
  19944        await are possible) */
  19945     func_ret = async_func_resume(ctx, s->func_state);
  19946     if (JS_IsException(func_ret))
  19947         goto fail;
  19948     JS_FreeValue(ctx, func_ret);
  19949 
  19950     obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_ASYNC_GENERATOR);
  19951     if (JS_IsException(obj))
  19952         goto fail;
  19953     s->generator = JS_VALUE_GET_OBJ(obj);
  19954     JS_SetOpaque(obj, s);
  19955     return obj;
  19956  fail:
  19957     js_async_generator_free(ctx->rt, s);
  19958     return JS_EXCEPTION;
  19959 }
  19960 
  19961 /* JS parser */
  19962 
  19963 enum {
  19964     TOK_NUMBER = -128,
  19965     TOK_STRING,
  19966     TOK_TEMPLATE,
  19967     TOK_IDENT,
  19968     TOK_REGEXP,
  19969     /* warning: order matters (see js_parse_assign_expr) */
  19970     TOK_MUL_ASSIGN,
  19971     TOK_DIV_ASSIGN,
  19972     TOK_MOD_ASSIGN,
  19973     TOK_PLUS_ASSIGN,
  19974     TOK_MINUS_ASSIGN,
  19975     TOK_SHL_ASSIGN,
  19976     TOK_SAR_ASSIGN,
  19977     TOK_SHR_ASSIGN,
  19978     TOK_AND_ASSIGN,
  19979     TOK_XOR_ASSIGN,
  19980     TOK_OR_ASSIGN,
  19981 #ifdef CONFIG_BIGNUM
  19982     TOK_MATH_POW_ASSIGN,
  19983 #endif
  19984     TOK_POW_ASSIGN,
  19985     TOK_LAND_ASSIGN,
  19986     TOK_LOR_ASSIGN,
  19987     TOK_DOUBLE_QUESTION_MARK_ASSIGN,
  19988     TOK_DEC,
  19989     TOK_INC,
  19990     TOK_SHL,
  19991     TOK_SAR,
  19992     TOK_SHR,
  19993     TOK_LT,
  19994     TOK_LTE,
  19995     TOK_GT,
  19996     TOK_GTE,
  19997     TOK_EQ,
  19998     TOK_STRICT_EQ,
  19999     TOK_NEQ,
  20000     TOK_STRICT_NEQ,
  20001     TOK_LAND,
  20002     TOK_LOR,
  20003 #ifdef CONFIG_BIGNUM
  20004     TOK_MATH_POW,
  20005 #endif
  20006     TOK_POW,
  20007     TOK_ARROW,
  20008     TOK_ELLIPSIS,
  20009     TOK_DOUBLE_QUESTION_MARK,
  20010     TOK_QUESTION_MARK_DOT,
  20011     TOK_ERROR,
  20012     TOK_PRIVATE_NAME,
  20013     TOK_EOF,
  20014     /* keywords: WARNING: same order as atoms */
  20015     TOK_NULL, /* must be first */
  20016     TOK_FALSE,
  20017     TOK_TRUE,
  20018     TOK_IF,
  20019     TOK_ELSE,
  20020     TOK_RETURN,
  20021     TOK_VAR,
  20022     TOK_THIS,
  20023     TOK_DELETE,
  20024     TOK_VOID,
  20025     TOK_TYPEOF,
  20026     TOK_NEW,
  20027     TOK_IN,
  20028     TOK_INSTANCEOF,
  20029     TOK_DO,
  20030     TOK_WHILE,
  20031     TOK_FOR,
  20032     TOK_BREAK,
  20033     TOK_CONTINUE,
  20034     TOK_SWITCH,
  20035     TOK_CASE,
  20036     TOK_DEFAULT,
  20037     TOK_THROW,
  20038     TOK_TRY,
  20039     TOK_CATCH,
  20040     TOK_FINALLY,
  20041     TOK_FUNCTION,
  20042     TOK_DEBUGGER,
  20043     TOK_WITH,
  20044     /* FutureReservedWord */
  20045     TOK_CLASS,
  20046     TOK_CONST,
  20047     TOK_ENUM,
  20048     TOK_EXPORT,
  20049     TOK_EXTENDS,
  20050     TOK_IMPORT,
  20051     TOK_SUPER,
  20052     /* FutureReservedWords when parsing strict mode code */
  20053     TOK_IMPLEMENTS,
  20054     TOK_INTERFACE,
  20055     TOK_LET,
  20056     TOK_PACKAGE,
  20057     TOK_PRIVATE,
  20058     TOK_PROTECTED,
  20059     TOK_PUBLIC,
  20060     TOK_STATIC,
  20061     TOK_YIELD,
  20062     TOK_AWAIT, /* must be last */
  20063     TOK_OF,     /* only used for js_parse_skip_parens_token() */
  20064 };
  20065 
  20066 #define TOK_FIRST_KEYWORD   TOK_NULL
  20067 #define TOK_LAST_KEYWORD    TOK_AWAIT
  20068 
  20069 /* unicode code points */
  20070 #define CP_NBSP 0x00a0
  20071 #define CP_BOM  0xfeff
  20072 
  20073 #define CP_LS   0x2028
  20074 #define CP_PS   0x2029
  20075 
  20076 typedef struct BlockEnv {
  20077     struct BlockEnv *prev;
  20078     JSAtom label_name; /* JS_ATOM_NULL if none */
  20079     int label_break; /* -1 if none */
  20080     int label_cont; /* -1 if none */
  20081     int drop_count; /* number of stack elements to drop */
  20082     int label_finally; /* -1 if none */
  20083     int scope_level;
  20084     int has_iterator;
  20085 } BlockEnv;
  20086 
  20087 typedef struct JSGlobalVar {
  20088     int cpool_idx; /* if >= 0, index in the constant pool for hoisted
  20089                       function defintion*/
  20090     uint8_t force_init : 1; /* force initialization to undefined */
  20091     uint8_t is_lexical : 1; /* global let/const definition */
  20092     uint8_t is_const   : 1; /* const definition */
  20093     int scope_level;    /* scope of definition */
  20094     JSAtom var_name;  /* variable name */
  20095 } JSGlobalVar;
  20096 
  20097 typedef struct RelocEntry {
  20098     struct RelocEntry *next;
  20099     uint32_t addr; /* address to patch */
  20100     int size;   /* address size: 1, 2 or 4 bytes */
  20101 } RelocEntry;
  20102 
  20103 typedef struct JumpSlot {
  20104     int op;
  20105     int size;
  20106     int pos;
  20107     int label;
  20108 } JumpSlot;
  20109 
  20110 typedef struct LabelSlot {
  20111     int ref_count;
  20112     int pos;    /* phase 1 address, -1 means not resolved yet */
  20113     int pos2;   /* phase 2 address, -1 means not resolved yet */
  20114     int addr;   /* phase 3 address, -1 means not resolved yet */
  20115     RelocEntry *first_reloc;
  20116 } LabelSlot;
  20117 
  20118 typedef struct LineNumberSlot {
  20119     uint32_t pc;
  20120     int line_num;
  20121 } LineNumberSlot;
  20122 
  20123 typedef enum JSParseFunctionEnum {
  20124     JS_PARSE_FUNC_STATEMENT,
  20125     JS_PARSE_FUNC_VAR,
  20126     JS_PARSE_FUNC_EXPR,
  20127     JS_PARSE_FUNC_ARROW,
  20128     JS_PARSE_FUNC_GETTER,
  20129     JS_PARSE_FUNC_SETTER,
  20130     JS_PARSE_FUNC_METHOD,
  20131     JS_PARSE_FUNC_CLASS_STATIC_INIT,
  20132     JS_PARSE_FUNC_CLASS_CONSTRUCTOR,
  20133     JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR,
  20134 } JSParseFunctionEnum;
  20135 
  20136 typedef enum JSParseExportEnum {
  20137     JS_PARSE_EXPORT_NONE,
  20138     JS_PARSE_EXPORT_NAMED,
  20139     JS_PARSE_EXPORT_DEFAULT,
  20140 } JSParseExportEnum;
  20141 
  20142 typedef struct JSFunctionDef {
  20143     JSContext *ctx;
  20144     struct JSFunctionDef *parent;
  20145     int parent_cpool_idx; /* index in the constant pool of the parent
  20146                              or -1 if none */
  20147     int parent_scope_level; /* scope level in parent at point of definition */
  20148     struct list_head child_list; /* list of JSFunctionDef.link */
  20149     struct list_head link;
  20150 
  20151     BOOL is_eval; /* TRUE if eval code */
  20152     int eval_type; /* only valid if is_eval = TRUE */
  20153     BOOL is_global_var; /* TRUE if variables are not defined locally:
  20154                            eval global, eval module or non strict eval */
  20155     BOOL is_func_expr; /* TRUE if function expression */
  20156     BOOL has_home_object; /* TRUE if the home object is available */
  20157     BOOL has_prototype; /* true if a prototype field is necessary */
  20158     BOOL has_simple_parameter_list;
  20159     BOOL has_parameter_expressions; /* if true, an argument scope is created */
  20160     BOOL has_use_strict; /* to reject directive in special cases */
  20161     BOOL has_eval_call; /* true if the function contains a call to eval() */
  20162     BOOL has_arguments_binding; /* true if the 'arguments' binding is
  20163                                    available in the function */
  20164     BOOL has_this_binding; /* true if the 'this' and new.target binding are
  20165                               available in the function */
  20166     BOOL new_target_allowed; /* true if the 'new.target' does not
  20167                                 throw a syntax error */
  20168     BOOL super_call_allowed; /* true if super() is allowed */
  20169     BOOL super_allowed; /* true if super. or super[] is allowed */
  20170     BOOL arguments_allowed; /* true if the 'arguments' identifier is allowed */
  20171     BOOL is_derived_class_constructor;
  20172     BOOL in_function_body;
  20173     BOOL backtrace_barrier;
  20174     JSFunctionKindEnum func_kind : 8;
  20175     JSParseFunctionEnum func_type : 8;
  20176     uint8_t js_mode; /* bitmap of JS_MODE_x */
  20177     JSAtom func_name; /* JS_ATOM_NULL if no name */
  20178 
  20179     JSVarDef *vars;
  20180     int var_size; /* allocated size for vars[] */
  20181     int var_count;
  20182     JSVarDef *args;
  20183     int arg_size; /* allocated size for args[] */
  20184     int arg_count; /* number of arguments */
  20185     int defined_arg_count;
  20186     int var_object_idx; /* -1 if none */
  20187     int arg_var_object_idx; /* -1 if none (var object for the argument scope) */
  20188     int arguments_var_idx; /* -1 if none */
  20189     int arguments_arg_idx; /* argument variable definition in argument scope,
  20190                               -1 if none */
  20191     int func_var_idx; /* variable containing the current function (-1
  20192                          if none, only used if is_func_expr is true) */
  20193     int eval_ret_idx; /* variable containing the return value of the eval, -1 if none */
  20194     int this_var_idx; /* variable containg the 'this' value, -1 if none */
  20195     int new_target_var_idx; /* variable containg the 'new.target' value, -1 if none */
  20196     int this_active_func_var_idx; /* variable containg the 'this.active_func' value, -1 if none */
  20197     int home_object_var_idx;
  20198     BOOL need_home_object;
  20199 
  20200     int scope_level;    /* index into fd->scopes if the current lexical scope */
  20201     int scope_first;    /* index into vd->vars of first lexically scoped variable */
  20202     int scope_size;     /* allocated size of fd->scopes array */
  20203     int scope_count;    /* number of entries used in the fd->scopes array */
  20204     JSVarScope *scopes;
  20205     JSVarScope def_scope_array[4];
  20206     int body_scope; /* scope of the body of the function or eval */
  20207 
  20208     int global_var_count;
  20209     int global_var_size;
  20210     JSGlobalVar *global_vars;
  20211 
  20212     DynBuf byte_code;
  20213     int last_opcode_pos; /* -1 if no last opcode */
  20214     int last_opcode_line_num;
  20215     BOOL use_short_opcodes; /* true if short opcodes are used in byte_code */
  20216 
  20217     LabelSlot *label_slots;
  20218     int label_size; /* allocated size for label_slots[] */
  20219     int label_count;
  20220     BlockEnv *top_break; /* break/continue label stack */
  20221 
  20222     /* constant pool (strings, functions, numbers) */
  20223     JSValue *cpool;
  20224     int cpool_count;
  20225     int cpool_size;
  20226 
  20227     /* list of variables in the closure */
  20228     int closure_var_count;
  20229     int closure_var_size;
  20230     JSClosureVar *closure_var;
  20231 
  20232     JumpSlot *jump_slots;
  20233     int jump_size;
  20234     int jump_count;
  20235 
  20236     LineNumberSlot *line_number_slots;
  20237     int line_number_size;
  20238     int line_number_count;
  20239     int line_number_last;
  20240     int line_number_last_pc;
  20241 
  20242     /* pc2line table */
  20243     JSAtom filename;
  20244     int line_num;
  20245     DynBuf pc2line;
  20246 
  20247     char *source;  /* raw source, utf-8 encoded */
  20248     int source_len;
  20249 
  20250     JSModuleDef *module; /* != NULL when parsing a module */
  20251     BOOL has_await; /* TRUE if await is used (used in module eval) */
  20252 } JSFunctionDef;
  20253 
  20254 typedef struct JSToken {
  20255     int val;
  20256     int line_num;   /* line number of token start */
  20257     const uint8_t *ptr;
  20258     union {
  20259         struct {
  20260             JSValue str;
  20261             int sep;
  20262         } str;
  20263         struct {
  20264             JSValue val;
  20265 #ifdef CONFIG_BIGNUM
  20266             slimb_t exponent; /* may be != 0 only if val is a float */
  20267 #endif
  20268         } num;
  20269         struct {
  20270             JSAtom atom;
  20271             BOOL has_escape;
  20272             BOOL is_reserved;
  20273         } ident;
  20274         struct {
  20275             JSValue body;
  20276             JSValue flags;
  20277         } regexp;
  20278     } u;
  20279 } JSToken;
  20280 
  20281 typedef struct JSParseState {
  20282     JSContext *ctx;
  20283     int last_line_num;  /* line number of last token */
  20284     int line_num;       /* line number of current offset */
  20285     const char *filename;
  20286     JSToken token;
  20287     BOOL got_lf; /* true if got line feed before the current token */
  20288     const uint8_t *last_ptr;
  20289     const uint8_t *buf_ptr;
  20290     const uint8_t *buf_end;
  20291 
  20292     /* current function code */
  20293     JSFunctionDef *cur_func;
  20294     BOOL is_module; /* parsing a module */
  20295     BOOL allow_html_comments;
  20296     BOOL ext_json; /* true if accepting JSON superset */
  20297 } JSParseState;
  20298 
  20299 typedef struct JSOpCode {
  20300 #ifdef DUMP_BYTECODE
  20301     const char *name;
  20302 #endif
  20303     uint8_t size; /* in bytes */
  20304     /* the opcodes remove n_pop items from the top of the stack, then
  20305        pushes n_push items */
  20306     uint8_t n_pop;
  20307     uint8_t n_push;
  20308     uint8_t fmt;
  20309 } JSOpCode;
  20310 
  20311 static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = {
  20312 #define FMT(f)
  20313 #ifdef DUMP_BYTECODE
  20314 #define DEF(id, size, n_pop, n_push, f) { #id, size, n_pop, n_push, OP_FMT_ ## f },
  20315 #else
  20316 #define DEF(id, size, n_pop, n_push, f) { size, n_pop, n_push, OP_FMT_ ## f },
  20317 #endif
  20318 #include "quickjs-opcode.h"
  20319 #undef DEF
  20320 #undef FMT
  20321 };
  20322 
  20323 #if SHORT_OPCODES
  20324 /* After the final compilation pass, short opcodes are used. Their
  20325    opcodes overlap with the temporary opcodes which cannot appear in
  20326    the final bytecode. Their description is after the temporary
  20327    opcodes in opcode_info[]. */
  20328 #define short_opcode_info(op)           \
  20329     opcode_info[(op) >= OP_TEMP_START ? \
  20330                 (op) + (OP_TEMP_END - OP_TEMP_START) : (op)]
  20331 #else
  20332 #define short_opcode_info(op) opcode_info[op]
  20333 #endif
  20334 
  20335 static __exception int next_token(JSParseState *s);
  20336 
  20337 static void free_token(JSParseState *s, JSToken *token)
  20338 {
  20339     switch(token->val) {
  20340     case TOK_NUMBER:
  20341         JS_FreeValue(s->ctx, token->u.num.val);
  20342         break;
  20343     case TOK_STRING:
  20344     case TOK_TEMPLATE:
  20345         JS_FreeValue(s->ctx, token->u.str.str);
  20346         break;
  20347     case TOK_REGEXP:
  20348         JS_FreeValue(s->ctx, token->u.regexp.body);
  20349         JS_FreeValue(s->ctx, token->u.regexp.flags);
  20350         break;
  20351     case TOK_IDENT:
  20352     case TOK_PRIVATE_NAME:
  20353         JS_FreeAtom(s->ctx, token->u.ident.atom);
  20354         break;
  20355     default:
  20356         if (token->val >= TOK_FIRST_KEYWORD &&
  20357             token->val <= TOK_LAST_KEYWORD) {
  20358             JS_FreeAtom(s->ctx, token->u.ident.atom);
  20359         }
  20360         break;
  20361     }
  20362 }
  20363 
  20364 static void __attribute((unused)) dump_token(JSParseState *s,
  20365                                              const JSToken *token)
  20366 {
  20367     switch(token->val) {
  20368     case TOK_NUMBER:
  20369         {
  20370             double d;
  20371             JS_ToFloat64(s->ctx, &d, token->u.num.val);  /* no exception possible */
  20372             printf("number: %.14g\n", d);
  20373         }
  20374         break;
  20375     case TOK_IDENT:
  20376     dump_atom:
  20377         {
  20378             char buf[ATOM_GET_STR_BUF_SIZE];
  20379             printf("ident: '%s'\n",
  20380                    JS_AtomGetStr(s->ctx, buf, sizeof(buf), token->u.ident.atom));
  20381         }
  20382         break;
  20383     case TOK_STRING:
  20384         {
  20385             const char *str;
  20386             /* XXX: quote the string */
  20387             str = JS_ToCString(s->ctx, token->u.str.str);
  20388             printf("string: '%s'\n", str);
  20389             JS_FreeCString(s->ctx, str);
  20390         }
  20391         break;
  20392     case TOK_TEMPLATE:
  20393         {
  20394             const char *str;
  20395             str = JS_ToCString(s->ctx, token->u.str.str);
  20396             printf("template: `%s`\n", str);
  20397             JS_FreeCString(s->ctx, str);
  20398         }
  20399         break;
  20400     case TOK_REGEXP:
  20401         {
  20402             const char *str, *str2;
  20403             str = JS_ToCString(s->ctx, token->u.regexp.body);
  20404             str2 = JS_ToCString(s->ctx, token->u.regexp.flags);
  20405             printf("regexp: '%s' '%s'\n", str, str2);
  20406             JS_FreeCString(s->ctx, str);
  20407             JS_FreeCString(s->ctx, str2);
  20408         }
  20409         break;
  20410     case TOK_EOF:
  20411         printf("eof\n");
  20412         break;
  20413     default:
  20414         if (s->token.val >= TOK_NULL && s->token.val <= TOK_LAST_KEYWORD) {
  20415             goto dump_atom;
  20416         } else if (s->token.val >= 256) {
  20417             printf("token: %d\n", token->val);
  20418         } else {
  20419             printf("token: '%c'\n", token->val);
  20420         }
  20421         break;
  20422     }
  20423 }
  20424 
  20425 int __attribute__((format(printf, 2, 3))) js_parse_error(JSParseState *s, const char *fmt, ...)
  20426 {
  20427     JSContext *ctx = s->ctx;
  20428     va_list ap;
  20429     int backtrace_flags;
  20430 
  20431     va_start(ap, fmt);
  20432     JS_ThrowError2(ctx, JS_SYNTAX_ERROR, fmt, ap, FALSE);
  20433     va_end(ap);
  20434     backtrace_flags = 0;
  20435     if (s->cur_func && s->cur_func->backtrace_barrier)
  20436         backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL;
  20437     build_backtrace(ctx, ctx->rt->current_exception, s->filename, s->line_num,
  20438                     backtrace_flags);
  20439     return -1;
  20440 }
  20441 
  20442 static int js_parse_expect(JSParseState *s, int tok)
  20443 {
  20444     if (s->token.val != tok) {
  20445         /* XXX: dump token correctly in all cases */
  20446         return js_parse_error(s, "expecting '%c'", tok);
  20447     }
  20448     return next_token(s);
  20449 }
  20450 
  20451 static int js_parse_expect_semi(JSParseState *s)
  20452 {
  20453     if (s->token.val != ';') {
  20454         /* automatic insertion of ';' */
  20455         if (s->token.val == TOK_EOF || s->token.val == '}' || s->got_lf) {
  20456             return 0;
  20457         }
  20458         return js_parse_error(s, "expecting '%c'", ';');
  20459     }
  20460     return next_token(s);
  20461 }
  20462 
  20463 static int js_parse_error_reserved_identifier(JSParseState *s)
  20464 {
  20465     char buf1[ATOM_GET_STR_BUF_SIZE];
  20466     return js_parse_error(s, "'%s' is a reserved identifier",
  20467                           JS_AtomGetStr(s->ctx, buf1, sizeof(buf1),
  20468                                         s->token.u.ident.atom));
  20469 }
  20470 
  20471 static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p)
  20472 {
  20473     uint32_t c;
  20474     StringBuffer b_s, *b = &b_s;
  20475 
  20476     /* p points to the first byte of the template part */
  20477     if (string_buffer_init(s->ctx, b, 32))
  20478         goto fail;
  20479     for(;;) {
  20480         if (p >= s->buf_end)
  20481             goto unexpected_eof;
  20482         c = *p++;
  20483         if (c == '`') {
  20484             /* template end part */
  20485             break;
  20486         }
  20487         if (c == '$' && *p == '{') {
  20488             /* template start or middle part */
  20489             p++;
  20490             break;
  20491         }
  20492         if (c == '\\') {
  20493             if (string_buffer_putc8(b, c))
  20494                 goto fail;
  20495             if (p >= s->buf_end)
  20496                 goto unexpected_eof;
  20497             c = *p++;
  20498         }
  20499         /* newline sequences are normalized as single '\n' bytes */
  20500         if (c == '\r') {
  20501             if (*p == '\n')
  20502                 p++;
  20503             c = '\n';
  20504         }
  20505         if (c == '\n') {
  20506             s->line_num++;
  20507         } else if (c >= 0x80) {
  20508             const uint8_t *p_next;
  20509             c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
  20510             if (c > 0x10FFFF) {
  20511                 js_parse_error(s, "invalid UTF-8 sequence");
  20512                 goto fail;
  20513             }
  20514             p = p_next;
  20515         }
  20516         if (string_buffer_putc(b, c))
  20517             goto fail;
  20518     }
  20519     s->token.val = TOK_TEMPLATE;
  20520     s->token.u.str.sep = c;
  20521     s->token.u.str.str = string_buffer_end(b);
  20522     s->buf_ptr = p;
  20523     return 0;
  20524 
  20525  unexpected_eof:
  20526     js_parse_error(s, "unexpected end of string");
  20527  fail:
  20528     string_buffer_free(b);
  20529     return -1;
  20530 }
  20531 
  20532 static __exception int js_parse_string(JSParseState *s, int sep,
  20533                                        BOOL do_throw, const uint8_t *p,
  20534                                        JSToken *token, const uint8_t **pp)
  20535 {
  20536     int ret;
  20537     uint32_t c;
  20538     StringBuffer b_s, *b = &b_s;
  20539 
  20540     /* string */
  20541     if (string_buffer_init(s->ctx, b, 32))
  20542         goto fail;
  20543     for(;;) {
  20544         if (p >= s->buf_end)
  20545             goto invalid_char;
  20546         c = *p;
  20547         if (c < 0x20) {
  20548             if (!s->cur_func) {
  20549                 if (do_throw)
  20550                     js_parse_error(s, "invalid character in a JSON string");
  20551                 goto fail;
  20552             }
  20553             if (sep == '`') {
  20554                 if (c == '\r') {
  20555                     if (p[1] == '\n')
  20556                         p++;
  20557                     c = '\n';
  20558                 }
  20559                 /* do not update s->line_num */
  20560             } else if (c == '\n' || c == '\r')
  20561                 goto invalid_char;
  20562         }
  20563         p++;
  20564         if (c == sep)
  20565             break;
  20566         if (c == '$' && *p == '{' && sep == '`') {
  20567             /* template start or middle part */
  20568             p++;
  20569             break;
  20570         }
  20571         if (c == '\\') {
  20572             c = *p;
  20573             /* XXX: need a specific JSON case to avoid
  20574                accepting invalid escapes */
  20575             switch(c) {
  20576             case '\0':
  20577                 if (p >= s->buf_end)
  20578                     goto invalid_char;
  20579                 p++;
  20580                 break;
  20581             case '\'':
  20582             case '\"':
  20583             case '\\':
  20584                 p++;
  20585                 break;
  20586             case '\r':  /* accept DOS and MAC newline sequences */
  20587                 if (p[1] == '\n') {
  20588                     p++;
  20589                 }
  20590                 /* fall thru */
  20591             case '\n':
  20592                 /* ignore escaped newline sequence */
  20593                 p++;
  20594                 if (sep != '`')
  20595                     s->line_num++;
  20596                 continue;
  20597             default:
  20598                 if (c >= '0' && c <= '9') {
  20599                     if (!s->cur_func)
  20600                         goto invalid_escape; /* JSON case */
  20601                     if (!(s->cur_func->js_mode & JS_MODE_STRICT) && sep != '`')
  20602                         goto parse_escape;
  20603                     if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) {
  20604                         p++;
  20605                         c = '\0';
  20606                     } else {
  20607                         if (c >= '8' || sep == '`') {
  20608                             /* Note: according to ES2021, \8 and \9 are not
  20609                                accepted in strict mode or in templates. */
  20610                             goto invalid_escape;
  20611                         } else {
  20612                             if (do_throw)
  20613                                 js_parse_error(s, "octal escape sequences are not allowed in strict mode");
  20614                         }
  20615                         goto fail;
  20616                     }
  20617                 } else if (c >= 0x80) {
  20618                     const uint8_t *p_next;
  20619                     c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);
  20620                     if (c > 0x10FFFF) {
  20621                         goto invalid_utf8;
  20622                     }
  20623                     p = p_next;
  20624                     /* LS or PS are skipped */
  20625                     if (c == CP_LS || c == CP_PS)
  20626                         continue;
  20627                 } else {
  20628                 parse_escape:
  20629                     ret = lre_parse_escape(&p, TRUE);
  20630                     if (ret == -1) {
  20631                     invalid_escape:
  20632                         if (do_throw)
  20633                             js_parse_error(s, "malformed escape sequence in string literal");
  20634                         goto fail;
  20635                     } else if (ret < 0) {
  20636                         /* ignore the '\' (could output a warning) */
  20637                         p++;
  20638                     } else {
  20639                         c = ret;
  20640                     }
  20641                 }
  20642                 break;
  20643             }
  20644         } else if (c >= 0x80) {
  20645             const uint8_t *p_next;
  20646             c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
  20647             if (c > 0x10FFFF)
  20648                 goto invalid_utf8;
  20649             p = p_next;
  20650         }
  20651         if (string_buffer_putc(b, c))
  20652             goto fail;
  20653     }
  20654     token->val = TOK_STRING;
  20655     token->u.str.sep = c;
  20656     token->u.str.str = string_buffer_end(b);
  20657     *pp = p;
  20658     return 0;
  20659 
  20660  invalid_utf8:
  20661     if (do_throw)
  20662         js_parse_error(s, "invalid UTF-8 sequence");
  20663     goto fail;
  20664  invalid_char:
  20665     if (do_throw)
  20666         js_parse_error(s, "unexpected end of string");
  20667  fail:
  20668     string_buffer_free(b);
  20669     return -1;
  20670 }
  20671 
  20672 static inline BOOL token_is_pseudo_keyword(JSParseState *s, JSAtom atom) {
  20673     return s->token.val == TOK_IDENT && s->token.u.ident.atom == atom &&
  20674         !s->token.u.ident.has_escape;
  20675 }
  20676 
  20677 static __exception int js_parse_regexp(JSParseState *s)
  20678 {
  20679     const uint8_t *p;
  20680     BOOL in_class;
  20681     StringBuffer b_s, *b = &b_s;
  20682     StringBuffer b2_s, *b2 = &b2_s;
  20683     uint32_t c;
  20684 
  20685     p = s->buf_ptr;
  20686     p++;
  20687     in_class = FALSE;
  20688     if (string_buffer_init(s->ctx, b, 32))
  20689         return -1;
  20690     if (string_buffer_init(s->ctx, b2, 1))
  20691         goto fail;
  20692     for(;;) {
  20693         if (p >= s->buf_end) {
  20694         eof_error:
  20695             js_parse_error(s, "unexpected end of regexp");
  20696             goto fail;
  20697         }
  20698         c = *p++;
  20699         if (c == '\n' || c == '\r') {
  20700             goto eol_error;
  20701         } else if (c == '/') {
  20702             if (!in_class)
  20703                 break;
  20704         } else if (c == '[') {
  20705             in_class = TRUE;
  20706         } else if (c == ']') {
  20707             /* XXX: incorrect as the first character in a class */
  20708             in_class = FALSE;
  20709         } else if (c == '\\') {
  20710             if (string_buffer_putc8(b, c))
  20711                 goto fail;
  20712             c = *p++;
  20713             if (c == '\n' || c == '\r')
  20714                 goto eol_error;
  20715             else if (c == '\0' && p >= s->buf_end)
  20716                 goto eof_error;
  20717             else if (c >= 0x80) {
  20718                 const uint8_t *p_next;
  20719                 c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
  20720                 if (c > 0x10FFFF) {
  20721                     goto invalid_utf8;
  20722                 }
  20723                 p = p_next;
  20724                 if (c == CP_LS || c == CP_PS)
  20725                     goto eol_error;
  20726             }
  20727         } else if (c >= 0x80) {
  20728             const uint8_t *p_next;
  20729             c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
  20730             if (c > 0x10FFFF) {
  20731             invalid_utf8:
  20732                 js_parse_error(s, "invalid UTF-8 sequence");
  20733                 goto fail;
  20734             }
  20735             p = p_next;
  20736             /* LS or PS are considered as line terminator */
  20737             if (c == CP_LS || c == CP_PS) {
  20738             eol_error:
  20739                 js_parse_error(s, "unexpected line terminator in regexp");
  20740                 goto fail;
  20741             }
  20742         }
  20743         if (string_buffer_putc(b, c))
  20744             goto fail;
  20745     }
  20746 
  20747     /* flags */
  20748     for(;;) {
  20749         const uint8_t *p_next = p;
  20750         c = *p_next++;
  20751         if (c >= 0x80) {
  20752             c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);
  20753             if (c > 0x10FFFF) {
  20754                 goto invalid_utf8;
  20755             }
  20756         }
  20757         if (!lre_js_is_ident_next(c))
  20758             break;
  20759         if (string_buffer_putc(b2, c))
  20760             goto fail;
  20761         p = p_next;
  20762     }
  20763 
  20764     s->token.val = TOK_REGEXP;
  20765     s->token.u.regexp.body = string_buffer_end(b);
  20766     s->token.u.regexp.flags = string_buffer_end(b2);
  20767     s->buf_ptr = p;
  20768     return 0;
  20769  fail:
  20770     string_buffer_free(b);
  20771     string_buffer_free(b2);
  20772     return -1;
  20773 }
  20774 
  20775 static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize,
  20776                                      char *static_buf)
  20777 {
  20778     char *buf, *new_buf;
  20779     size_t size, new_size;
  20780 
  20781     buf = *pbuf;
  20782     size = *psize;
  20783     if (size >= (SIZE_MAX / 3) * 2)
  20784         new_size = SIZE_MAX;
  20785     else
  20786         new_size = size + (size >> 1);
  20787     if (buf == static_buf) {
  20788         new_buf = js_malloc(ctx, new_size);
  20789         if (!new_buf)
  20790             return -1;
  20791         memcpy(new_buf, buf, size);
  20792     } else {
  20793         new_buf = js_realloc(ctx, buf, new_size);
  20794         if (!new_buf)
  20795             return -1;
  20796     }
  20797     *pbuf = new_buf;
  20798     *psize = new_size;
  20799     return 0;
  20800 }
  20801 
  20802 /* convert a TOK_IDENT to a keyword when needed */
  20803 static void update_token_ident(JSParseState *s)
  20804 {
  20805     if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD ||
  20806         (s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD &&
  20807          (s->cur_func->js_mode & JS_MODE_STRICT)) ||
  20808         (s->token.u.ident.atom == JS_ATOM_yield &&
  20809          ((s->cur_func->func_kind & JS_FUNC_GENERATOR) ||
  20810           (s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&
  20811            !s->cur_func->in_function_body && s->cur_func->parent &&
  20812            (s->cur_func->parent->func_kind & JS_FUNC_GENERATOR)))) ||
  20813         (s->token.u.ident.atom == JS_ATOM_await &&
  20814          (s->is_module ||
  20815           (s->cur_func->func_kind & JS_FUNC_ASYNC) ||
  20816           s->cur_func->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT ||
  20817           (s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&
  20818            !s->cur_func->in_function_body && s->cur_func->parent &&
  20819            ((s->cur_func->parent->func_kind & JS_FUNC_ASYNC) ||
  20820             s->cur_func->parent->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT))))) {
  20821         if (s->token.u.ident.has_escape) {
  20822             s->token.u.ident.is_reserved = TRUE;
  20823             s->token.val = TOK_IDENT;
  20824         } else {
  20825             /* The keywords atoms are pre allocated */
  20826             s->token.val = s->token.u.ident.atom - 1 + TOK_FIRST_KEYWORD;
  20827         }
  20828     }
  20829 }
  20830 
  20831 /* if the current token is an identifier or keyword, reparse it
  20832    according to the current function type */
  20833 static void reparse_ident_token(JSParseState *s)
  20834 {
  20835     if (s->token.val == TOK_IDENT ||
  20836         (s->token.val >= TOK_FIRST_KEYWORD &&
  20837          s->token.val <= TOK_LAST_KEYWORD)) {
  20838         s->token.val = TOK_IDENT;
  20839         s->token.u.ident.is_reserved = FALSE;
  20840         update_token_ident(s);
  20841     }
  20842 }
  20843 
  20844 /* 'c' is the first character. Return JS_ATOM_NULL in case of error */
  20845 static JSAtom parse_ident(JSParseState *s, const uint8_t **pp,
  20846                           BOOL *pident_has_escape, int c, BOOL is_private)
  20847 {
  20848     const uint8_t *p, *p1;
  20849     char ident_buf[128], *buf;
  20850     size_t ident_size, ident_pos;
  20851     JSAtom atom;
  20852 
  20853     p = *pp;
  20854     buf = ident_buf;
  20855     ident_size = sizeof(ident_buf);
  20856     ident_pos = 0;
  20857     if (is_private)
  20858         buf[ident_pos++] = '#';
  20859     for(;;) {
  20860         p1 = p;
  20861 
  20862         if (c < 128) {
  20863             buf[ident_pos++] = c;
  20864         } else {
  20865             ident_pos += unicode_to_utf8((uint8_t*)buf + ident_pos, c);
  20866         }
  20867         c = *p1++;
  20868         if (c == '\\' && *p1 == 'u') {
  20869             c = lre_parse_escape(&p1, TRUE);
  20870             *pident_has_escape = TRUE;
  20871         } else if (c >= 128) {
  20872             c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1);
  20873         }
  20874         if (!lre_js_is_ident_next(c))
  20875             break;
  20876         p = p1;
  20877         if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) {
  20878             if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) {
  20879                 atom = JS_ATOM_NULL;
  20880                 goto done;
  20881             }
  20882         }
  20883     }
  20884     atom = JS_NewAtomLen(s->ctx, buf, ident_pos);
  20885  done:
  20886     if (unlikely(buf != ident_buf))
  20887         js_free(s->ctx, buf);
  20888     *pp = p;
  20889     return atom;
  20890 }
  20891 
  20892 
  20893 static __exception int next_token(JSParseState *s)
  20894 {
  20895     const uint8_t *p;
  20896     int c;
  20897     BOOL ident_has_escape;
  20898     JSAtom atom;
  20899 
  20900     if (js_check_stack_overflow(s->ctx->rt, 0)) {
  20901         return js_parse_error(s, "stack overflow");
  20902     }
  20903 
  20904     free_token(s, &s->token);
  20905 
  20906     p = s->last_ptr = s->buf_ptr;
  20907     s->got_lf = FALSE;
  20908     s->last_line_num = s->token.line_num;
  20909  redo:
  20910     s->token.line_num = s->line_num;
  20911     s->token.ptr = p;
  20912     c = *p;
  20913     switch(c) {
  20914     case 0:
  20915         if (p >= s->buf_end) {
  20916             s->token.val = TOK_EOF;
  20917         } else {
  20918             goto def_token;
  20919         }
  20920         break;
  20921     case '`':
  20922         if (js_parse_template_part(s, p + 1))
  20923             goto fail;
  20924         p = s->buf_ptr;
  20925         break;
  20926     case '\'':
  20927     case '\"':
  20928         if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p))
  20929             goto fail;
  20930         break;
  20931     case '\r':  /* accept DOS and MAC newline sequences */
  20932         if (p[1] == '\n') {
  20933             p++;
  20934         }
  20935         /* fall thru */
  20936     case '\n':
  20937         p++;
  20938     line_terminator:
  20939         s->got_lf = TRUE;
  20940         s->line_num++;
  20941         goto redo;
  20942     case '\f':
  20943     case '\v':
  20944     case ' ':
  20945     case '\t':
  20946         p++;
  20947         goto redo;
  20948     case '/':
  20949         if (p[1] == '*') {
  20950             /* comment */
  20951             p += 2;
  20952             for(;;) {
  20953                 if (*p == '\0' && p >= s->buf_end) {
  20954                     js_parse_error(s, "unexpected end of comment");
  20955                     goto fail;
  20956                 }
  20957                 if (p[0] == '*' && p[1] == '/') {
  20958                     p += 2;
  20959                     break;
  20960                 }
  20961                 if (*p == '\n') {
  20962                     s->line_num++;
  20963                     s->got_lf = TRUE; /* considered as LF for ASI */
  20964                     p++;
  20965                 } else if (*p == '\r') {
  20966                     s->got_lf = TRUE; /* considered as LF for ASI */
  20967                     p++;
  20968                 } else if (*p >= 0x80) {
  20969                     c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
  20970                     if (c == CP_LS || c == CP_PS) {
  20971                         s->got_lf = TRUE; /* considered as LF for ASI */
  20972                     } else if (c == -1) {
  20973                         p++; /* skip invalid UTF-8 */
  20974                     }
  20975                 } else {
  20976                     p++;
  20977                 }
  20978             }
  20979             goto redo;
  20980         } else if (p[1] == '/') {
  20981             /* line comment */
  20982             p += 2;
  20983         skip_line_comment:
  20984             for(;;) {
  20985                 if (*p == '\0' && p >= s->buf_end)
  20986                     break;
  20987                 if (*p == '\r' || *p == '\n')
  20988                     break;
  20989                 if (*p >= 0x80) {
  20990                     c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
  20991                     /* LS or PS are considered as line terminator */
  20992                     if (c == CP_LS || c == CP_PS) {
  20993                         break;
  20994                     } else if (c == -1) {
  20995                         p++; /* skip invalid UTF-8 */
  20996                     }
  20997                 } else {
  20998                     p++;
  20999                 }
  21000             }
  21001             goto redo;
  21002         } else if (p[1] == '=') {
  21003             p += 2;
  21004             s->token.val = TOK_DIV_ASSIGN;
  21005         } else {
  21006             p++;
  21007             s->token.val = c;
  21008         }
  21009         break;
  21010     case '\\':
  21011         if (p[1] == 'u') {
  21012             const uint8_t *p1 = p + 1;
  21013             int c1 = lre_parse_escape(&p1, TRUE);
  21014             if (c1 >= 0 && lre_js_is_ident_first(c1)) {
  21015                 c = c1;
  21016                 p = p1;
  21017                 ident_has_escape = TRUE;
  21018                 goto has_ident;
  21019             } else {
  21020                 /* XXX: syntax error? */
  21021             }
  21022         }
  21023         goto def_token;
  21024     case 'a': case 'b': case 'c': case 'd':
  21025     case 'e': case 'f': case 'g': case 'h':
  21026     case 'i': case 'j': case 'k': case 'l':
  21027     case 'm': case 'n': case 'o': case 'p':
  21028     case 'q': case 'r': case 's': case 't':
  21029     case 'u': case 'v': case 'w': case 'x':
  21030     case 'y': case 'z':
  21031     case 'A': case 'B': case 'C': case 'D':
  21032     case 'E': case 'F': case 'G': case 'H':
  21033     case 'I': case 'J': case 'K': case 'L':
  21034     case 'M': case 'N': case 'O': case 'P':
  21035     case 'Q': case 'R': case 'S': case 'T':
  21036     case 'U': case 'V': case 'W': case 'X':
  21037     case 'Y': case 'Z':
  21038     case '_':
  21039     case '$':
  21040         /* identifier */
  21041         p++;
  21042         ident_has_escape = FALSE;
  21043     has_ident:
  21044         atom = parse_ident(s, &p, &ident_has_escape, c, FALSE);
  21045         if (atom == JS_ATOM_NULL)
  21046             goto fail;
  21047         s->token.u.ident.atom = atom;
  21048         s->token.u.ident.has_escape = ident_has_escape;
  21049         s->token.u.ident.is_reserved = FALSE;
  21050         s->token.val = TOK_IDENT;
  21051         update_token_ident(s);
  21052         break;
  21053     case '#':
  21054         /* private name */
  21055         {
  21056             const uint8_t *p1;
  21057             p++;
  21058             p1 = p;
  21059             c = *p1++;
  21060             if (c == '\\' && *p1 == 'u') {
  21061                 c = lre_parse_escape(&p1, TRUE);
  21062             } else if (c >= 128) {
  21063                 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1);
  21064             }
  21065             if (!lre_js_is_ident_first(c)) {
  21066                 js_parse_error(s, "invalid first character of private name");
  21067                 goto fail;
  21068             }
  21069             p = p1;
  21070             ident_has_escape = FALSE; /* not used */
  21071             atom = parse_ident(s, &p, &ident_has_escape, c, TRUE);
  21072             if (atom == JS_ATOM_NULL)
  21073                 goto fail;
  21074             s->token.u.ident.atom = atom;
  21075             s->token.val = TOK_PRIVATE_NAME;
  21076         }
  21077         break;
  21078     case '.':
  21079         if (p[1] == '.' && p[2] == '.') {
  21080             p += 3;
  21081             s->token.val = TOK_ELLIPSIS;
  21082             break;
  21083         }
  21084         if (p[1] >= '0' && p[1] <= '9') {
  21085             goto parse_number;
  21086         } else {
  21087             goto def_token;
  21088         }
  21089         break;
  21090     case '0':
  21091         /* in strict mode, octal literals are not accepted */
  21092         if (is_digit(p[1]) && (s->cur_func->js_mode & JS_MODE_STRICT)) {
  21093             js_parse_error(s, "octal literals are deprecated in strict mode");
  21094             goto fail;
  21095         }
  21096         goto parse_number;
  21097     case '1': case '2': case '3': case '4':
  21098     case '5': case '6': case '7': case '8':
  21099     case '9':
  21100         /* number */
  21101     parse_number:
  21102         {
  21103             JSValue ret;
  21104             const uint8_t *p1;
  21105             int flags, radix;
  21106             flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL |
  21107                 ATOD_ACCEPT_UNDERSCORES;
  21108             flags |= ATOD_ACCEPT_SUFFIX;
  21109 #ifdef CONFIG_BIGNUM
  21110             if (s->cur_func->js_mode & JS_MODE_MATH) {
  21111                 flags |= ATOD_MODE_BIGINT;
  21112                 if (s->cur_func->js_mode & JS_MODE_MATH)
  21113                     flags |= ATOD_TYPE_BIG_FLOAT;
  21114             }
  21115 #endif
  21116             radix = 0;
  21117 #ifdef CONFIG_BIGNUM
  21118             s->token.u.num.exponent = 0;
  21119             ret = js_atof2(s->ctx, (const char *)p, (const char **)&p, radix,
  21120                            flags, &s->token.u.num.exponent);
  21121 #else
  21122             ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix,
  21123                           flags);
  21124 #endif
  21125             if (JS_IsException(ret))
  21126                 goto fail;
  21127             /* reject `10instanceof Number` */
  21128             if (JS_VALUE_IS_NAN(ret) ||
  21129                 lre_js_is_ident_next(unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1))) {
  21130                 JS_FreeValue(s->ctx, ret);
  21131                 js_parse_error(s, "invalid number literal");
  21132                 goto fail;
  21133             }
  21134             s->token.val = TOK_NUMBER;
  21135             s->token.u.num.val = ret;
  21136         }
  21137         break;
  21138     case '*':
  21139         if (p[1] == '=') {
  21140             p += 2;
  21141             s->token.val = TOK_MUL_ASSIGN;
  21142         } else if (p[1] == '*') {
  21143             if (p[2] == '=') {
  21144                 p += 3;
  21145                 s->token.val = TOK_POW_ASSIGN;
  21146             } else {
  21147                 p += 2;
  21148                 s->token.val = TOK_POW;
  21149             }
  21150         } else {
  21151             goto def_token;
  21152         }
  21153         break;
  21154     case '%':
  21155         if (p[1] == '=') {
  21156             p += 2;
  21157             s->token.val = TOK_MOD_ASSIGN;
  21158         } else {
  21159             goto def_token;
  21160         }
  21161         break;
  21162     case '+':
  21163         if (p[1] == '=') {
  21164             p += 2;
  21165             s->token.val = TOK_PLUS_ASSIGN;
  21166         } else if (p[1] == '+') {
  21167             p += 2;
  21168             s->token.val = TOK_INC;
  21169         } else {
  21170             goto def_token;
  21171         }
  21172         break;
  21173     case '-':
  21174         if (p[1] == '=') {
  21175             p += 2;
  21176             s->token.val = TOK_MINUS_ASSIGN;
  21177         } else if (p[1] == '-') {
  21178             if (s->allow_html_comments &&
  21179                 p[2] == '>' && s->last_line_num != s->line_num) {
  21180                 /* Annex B: `-->` at beginning of line is an html comment end.
  21181                    It extends to the end of the line.
  21182                  */
  21183                 goto skip_line_comment;
  21184             }
  21185             p += 2;
  21186             s->token.val = TOK_DEC;
  21187         } else {
  21188             goto def_token;
  21189         }
  21190         break;
  21191     case '<':
  21192         if (p[1] == '=') {
  21193             p += 2;
  21194             s->token.val = TOK_LTE;
  21195         } else if (p[1] == '<') {
  21196             if (p[2] == '=') {
  21197                 p += 3;
  21198                 s->token.val = TOK_SHL_ASSIGN;
  21199             } else {
  21200                 p += 2;
  21201                 s->token.val = TOK_SHL;
  21202             }
  21203         } else if (s->allow_html_comments &&
  21204                    p[1] == '!' && p[2] == '-' && p[3] == '-') {
  21205             /* Annex B: handle `<!--` single line html comments */
  21206             goto skip_line_comment;
  21207         } else {
  21208             goto def_token;
  21209         }
  21210         break;
  21211     case '>':
  21212         if (p[1] == '=') {
  21213             p += 2;
  21214             s->token.val = TOK_GTE;
  21215         } else if (p[1] == '>') {
  21216             if (p[2] == '>') {
  21217                 if (p[3] == '=') {
  21218                     p += 4;
  21219                     s->token.val = TOK_SHR_ASSIGN;
  21220                 } else {
  21221                     p += 3;
  21222                     s->token.val = TOK_SHR;
  21223                 }
  21224             } else if (p[2] == '=') {
  21225                 p += 3;
  21226                 s->token.val = TOK_SAR_ASSIGN;
  21227             } else {
  21228                 p += 2;
  21229                 s->token.val = TOK_SAR;
  21230             }
  21231         } else {
  21232             goto def_token;
  21233         }
  21234         break;
  21235     case '=':
  21236         if (p[1] == '=') {
  21237             if (p[2] == '=') {
  21238                 p += 3;
  21239                 s->token.val = TOK_STRICT_EQ;
  21240             } else {
  21241                 p += 2;
  21242                 s->token.val = TOK_EQ;
  21243             }
  21244         } else if (p[1] == '>') {
  21245             p += 2;
  21246             s->token.val = TOK_ARROW;
  21247         } else {
  21248             goto def_token;
  21249         }
  21250         break;
  21251     case '!':
  21252         if (p[1] == '=') {
  21253             if (p[2] == '=') {
  21254                 p += 3;
  21255                 s->token.val = TOK_STRICT_NEQ;
  21256             } else {
  21257                 p += 2;
  21258                 s->token.val = TOK_NEQ;
  21259             }
  21260         } else {
  21261             goto def_token;
  21262         }
  21263         break;
  21264     case '&':
  21265         if (p[1] == '=') {
  21266             p += 2;
  21267             s->token.val = TOK_AND_ASSIGN;
  21268         } else if (p[1] == '&') {
  21269             if (p[2] == '=') {
  21270                 p += 3;
  21271                 s->token.val = TOK_LAND_ASSIGN;
  21272             } else {
  21273                 p += 2;
  21274                 s->token.val = TOK_LAND;
  21275             }
  21276         } else {
  21277             goto def_token;
  21278         }
  21279         break;
  21280 #ifdef CONFIG_BIGNUM
  21281         /* in math mode, '^' is the power operator. '^^' is always the
  21282            xor operator and '**' is always the power operator */
  21283     case '^':
  21284         if (p[1] == '=') {
  21285             p += 2;
  21286             if (s->cur_func->js_mode & JS_MODE_MATH)
  21287                 s->token.val = TOK_MATH_POW_ASSIGN;
  21288             else
  21289                 s->token.val = TOK_XOR_ASSIGN;
  21290         } else if (p[1] == '^') {
  21291             if (p[2] == '=') {
  21292                 p += 3;
  21293                 s->token.val = TOK_XOR_ASSIGN;
  21294             } else {
  21295                 p += 2;
  21296                 s->token.val = '^';
  21297             }
  21298         } else {
  21299             p++;
  21300             if (s->cur_func->js_mode & JS_MODE_MATH)
  21301                 s->token.val = TOK_MATH_POW;
  21302             else
  21303                 s->token.val = '^';
  21304         }
  21305         break;
  21306 #else
  21307     case '^':
  21308         if (p[1] == '=') {
  21309             p += 2;
  21310             s->token.val = TOK_XOR_ASSIGN;
  21311         } else {
  21312             goto def_token;
  21313         }
  21314         break;
  21315 #endif
  21316     case '|':
  21317         if (p[1] == '=') {
  21318             p += 2;
  21319             s->token.val = TOK_OR_ASSIGN;
  21320         } else if (p[1] == '|') {
  21321             if (p[2] == '=') {
  21322                 p += 3;
  21323                 s->token.val = TOK_LOR_ASSIGN;
  21324             } else {
  21325                 p += 2;
  21326                 s->token.val = TOK_LOR;
  21327             }
  21328         } else {
  21329             goto def_token;
  21330         }
  21331         break;
  21332     case '?':
  21333         if (p[1] == '?') {
  21334             if (p[2] == '=') {
  21335                 p += 3;
  21336                 s->token.val = TOK_DOUBLE_QUESTION_MARK_ASSIGN;
  21337             } else {
  21338                 p += 2;
  21339                 s->token.val = TOK_DOUBLE_QUESTION_MARK;
  21340             }
  21341         } else if (p[1] == '.' && !(p[2] >= '0' && p[2] <= '9')) {
  21342             p += 2;
  21343             s->token.val = TOK_QUESTION_MARK_DOT;
  21344         } else {
  21345             goto def_token;
  21346         }
  21347         break;
  21348     default:
  21349         if (c >= 128) {
  21350             /* unicode value */
  21351             c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
  21352             switch(c) {
  21353             case CP_PS:
  21354             case CP_LS:
  21355                 /* XXX: should avoid incrementing line_number, but
  21356                    needed to handle HTML comments */
  21357                 goto line_terminator;
  21358             default:
  21359                 if (lre_is_space(c)) {
  21360                     goto redo;
  21361                 } else if (lre_js_is_ident_first(c)) {
  21362                     ident_has_escape = FALSE;
  21363                     goto has_ident;
  21364                 } else {
  21365                     js_parse_error(s, "unexpected character");
  21366                     goto fail;
  21367                 }
  21368             }
  21369         }
  21370     def_token:
  21371         s->token.val = c;
  21372         p++;
  21373         break;
  21374     }
  21375     s->buf_ptr = p;
  21376 
  21377     //    dump_token(s, &s->token);
  21378     return 0;
  21379 
  21380  fail:
  21381     s->token.val = TOK_ERROR;
  21382     return -1;
  21383 }
  21384 
  21385 /* 'c' is the first character. Return JS_ATOM_NULL in case of error */
  21386 static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c)
  21387 {
  21388     const uint8_t *p;
  21389     char ident_buf[128], *buf;
  21390     size_t ident_size, ident_pos;
  21391     JSAtom atom;
  21392 
  21393     p = *pp;
  21394     buf = ident_buf;
  21395     ident_size = sizeof(ident_buf);
  21396     ident_pos = 0;
  21397     for(;;) {
  21398         buf[ident_pos++] = c;
  21399         c = *p;
  21400         if (c >= 128 || !lre_is_id_continue_byte(c))
  21401             break;
  21402         p++;
  21403         if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) {
  21404             if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) {
  21405                 atom = JS_ATOM_NULL;
  21406                 goto done;
  21407             }
  21408         }
  21409     }
  21410     atom = JS_NewAtomLen(s->ctx, buf, ident_pos);
  21411  done:
  21412     if (unlikely(buf != ident_buf))
  21413         js_free(s->ctx, buf);
  21414     *pp = p;
  21415     return atom;
  21416 }
  21417 
  21418 static __exception int json_next_token(JSParseState *s)
  21419 {
  21420     const uint8_t *p;
  21421     int c;
  21422     JSAtom atom;
  21423 
  21424     if (js_check_stack_overflow(s->ctx->rt, 0)) {
  21425         return js_parse_error(s, "stack overflow");
  21426     }
  21427 
  21428     free_token(s, &s->token);
  21429 
  21430     p = s->last_ptr = s->buf_ptr;
  21431     s->last_line_num = s->token.line_num;
  21432  redo:
  21433     s->token.line_num = s->line_num;
  21434     s->token.ptr = p;
  21435     c = *p;
  21436     switch(c) {
  21437     case 0:
  21438         if (p >= s->buf_end) {
  21439             s->token.val = TOK_EOF;
  21440         } else {
  21441             goto def_token;
  21442         }
  21443         break;
  21444     case '\'':
  21445         if (!s->ext_json) {
  21446             /* JSON does not accept single quoted strings */
  21447             goto def_token;
  21448         }
  21449         /* fall through */
  21450     case '\"':
  21451         if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p))
  21452             goto fail;
  21453         break;
  21454     case '\r':  /* accept DOS and MAC newline sequences */
  21455         if (p[1] == '\n') {
  21456             p++;
  21457         }
  21458         /* fall thru */
  21459     case '\n':
  21460         p++;
  21461         s->line_num++;
  21462         goto redo;
  21463     case '\f':
  21464     case '\v':
  21465         if (!s->ext_json) {
  21466             /* JSONWhitespace does not match <VT>, nor <FF> */
  21467             goto def_token;
  21468         }
  21469         /* fall through */
  21470     case ' ':
  21471     case '\t':
  21472         p++;
  21473         goto redo;
  21474     case '/':
  21475         if (!s->ext_json) {
  21476             /* JSON does not accept comments */
  21477             goto def_token;
  21478         }
  21479         if (p[1] == '*') {
  21480             /* comment */
  21481             p += 2;
  21482             for(;;) {
  21483                 if (*p == '\0' && p >= s->buf_end) {
  21484                     js_parse_error(s, "unexpected end of comment");
  21485                     goto fail;
  21486                 }
  21487                 if (p[0] == '*' && p[1] == '/') {
  21488                     p += 2;
  21489                     break;
  21490                 }
  21491                 if (*p == '\n') {
  21492                     s->line_num++;
  21493                     p++;
  21494                 } else if (*p == '\r') {
  21495                     p++;
  21496                 } else if (*p >= 0x80) {
  21497                     c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
  21498                     if (c == -1) {
  21499                         p++; /* skip invalid UTF-8 */
  21500                     }
  21501                 } else {
  21502                     p++;
  21503                 }
  21504             }
  21505             goto redo;
  21506         } else if (p[1] == '/') {
  21507             /* line comment */
  21508             p += 2;
  21509             for(;;) {
  21510                 if (*p == '\0' && p >= s->buf_end)
  21511                     break;
  21512                 if (*p == '\r' || *p == '\n')
  21513                     break;
  21514                 if (*p >= 0x80) {
  21515                     c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
  21516                     /* LS or PS are considered as line terminator */
  21517                     if (c == CP_LS || c == CP_PS) {
  21518                         break;
  21519                     } else if (c == -1) {
  21520                         p++; /* skip invalid UTF-8 */
  21521                     }
  21522                 } else {
  21523                     p++;
  21524                 }
  21525             }
  21526             goto redo;
  21527         } else {
  21528             goto def_token;
  21529         }
  21530         break;
  21531     case 'a': case 'b': case 'c': case 'd':
  21532     case 'e': case 'f': case 'g': case 'h':
  21533     case 'i': case 'j': case 'k': case 'l':
  21534     case 'm': case 'n': case 'o': case 'p':
  21535     case 'q': case 'r': case 's': case 't':
  21536     case 'u': case 'v': case 'w': case 'x':
  21537     case 'y': case 'z':
  21538     case 'A': case 'B': case 'C': case 'D':
  21539     case 'E': case 'F': case 'G': case 'H':
  21540     case 'I': case 'J': case 'K': case 'L':
  21541     case 'M': case 'N': case 'O': case 'P':
  21542     case 'Q': case 'R': case 'S': case 'T':
  21543     case 'U': case 'V': case 'W': case 'X':
  21544     case 'Y': case 'Z':
  21545     case '_':
  21546     case '$':
  21547         /* identifier : only pure ascii characters are accepted */
  21548         p++;
  21549         atom = json_parse_ident(s, &p, c);
  21550         if (atom == JS_ATOM_NULL)
  21551             goto fail;
  21552         s->token.u.ident.atom = atom;
  21553         s->token.u.ident.has_escape = FALSE;
  21554         s->token.u.ident.is_reserved = FALSE;
  21555         s->token.val = TOK_IDENT;
  21556         break;
  21557     case '+':
  21558         if (!s->ext_json || !is_digit(p[1]))
  21559             goto def_token;
  21560         goto parse_number;
  21561     case '0':
  21562         if (is_digit(p[1]))
  21563             goto def_token;
  21564         goto parse_number;
  21565     case '-':
  21566         if (!is_digit(p[1]))
  21567             goto def_token;
  21568         goto parse_number;
  21569     case '1': case '2': case '3': case '4':
  21570     case '5': case '6': case '7': case '8':
  21571     case '9':
  21572         /* number */
  21573     parse_number:
  21574         {
  21575             JSValue ret;
  21576             int flags, radix;
  21577             if (!s->ext_json) {
  21578                 flags = 0;
  21579                 radix = 10;
  21580             } else {
  21581                 flags = ATOD_ACCEPT_BIN_OCT;
  21582                 radix = 0;
  21583             }
  21584             ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix,
  21585                           flags);
  21586             if (JS_IsException(ret))
  21587                 goto fail;
  21588             s->token.val = TOK_NUMBER;
  21589             s->token.u.num.val = ret;
  21590         }
  21591         break;
  21592     default:
  21593         if (c >= 128) {
  21594             js_parse_error(s, "unexpected character");
  21595             goto fail;
  21596         }
  21597     def_token:
  21598         s->token.val = c;
  21599         p++;
  21600         break;
  21601     }
  21602     s->buf_ptr = p;
  21603 
  21604     //    dump_token(s, &s->token);
  21605     return 0;
  21606 
  21607  fail:
  21608     s->token.val = TOK_ERROR;
  21609     return -1;
  21610 }
  21611 
  21612 static int match_identifier(const uint8_t *p, const char *s) {
  21613     uint32_t c;
  21614     while (*s) {
  21615         if ((uint8_t)*s++ != *p++)
  21616             return 0;
  21617     }
  21618     c = *p;
  21619     if (c >= 128)
  21620         c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
  21621     return !lre_js_is_ident_next(c);
  21622 }
  21623 
  21624 /* simple_next_token() is used to check for the next token in simple cases.
  21625    It is only used for ':' and '=>', 'let' or 'function' look-ahead.
  21626    (*pp) is only set if TOK_IMPORT is returned for JS_DetectModule()
  21627    Whitespace and comments are skipped correctly.
  21628    Then the next token is analyzed, only for specific words.
  21629    Return values:
  21630    - '\n' if !no_line_terminator
  21631    - TOK_ARROW, TOK_IN, TOK_IMPORT, TOK_OF, TOK_EXPORT, TOK_FUNCTION
  21632    - TOK_IDENT is returned for other identifiers and keywords
  21633    - otherwise the next character or unicode codepoint is returned.
  21634  */
  21635 static int simple_next_token(const uint8_t **pp, BOOL no_line_terminator)
  21636 {
  21637     const uint8_t *p;
  21638     uint32_t c;
  21639 
  21640     /* skip spaces and comments */
  21641     p = *pp;
  21642     for (;;) {
  21643         switch(c = *p++) {
  21644         case '\r':
  21645         case '\n':
  21646             if (no_line_terminator)
  21647                 return '\n';
  21648             continue;
  21649         case ' ':
  21650         case '\t':
  21651         case '\v':
  21652         case '\f':
  21653             continue;
  21654         case '/':
  21655             if (*p == '/') {
  21656                 if (no_line_terminator)
  21657                     return '\n';
  21658                 while (*p && *p != '\r' && *p != '\n')
  21659                     p++;
  21660                 continue;
  21661             }
  21662             if (*p == '*') {
  21663                 while (*++p) {
  21664                     if ((*p == '\r' || *p == '\n') && no_line_terminator)
  21665                         return '\n';
  21666                     if (*p == '*' && p[1] == '/') {
  21667                         p += 2;
  21668                         break;
  21669                     }
  21670                 }
  21671                 continue;
  21672             }
  21673             break;
  21674         case '=':
  21675             if (*p == '>')
  21676                 return TOK_ARROW;
  21677             break;
  21678         case 'i':
  21679             if (match_identifier(p, "n"))
  21680                 return TOK_IN;
  21681             if (match_identifier(p, "mport")) {
  21682                 *pp = p + 5;
  21683                 return TOK_IMPORT;
  21684             }
  21685             return TOK_IDENT;
  21686         case 'o':
  21687             if (match_identifier(p, "f"))
  21688                 return TOK_OF;
  21689             return TOK_IDENT;
  21690         case 'e':
  21691             if (match_identifier(p, "xport"))
  21692                 return TOK_EXPORT;
  21693             return TOK_IDENT;
  21694         case 'f':
  21695             if (match_identifier(p, "unction"))
  21696                 return TOK_FUNCTION;
  21697             return TOK_IDENT;
  21698         case '\\':
  21699             if (*p == 'u') {
  21700                 if (lre_js_is_ident_first(lre_parse_escape(&p, TRUE)))
  21701                     return TOK_IDENT;
  21702             }
  21703             break;
  21704         default:
  21705             if (c >= 128) {
  21706                 c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p);
  21707                 if (no_line_terminator && (c == CP_PS || c == CP_LS))
  21708                     return '\n';
  21709             }
  21710             if (lre_is_space(c))
  21711                 continue;
  21712             if (lre_js_is_ident_first(c))
  21713                 return TOK_IDENT;
  21714             break;
  21715         }
  21716         return c;
  21717     }
  21718 }
  21719 
  21720 static int peek_token(JSParseState *s, BOOL no_line_terminator)
  21721 {
  21722     const uint8_t *p = s->buf_ptr;
  21723     return simple_next_token(&p, no_line_terminator);
  21724 }
  21725 
  21726 static void skip_shebang(const uint8_t **pp, const uint8_t *buf_end)
  21727 {
  21728     const uint8_t *p = *pp;
  21729     int c;
  21730 
  21731     if (p[0] == '#' && p[1] == '!') {
  21732         p += 2;
  21733         while (p < buf_end) {
  21734             if (*p == '\n' || *p == '\r') {
  21735                 break;
  21736             } else if (*p >= 0x80) {
  21737                 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
  21738                 if (c == CP_LS || c == CP_PS) {
  21739                     break;
  21740                 } else if (c == -1) {
  21741                     p++; /* skip invalid UTF-8 */
  21742                 }
  21743             } else {
  21744                 p++;
  21745             }
  21746         }
  21747         *pp = p;
  21748     }
  21749 }
  21750 
  21751 /* return true if 'input' contains the source of a module
  21752    (heuristic). 'input' must be a zero terminated.
  21753 
  21754    Heuristic: skip comments and expect 'import' keyword not followed
  21755    by '(' or '.' or export keyword.
  21756 */
  21757 BOOL JS_DetectModule(const char *input, size_t input_len)
  21758 {
  21759     const uint8_t *p = (const uint8_t *)input;
  21760     int tok;
  21761 
  21762     skip_shebang(&p, p + input_len);
  21763     switch(simple_next_token(&p, FALSE)) {
  21764     case TOK_IMPORT:
  21765         tok = simple_next_token(&p, FALSE);
  21766         return (tok != '.' && tok != '(');
  21767     case TOK_EXPORT:
  21768         return TRUE;
  21769     default:
  21770         return FALSE;
  21771     }
  21772 }
  21773 
  21774 static inline int get_prev_opcode(JSFunctionDef *fd) {
  21775     if (fd->last_opcode_pos < 0)
  21776         return OP_invalid;
  21777     else
  21778         return fd->byte_code.buf[fd->last_opcode_pos];
  21779 }
  21780 
  21781 static BOOL js_is_live_code(JSParseState *s) {
  21782     switch (get_prev_opcode(s->cur_func)) {
  21783     case OP_tail_call:
  21784     case OP_tail_call_method:
  21785     case OP_return:
  21786     case OP_return_undef:
  21787     case OP_return_async:
  21788     case OP_throw:
  21789     case OP_throw_error:
  21790     case OP_goto:
  21791 #if SHORT_OPCODES
  21792     case OP_goto8:
  21793     case OP_goto16:
  21794 #endif
  21795     case OP_ret:
  21796         return FALSE;
  21797     default:
  21798         return TRUE;
  21799     }
  21800 }
  21801 
  21802 static void emit_u8(JSParseState *s, uint8_t val)
  21803 {
  21804     dbuf_putc(&s->cur_func->byte_code, val);
  21805 }
  21806 
  21807 static void emit_u16(JSParseState *s, uint16_t val)
  21808 {
  21809     dbuf_put_u16(&s->cur_func->byte_code, val);
  21810 }
  21811 
  21812 static void emit_u32(JSParseState *s, uint32_t val)
  21813 {
  21814     dbuf_put_u32(&s->cur_func->byte_code, val);
  21815 }
  21816 
  21817 static void emit_op(JSParseState *s, uint8_t val)
  21818 {
  21819     JSFunctionDef *fd = s->cur_func;
  21820     DynBuf *bc = &fd->byte_code;
  21821 
  21822     /* Use the line number of the last token used, not the next token,
  21823        nor the current offset in the source file.
  21824      */
  21825     if (unlikely(fd->last_opcode_line_num != s->last_line_num)) {
  21826         dbuf_putc(bc, OP_line_num);
  21827         dbuf_put_u32(bc, s->last_line_num);
  21828         fd->last_opcode_line_num = s->last_line_num;
  21829     }
  21830     fd->last_opcode_pos = bc->size;
  21831     dbuf_putc(bc, val);
  21832 }
  21833 
  21834 static void emit_atom(JSParseState *s, JSAtom name)
  21835 {
  21836     emit_u32(s, JS_DupAtom(s->ctx, name));
  21837 }
  21838 
  21839 static int update_label(JSFunctionDef *s, int label, int delta)
  21840 {
  21841     LabelSlot *ls;
  21842 
  21843     assert(label >= 0 && label < s->label_count);
  21844     ls = &s->label_slots[label];
  21845     ls->ref_count += delta;
  21846     assert(ls->ref_count >= 0);
  21847     return ls->ref_count;
  21848 }
  21849 
  21850 static int new_label_fd(JSFunctionDef *fd, int label)
  21851 {
  21852     LabelSlot *ls;
  21853 
  21854     if (label < 0) {
  21855         if (js_resize_array(fd->ctx, (void *)&fd->label_slots,
  21856                             sizeof(fd->label_slots[0]),
  21857                             &fd->label_size, fd->label_count + 1))
  21858             return -1;
  21859         label = fd->label_count++;
  21860         ls = &fd->label_slots[label];
  21861         ls->ref_count = 0;
  21862         ls->pos = -1;
  21863         ls->pos2 = -1;
  21864         ls->addr = -1;
  21865         ls->first_reloc = NULL;
  21866     }
  21867     return label;
  21868 }
  21869 
  21870 static int new_label(JSParseState *s)
  21871 {
  21872     return new_label_fd(s->cur_func, -1);
  21873 }
  21874 
  21875 /* don't update the last opcode and don't emit line number info */
  21876 static void emit_label_raw(JSParseState *s, int label)
  21877 {
  21878     emit_u8(s, OP_label);
  21879     emit_u32(s, label);
  21880     s->cur_func->label_slots[label].pos = s->cur_func->byte_code.size;
  21881 }
  21882 
  21883 /* return the label ID offset */
  21884 static int emit_label(JSParseState *s, int label)
  21885 {
  21886     if (label >= 0) {
  21887         emit_op(s, OP_label);
  21888         emit_u32(s, label);
  21889         s->cur_func->label_slots[label].pos = s->cur_func->byte_code.size;
  21890         return s->cur_func->byte_code.size - 4;
  21891     } else {
  21892         return -1;
  21893     }
  21894 }
  21895 
  21896 /* return label or -1 if dead code */
  21897 static int emit_goto(JSParseState *s, int opcode, int label)
  21898 {
  21899     if (js_is_live_code(s)) {
  21900         if (label < 0)
  21901             label = new_label(s);
  21902         emit_op(s, opcode);
  21903         emit_u32(s, label);
  21904         s->cur_func->label_slots[label].ref_count++;
  21905         return label;
  21906     }
  21907     return -1;
  21908 }
  21909 
  21910 /* return the constant pool index. 'val' is not duplicated. */
  21911 static int cpool_add(JSParseState *s, JSValue val)
  21912 {
  21913     JSFunctionDef *fd = s->cur_func;
  21914 
  21915     if (js_resize_array(s->ctx, (void *)&fd->cpool, sizeof(fd->cpool[0]),
  21916                         &fd->cpool_size, fd->cpool_count + 1))
  21917         return -1;
  21918     fd->cpool[fd->cpool_count++] = val;
  21919     return fd->cpool_count - 1;
  21920 }
  21921 
  21922 static __exception int emit_push_const(JSParseState *s, JSValueConst val,
  21923                                        BOOL as_atom)
  21924 {
  21925     int idx;
  21926 
  21927     if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING && as_atom) {
  21928         JSAtom atom;
  21929         /* warning: JS_NewAtomStr frees the string value */
  21930         JS_DupValue(s->ctx, val);
  21931         atom = JS_NewAtomStr(s->ctx, JS_VALUE_GET_STRING(val));
  21932         if (atom != JS_ATOM_NULL && !__JS_AtomIsTaggedInt(atom)) {
  21933             emit_op(s, OP_push_atom_value);
  21934             emit_u32(s, atom);
  21935             return 0;
  21936         }
  21937     }
  21938 
  21939     idx = cpool_add(s, JS_DupValue(s->ctx, val));
  21940     if (idx < 0)
  21941         return -1;
  21942     emit_op(s, OP_push_const);
  21943     emit_u32(s, idx);
  21944     return 0;
  21945 }
  21946 
  21947 /* return the variable index or -1 if not found,
  21948    add ARGUMENT_VAR_OFFSET for argument variables */
  21949 static int find_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
  21950 {
  21951     int i;
  21952     for(i = fd->arg_count; i-- > 0;) {
  21953         if (fd->args[i].var_name == name)
  21954             return i | ARGUMENT_VAR_OFFSET;
  21955     }
  21956     return -1;
  21957 }
  21958 
  21959 static int find_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
  21960 {
  21961     int i;
  21962     for(i = fd->var_count; i-- > 0;) {
  21963         if (fd->vars[i].var_name == name && fd->vars[i].scope_level == 0)
  21964             return i;
  21965     }
  21966     return find_arg(ctx, fd, name);
  21967 }
  21968 
  21969 /* find a variable declaration in a given scope */
  21970 static int find_var_in_scope(JSContext *ctx, JSFunctionDef *fd,
  21971                              JSAtom name, int scope_level)
  21972 {
  21973     int scope_idx;
  21974     for(scope_idx = fd->scopes[scope_level].first; scope_idx >= 0;
  21975         scope_idx = fd->vars[scope_idx].scope_next) {
  21976         if (fd->vars[scope_idx].scope_level != scope_level)
  21977             break;
  21978         if (fd->vars[scope_idx].var_name == name)
  21979             return scope_idx;
  21980     }
  21981     return -1;
  21982 }
  21983 
  21984 /* return true if scope == parent_scope or if scope is a child of
  21985    parent_scope */
  21986 static BOOL is_child_scope(JSContext *ctx, JSFunctionDef *fd,
  21987                            int scope, int parent_scope)
  21988 {
  21989     while (scope >= 0) {
  21990         if (scope == parent_scope)
  21991             return TRUE;
  21992         scope = fd->scopes[scope].parent;
  21993     }
  21994     return FALSE;
  21995 }
  21996 
  21997 /* find a 'var' declaration in the same scope or a child scope */
  21998 static int find_var_in_child_scope(JSContext *ctx, JSFunctionDef *fd,
  21999                                    JSAtom name, int scope_level)
  22000 {
  22001     int i;
  22002     for(i = 0; i < fd->var_count; i++) {
  22003         JSVarDef *vd = &fd->vars[i];
  22004         if (vd->var_name == name && vd->scope_level == 0) {
  22005             if (is_child_scope(ctx, fd, vd->scope_next,
  22006                                scope_level))
  22007                 return i;
  22008         }
  22009     }
  22010     return -1;
  22011 }
  22012 
  22013 
  22014 static JSGlobalVar *find_global_var(JSFunctionDef *fd, JSAtom name)
  22015 {
  22016     int i;
  22017     for(i = 0; i < fd->global_var_count; i++) {
  22018         JSGlobalVar *hf = &fd->global_vars[i];
  22019         if (hf->var_name == name)
  22020             return hf;
  22021     }
  22022     return NULL;
  22023 
  22024 }
  22025 
  22026 static JSGlobalVar *find_lexical_global_var(JSFunctionDef *fd, JSAtom name)
  22027 {
  22028     JSGlobalVar *hf = find_global_var(fd, name);
  22029     if (hf && hf->is_lexical)
  22030         return hf;
  22031     else
  22032         return NULL;
  22033 }
  22034 
  22035 static int find_lexical_decl(JSContext *ctx, JSFunctionDef *fd, JSAtom name,
  22036                              int scope_idx, BOOL check_catch_var)
  22037 {
  22038     while (scope_idx >= 0) {
  22039         JSVarDef *vd = &fd->vars[scope_idx];
  22040         if (vd->var_name == name &&
  22041             (vd->is_lexical || (vd->var_kind == JS_VAR_CATCH &&
  22042                                 check_catch_var)))
  22043             return scope_idx;
  22044         scope_idx = vd->scope_next;
  22045     }
  22046 
  22047     if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_GLOBAL) {
  22048         if (find_lexical_global_var(fd, name))
  22049             return GLOBAL_VAR_OFFSET;
  22050     }
  22051     return -1;
  22052 }
  22053 
  22054 static int push_scope(JSParseState *s) {
  22055     if (s->cur_func) {
  22056         JSFunctionDef *fd = s->cur_func;
  22057         int scope = fd->scope_count;
  22058         /* XXX: should check for scope overflow */
  22059         if ((fd->scope_count + 1) > fd->scope_size) {
  22060             int new_size;
  22061             size_t slack;
  22062             JSVarScope *new_buf;
  22063             /* XXX: potential arithmetic overflow */
  22064             new_size = max_int(fd->scope_count + 1, fd->scope_size * 3 / 2);
  22065             if (fd->scopes == fd->def_scope_array) {
  22066                 new_buf = js_realloc2(s->ctx, NULL, new_size * sizeof(*fd->scopes), &slack);
  22067                 if (!new_buf)
  22068                     return -1;
  22069                 memcpy(new_buf, fd->scopes, fd->scope_count * sizeof(*fd->scopes));
  22070             } else {
  22071                 new_buf = js_realloc2(s->ctx, fd->scopes, new_size * sizeof(*fd->scopes), &slack);
  22072                 if (!new_buf)
  22073                     return -1;
  22074             }
  22075             new_size += slack / sizeof(*new_buf);
  22076             fd->scopes = new_buf;
  22077             fd->scope_size = new_size;
  22078         }
  22079         fd->scope_count++;
  22080         fd->scopes[scope].parent = fd->scope_level;
  22081         fd->scopes[scope].first = fd->scope_first;
  22082         emit_op(s, OP_enter_scope);
  22083         emit_u16(s, scope);
  22084         return fd->scope_level = scope;
  22085     }
  22086     return 0;
  22087 }
  22088 
  22089 static int get_first_lexical_var(JSFunctionDef *fd, int scope)
  22090 {
  22091     while (scope >= 0) {
  22092         int scope_idx = fd->scopes[scope].first;
  22093         if (scope_idx >= 0)
  22094             return scope_idx;
  22095         scope = fd->scopes[scope].parent;
  22096     }
  22097     return -1;
  22098 }
  22099 
  22100 static void pop_scope(JSParseState *s) {
  22101     if (s->cur_func) {
  22102         /* disable scoped variables */
  22103         JSFunctionDef *fd = s->cur_func;
  22104         int scope = fd->scope_level;
  22105         emit_op(s, OP_leave_scope);
  22106         emit_u16(s, scope);
  22107         fd->scope_level = fd->scopes[scope].parent;
  22108         fd->scope_first = get_first_lexical_var(fd, fd->scope_level);
  22109     }
  22110 }
  22111 
  22112 static void close_scopes(JSParseState *s, int scope, int scope_stop)
  22113 {
  22114     while (scope > scope_stop) {
  22115         emit_op(s, OP_leave_scope);
  22116         emit_u16(s, scope);
  22117         scope = s->cur_func->scopes[scope].parent;
  22118     }
  22119 }
  22120 
  22121 /* return the variable index or -1 if error */
  22122 static int add_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
  22123 {
  22124     JSVarDef *vd;
  22125 
  22126     /* the local variable indexes are currently stored on 16 bits */
  22127     if (fd->var_count >= JS_MAX_LOCAL_VARS) {
  22128         JS_ThrowInternalError(ctx, "too many local variables");
  22129         return -1;
  22130     }
  22131     if (js_resize_array(ctx, (void **)&fd->vars, sizeof(fd->vars[0]),
  22132                         &fd->var_size, fd->var_count + 1))
  22133         return -1;
  22134     vd = &fd->vars[fd->var_count++];
  22135     memset(vd, 0, sizeof(*vd));
  22136     vd->var_name = JS_DupAtom(ctx, name);
  22137     vd->func_pool_idx = -1;
  22138     return fd->var_count - 1;
  22139 }
  22140 
  22141 static int add_scope_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name,
  22142                          JSVarKindEnum var_kind)
  22143 {
  22144     int idx = add_var(ctx, fd, name);
  22145     if (idx >= 0) {
  22146         JSVarDef *vd = &fd->vars[idx];
  22147         vd->var_kind = var_kind;
  22148         vd->scope_level = fd->scope_level;
  22149         vd->scope_next = fd->scope_first;
  22150         fd->scopes[fd->scope_level].first = idx;
  22151         fd->scope_first = idx;
  22152     }
  22153     return idx;
  22154 }
  22155 
  22156 static int add_func_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
  22157 {
  22158     int idx = fd->func_var_idx;
  22159     if (idx < 0 && (idx = add_var(ctx, fd, name)) >= 0) {
  22160         fd->func_var_idx = idx;
  22161         fd->vars[idx].var_kind = JS_VAR_FUNCTION_NAME;
  22162         if (fd->js_mode & JS_MODE_STRICT)
  22163             fd->vars[idx].is_const = TRUE;
  22164     }
  22165     return idx;
  22166 }
  22167 
  22168 static int add_arguments_var(JSContext *ctx, JSFunctionDef *fd)
  22169 {
  22170     int idx = fd->arguments_var_idx;
  22171     if (idx < 0 && (idx = add_var(ctx, fd, JS_ATOM_arguments)) >= 0) {
  22172         fd->arguments_var_idx = idx;
  22173     }
  22174     return idx;
  22175 }
  22176 
  22177 /* add an argument definition in the argument scope. Only needed when
  22178    "eval()" may be called in the argument scope. Return 0 if OK. */
  22179 static int add_arguments_arg(JSContext *ctx, JSFunctionDef *fd)
  22180 {
  22181     int idx;
  22182     if (fd->arguments_arg_idx < 0) {
  22183         idx = find_var_in_scope(ctx, fd, JS_ATOM_arguments, ARG_SCOPE_INDEX);
  22184         if (idx < 0) {
  22185             /* XXX: the scope links are not fully updated. May be an
  22186                issue if there are child scopes of the argument
  22187                scope */
  22188             idx = add_var(ctx, fd, JS_ATOM_arguments);
  22189             if (idx < 0)
  22190                 return -1;
  22191             fd->vars[idx].scope_next = fd->scopes[ARG_SCOPE_INDEX].first;
  22192             fd->scopes[ARG_SCOPE_INDEX].first = idx;
  22193             fd->vars[idx].scope_level = ARG_SCOPE_INDEX;
  22194             fd->vars[idx].is_lexical = TRUE;
  22195 
  22196             fd->arguments_arg_idx = idx;
  22197         }
  22198     }
  22199     return 0;
  22200 }
  22201 
  22202 static int add_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
  22203 {
  22204     JSVarDef *vd;
  22205 
  22206     /* the local variable indexes are currently stored on 16 bits */
  22207     if (fd->arg_count >= JS_MAX_LOCAL_VARS) {
  22208         JS_ThrowInternalError(ctx, "too many arguments");
  22209         return -1;
  22210     }
  22211     if (js_resize_array(ctx, (void **)&fd->args, sizeof(fd->args[0]),
  22212                         &fd->arg_size, fd->arg_count + 1))
  22213         return -1;
  22214     vd = &fd->args[fd->arg_count++];
  22215     memset(vd, 0, sizeof(*vd));
  22216     vd->var_name = JS_DupAtom(ctx, name);
  22217     vd->func_pool_idx = -1;
  22218     return fd->arg_count - 1;
  22219 }
  22220 
  22221 /* add a global variable definition */
  22222 static JSGlobalVar *add_global_var(JSContext *ctx, JSFunctionDef *s,
  22223                                      JSAtom name)
  22224 {
  22225     JSGlobalVar *hf;
  22226 
  22227     if (js_resize_array(ctx, (void **)&s->global_vars,
  22228                         sizeof(s->global_vars[0]),
  22229                         &s->global_var_size, s->global_var_count + 1))
  22230         return NULL;
  22231     hf = &s->global_vars[s->global_var_count++];
  22232     hf->cpool_idx = -1;
  22233     hf->force_init = FALSE;
  22234     hf->is_lexical = FALSE;
  22235     hf->is_const = FALSE;
  22236     hf->scope_level = s->scope_level;
  22237     hf->var_name = JS_DupAtom(ctx, name);
  22238     return hf;
  22239 }
  22240 
  22241 typedef enum {
  22242     JS_VAR_DEF_WITH,
  22243     JS_VAR_DEF_LET,
  22244     JS_VAR_DEF_CONST,
  22245     JS_VAR_DEF_FUNCTION_DECL, /* function declaration */
  22246     JS_VAR_DEF_NEW_FUNCTION_DECL, /* async/generator function declaration */
  22247     JS_VAR_DEF_CATCH,
  22248     JS_VAR_DEF_VAR,
  22249 } JSVarDefEnum;
  22250 
  22251 static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name,
  22252                       JSVarDefEnum var_def_type)
  22253 {
  22254     JSContext *ctx = s->ctx;
  22255     JSVarDef *vd;
  22256     int idx;
  22257 
  22258     switch (var_def_type) {
  22259     case JS_VAR_DEF_WITH:
  22260         idx = add_scope_var(ctx, fd, name, JS_VAR_NORMAL);
  22261         break;
  22262 
  22263     case JS_VAR_DEF_LET:
  22264     case JS_VAR_DEF_CONST:
  22265     case JS_VAR_DEF_FUNCTION_DECL:
  22266     case JS_VAR_DEF_NEW_FUNCTION_DECL:
  22267         idx = find_lexical_decl(ctx, fd, name, fd->scope_first, TRUE);
  22268         if (idx >= 0) {
  22269             if (idx < GLOBAL_VAR_OFFSET) {
  22270                 if (fd->vars[idx].scope_level == fd->scope_level) {
  22271                     /* same scope: in non strict mode, functions
  22272                        can be redefined (annex B.3.3.4). */
  22273                     if (!(!(fd->js_mode & JS_MODE_STRICT) &&
  22274                           var_def_type == JS_VAR_DEF_FUNCTION_DECL &&
  22275                           fd->vars[idx].var_kind == JS_VAR_FUNCTION_DECL)) {
  22276                         goto redef_lex_error;
  22277                     }
  22278                 } else if (fd->vars[idx].var_kind == JS_VAR_CATCH && (fd->vars[idx].scope_level + 2) == fd->scope_level) {
  22279                     goto redef_lex_error;
  22280                 }
  22281             } else {
  22282                 if (fd->scope_level == fd->body_scope) {
  22283                 redef_lex_error:
  22284                     /* redefining a scoped var in the same scope: error */
  22285                     return js_parse_error(s, "invalid redefinition of lexical identifier");
  22286                 }
  22287             }
  22288         }
  22289         if (var_def_type != JS_VAR_DEF_FUNCTION_DECL &&
  22290             var_def_type != JS_VAR_DEF_NEW_FUNCTION_DECL &&
  22291             fd->scope_level == fd->body_scope &&
  22292             find_arg(ctx, fd, name) >= 0) {
  22293             /* lexical variable redefines a parameter name */
  22294             return js_parse_error(s, "invalid redefinition of parameter name");
  22295         }
  22296 
  22297         if (find_var_in_child_scope(ctx, fd, name, fd->scope_level) >= 0) {
  22298             return js_parse_error(s, "invalid redefinition of a variable");
  22299         }
  22300 
  22301         if (fd->is_global_var) {
  22302             JSGlobalVar *hf;
  22303             hf = find_global_var(fd, name);
  22304             if (hf && is_child_scope(ctx, fd, hf->scope_level,
  22305                                      fd->scope_level)) {
  22306                 return js_parse_error(s, "invalid redefinition of global identifier");
  22307             }
  22308         }
  22309 
  22310         if (fd->is_eval &&
  22311             (fd->eval_type == JS_EVAL_TYPE_GLOBAL ||
  22312              fd->eval_type == JS_EVAL_TYPE_MODULE) &&
  22313             fd->scope_level == fd->body_scope) {
  22314             JSGlobalVar *hf;
  22315             hf = add_global_var(s->ctx, fd, name);
  22316             if (!hf)
  22317                 return -1;
  22318             hf->is_lexical = TRUE;
  22319             hf->is_const = (var_def_type == JS_VAR_DEF_CONST);
  22320             idx = GLOBAL_VAR_OFFSET;
  22321         } else {
  22322             JSVarKindEnum var_kind;
  22323             if (var_def_type == JS_VAR_DEF_FUNCTION_DECL)
  22324                 var_kind = JS_VAR_FUNCTION_DECL;
  22325             else if (var_def_type == JS_VAR_DEF_NEW_FUNCTION_DECL)
  22326                 var_kind = JS_VAR_NEW_FUNCTION_DECL;
  22327             else
  22328                 var_kind = JS_VAR_NORMAL;
  22329             idx = add_scope_var(ctx, fd, name, var_kind);
  22330             if (idx >= 0) {
  22331                 vd = &fd->vars[idx];
  22332                 vd->is_lexical = 1;
  22333                 vd->is_const = (var_def_type == JS_VAR_DEF_CONST);
  22334             }
  22335         }
  22336         break;
  22337 
  22338     case JS_VAR_DEF_CATCH:
  22339         idx = add_scope_var(ctx, fd, name, JS_VAR_CATCH);
  22340         break;
  22341 
  22342     case JS_VAR_DEF_VAR:
  22343         if (find_lexical_decl(ctx, fd, name, fd->scope_first,
  22344                               FALSE) >= 0) {
  22345        invalid_lexical_redefinition:
  22346             /* error to redefine a var that inside a lexical scope */
  22347             return js_parse_error(s, "invalid redefinition of lexical identifier");
  22348         }
  22349         if (fd->is_global_var) {
  22350             JSGlobalVar *hf;
  22351             hf = find_global_var(fd, name);
  22352             if (hf && hf->is_lexical && hf->scope_level == fd->scope_level &&
  22353                 fd->eval_type == JS_EVAL_TYPE_MODULE) {
  22354                 goto invalid_lexical_redefinition;
  22355             }
  22356             hf = add_global_var(s->ctx, fd, name);
  22357             if (!hf)
  22358                 return -1;
  22359             idx = GLOBAL_VAR_OFFSET;
  22360         } else {
  22361             /* if the variable already exists, don't add it again  */
  22362             idx = find_var(ctx, fd, name);
  22363             if (idx >= 0)
  22364                 break;
  22365             idx = add_var(ctx, fd, name);
  22366             if (idx >= 0) {
  22367                 if (name == JS_ATOM_arguments && fd->has_arguments_binding)
  22368                     fd->arguments_var_idx = idx;
  22369                 fd->vars[idx].scope_next = fd->scope_level;
  22370             }
  22371         }
  22372         break;
  22373     default:
  22374         abort();
  22375     }
  22376     return idx;
  22377 }
  22378 
  22379 /* add a private field variable in the current scope */
  22380 static int add_private_class_field(JSParseState *s, JSFunctionDef *fd,
  22381                                    JSAtom name, JSVarKindEnum var_kind, BOOL is_static)
  22382 {
  22383     JSContext *ctx = s->ctx;
  22384     JSVarDef *vd;
  22385     int idx;
  22386 
  22387     idx = add_scope_var(ctx, fd, name, var_kind);
  22388     if (idx < 0)
  22389         return idx;
  22390     vd = &fd->vars[idx];
  22391     vd->is_lexical = 1;
  22392     vd->is_const = 1;
  22393     vd->is_static_private = is_static;
  22394     return idx;
  22395 }
  22396 
  22397 static __exception int js_parse_expr(JSParseState *s);
  22398 static __exception int js_parse_function_decl(JSParseState *s,
  22399                                               JSParseFunctionEnum func_type,
  22400                                               JSFunctionKindEnum func_kind,
  22401                                               JSAtom func_name, const uint8_t *ptr,
  22402                                               int start_line);
  22403 static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s);
  22404 static __exception int js_parse_function_decl2(JSParseState *s,
  22405                                                JSParseFunctionEnum func_type,
  22406                                                JSFunctionKindEnum func_kind,
  22407                                                JSAtom func_name,
  22408                                                const uint8_t *ptr,
  22409                                                int function_line_num,
  22410                                                JSParseExportEnum export_flag,
  22411                                                JSFunctionDef **pfd);
  22412 static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags);
  22413 static __exception int js_parse_assign_expr(JSParseState *s);
  22414 static __exception int js_parse_unary(JSParseState *s, int parse_flags);
  22415 static void push_break_entry(JSFunctionDef *fd, BlockEnv *be,
  22416                              JSAtom label_name,
  22417                              int label_break, int label_cont,
  22418                              int drop_count);
  22419 static void pop_break_entry(JSFunctionDef *fd);
  22420 static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m,
  22421                                        JSAtom local_name, JSAtom export_name,
  22422                                        JSExportTypeEnum export_type);
  22423 
  22424 /* Note: all the fields are already sealed except length */
  22425 static int seal_template_obj(JSContext *ctx, JSValueConst obj)
  22426 {
  22427     JSObject *p;
  22428     JSShapeProperty *prs;
  22429 
  22430     p = JS_VALUE_GET_OBJ(obj);
  22431     prs = find_own_property1(p, JS_ATOM_length);
  22432     if (prs) {
  22433         if (js_update_property_flags(ctx, p, &prs,
  22434                                      prs->flags & ~(JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)))
  22435             return -1;
  22436     }
  22437     p->extensible = FALSE;
  22438     return 0;
  22439 }
  22440 
  22441 static __exception int js_parse_template(JSParseState *s, int call, int *argc)
  22442 {
  22443     JSContext *ctx = s->ctx;
  22444     JSValue raw_array, template_object;
  22445     JSToken cooked;
  22446     int depth, ret;
  22447 
  22448     raw_array = JS_UNDEFINED; /* avoid warning */
  22449     template_object = JS_UNDEFINED; /* avoid warning */
  22450     if (call) {
  22451         /* Create a template object: an array of cooked strings */
  22452         /* Create an array of raw strings and store it to the raw property */
  22453         template_object = JS_NewArray(ctx);
  22454         if (JS_IsException(template_object))
  22455             return -1;
  22456         //        pool_idx = s->cur_func->cpool_count;
  22457         ret = emit_push_const(s, template_object, 0);
  22458         JS_FreeValue(ctx, template_object);
  22459         if (ret)
  22460             return -1;
  22461         raw_array = JS_NewArray(ctx);
  22462         if (JS_IsException(raw_array))
  22463             return -1;
  22464         if (JS_DefinePropertyValue(ctx, template_object, JS_ATOM_raw,
  22465                                    raw_array, JS_PROP_THROW) < 0) {
  22466             return -1;
  22467         }
  22468     }
  22469 
  22470     depth = 0;
  22471     while (s->token.val == TOK_TEMPLATE) {
  22472         const uint8_t *p = s->token.ptr + 1;
  22473         cooked = s->token;
  22474         if (call) {
  22475             if (JS_DefinePropertyValueUint32(ctx, raw_array, depth,
  22476                                              JS_DupValue(ctx, s->token.u.str.str),
  22477                                              JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) {
  22478                 return -1;
  22479             }
  22480             /* re-parse the string with escape sequences but do not throw a
  22481                syntax error if it contains invalid sequences
  22482              */
  22483             if (js_parse_string(s, '`', FALSE, p, &cooked, &p)) {
  22484                 cooked.u.str.str = JS_UNDEFINED;
  22485             }
  22486             if (JS_DefinePropertyValueUint32(ctx, template_object, depth,
  22487                                              cooked.u.str.str,
  22488                                              JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) {
  22489                 return -1;
  22490             }
  22491         } else {
  22492             JSString *str;
  22493             /* re-parse the string with escape sequences and throw a
  22494                syntax error if it contains invalid sequences
  22495              */
  22496             JS_FreeValue(ctx, s->token.u.str.str);
  22497             s->token.u.str.str = JS_UNDEFINED;
  22498             if (js_parse_string(s, '`', TRUE, p, &cooked, &p))
  22499                 return -1;
  22500             str = JS_VALUE_GET_STRING(cooked.u.str.str);
  22501             if (str->len != 0 || depth == 0) {
  22502                 ret = emit_push_const(s, cooked.u.str.str, 1);
  22503                 JS_FreeValue(s->ctx, cooked.u.str.str);
  22504                 if (ret)
  22505                     return -1;
  22506                 if (depth == 0) {
  22507                     if (s->token.u.str.sep == '`')
  22508                         goto done1;
  22509                     emit_op(s, OP_get_field2);
  22510                     emit_atom(s, JS_ATOM_concat);
  22511                 }
  22512                 depth++;
  22513             } else {
  22514                 JS_FreeValue(s->ctx, cooked.u.str.str);
  22515             }
  22516         }
  22517         if (s->token.u.str.sep == '`')
  22518             goto done;
  22519         if (next_token(s))
  22520             return -1;
  22521         if (js_parse_expr(s))
  22522             return -1;
  22523         depth++;
  22524         if (s->token.val != '}') {
  22525             return js_parse_error(s, "expected '}' after template expression");
  22526         }
  22527         /* XXX: should convert to string at this stage? */
  22528         free_token(s, &s->token);
  22529         /* Resume TOK_TEMPLATE parsing (s->token.line_num and
  22530          * s->token.ptr are OK) */
  22531         s->got_lf = FALSE;
  22532         s->last_line_num = s->token.line_num;
  22533         if (js_parse_template_part(s, s->buf_ptr))
  22534             return -1;
  22535     }
  22536     return js_parse_expect(s, TOK_TEMPLATE);
  22537 
  22538  done:
  22539     if (call) {
  22540         /* Seal the objects */
  22541         seal_template_obj(ctx, raw_array);
  22542         seal_template_obj(ctx, template_object);
  22543         *argc = depth + 1;
  22544     } else {
  22545         emit_op(s, OP_call_method);
  22546         emit_u16(s, depth - 1);
  22547     }
  22548  done1:
  22549     return next_token(s);
  22550 }
  22551 
  22552 
  22553 #define PROP_TYPE_IDENT 0
  22554 #define PROP_TYPE_VAR   1
  22555 #define PROP_TYPE_GET   2
  22556 #define PROP_TYPE_SET   3
  22557 #define PROP_TYPE_STAR  4
  22558 #define PROP_TYPE_ASYNC 5
  22559 #define PROP_TYPE_ASYNC_STAR 6
  22560 
  22561 #define PROP_TYPE_PRIVATE (1 << 4)
  22562 
  22563 static BOOL token_is_ident(int tok)
  22564 {
  22565     /* Accept keywords and reserved words as property names */
  22566     return (tok == TOK_IDENT ||
  22567             (tok >= TOK_FIRST_KEYWORD &&
  22568              tok <= TOK_LAST_KEYWORD));
  22569 }
  22570 
  22571 /* if the property is an expression, name = JS_ATOM_NULL */
  22572 static int __exception js_parse_property_name(JSParseState *s,
  22573                                               JSAtom *pname,
  22574                                               BOOL allow_method, BOOL allow_var,
  22575                                               BOOL allow_private)
  22576 {
  22577     int is_private = 0;
  22578     BOOL is_non_reserved_ident;
  22579     JSAtom name;
  22580     int prop_type;
  22581 
  22582     prop_type = PROP_TYPE_IDENT;
  22583     if (allow_method) {
  22584         if (token_is_pseudo_keyword(s, JS_ATOM_get)
  22585         ||  token_is_pseudo_keyword(s, JS_ATOM_set)) {
  22586             /* get x(), set x() */
  22587             name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
  22588             if (next_token(s))
  22589                 goto fail1;
  22590             if (s->token.val == ':' || s->token.val == ',' ||
  22591                 s->token.val == '}' || s->token.val == '(' ||
  22592                 s->token.val == '=') {
  22593                 is_non_reserved_ident = TRUE;
  22594                 goto ident_found;
  22595             }
  22596             prop_type = PROP_TYPE_GET + (name == JS_ATOM_set);
  22597             JS_FreeAtom(s->ctx, name);
  22598         } else if (s->token.val == '*') {
  22599             if (next_token(s))
  22600                 goto fail;
  22601             prop_type = PROP_TYPE_STAR;
  22602         } else if (token_is_pseudo_keyword(s, JS_ATOM_async) &&
  22603                    peek_token(s, TRUE) != '\n') {
  22604             name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
  22605             if (next_token(s))
  22606                 goto fail1;
  22607             if (s->token.val == ':' || s->token.val == ',' ||
  22608                 s->token.val == '}' || s->token.val == '(' ||
  22609                 s->token.val == '=') {
  22610                 is_non_reserved_ident = TRUE;
  22611                 goto ident_found;
  22612             }
  22613             JS_FreeAtom(s->ctx, name);
  22614             if (s->token.val == '*') {
  22615                 if (next_token(s))
  22616                     goto fail;
  22617                 prop_type = PROP_TYPE_ASYNC_STAR;
  22618             } else {
  22619                 prop_type = PROP_TYPE_ASYNC;
  22620             }
  22621         }
  22622     }
  22623 
  22624     if (token_is_ident(s->token.val)) {
  22625         /* variable can only be a non-reserved identifier */
  22626         is_non_reserved_ident =
  22627             (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved);
  22628         /* keywords and reserved words have a valid atom */
  22629         name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
  22630         if (next_token(s))
  22631             goto fail1;
  22632     ident_found:
  22633         if (is_non_reserved_ident &&
  22634             prop_type == PROP_TYPE_IDENT && allow_var) {
  22635             if (!(s->token.val == ':' ||
  22636                   (s->token.val == '(' && allow_method))) {
  22637                 prop_type = PROP_TYPE_VAR;
  22638             }
  22639         }
  22640     } else if (s->token.val == TOK_STRING) {
  22641         name = JS_ValueToAtom(s->ctx, s->token.u.str.str);
  22642         if (name == JS_ATOM_NULL)
  22643             goto fail;
  22644         if (next_token(s))
  22645             goto fail1;
  22646     } else if (s->token.val == TOK_NUMBER) {
  22647         JSValue val;
  22648         val = s->token.u.num.val;
  22649 #ifdef CONFIG_BIGNUM
  22650         if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {
  22651             JSBigFloat *p = JS_VALUE_GET_PTR(val);
  22652             val = s->ctx->rt->bigfloat_ops.
  22653                 mul_pow10_to_float64(s->ctx, &p->num,
  22654                                      s->token.u.num.exponent);
  22655             if (JS_IsException(val))
  22656                 goto fail;
  22657             name = JS_ValueToAtom(s->ctx, val);
  22658             JS_FreeValue(s->ctx, val);
  22659         } else
  22660 #endif
  22661         {
  22662             name = JS_ValueToAtom(s->ctx, val);
  22663         }
  22664         if (name == JS_ATOM_NULL)
  22665             goto fail;
  22666         if (next_token(s))
  22667             goto fail1;
  22668     } else if (s->token.val == '[') {
  22669         if (next_token(s))
  22670             goto fail;
  22671         if (js_parse_expr(s))
  22672             goto fail;
  22673         if (js_parse_expect(s, ']'))
  22674             goto fail;
  22675         name = JS_ATOM_NULL;
  22676     } else if (s->token.val == TOK_PRIVATE_NAME && allow_private) {
  22677         name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
  22678         if (next_token(s))
  22679             goto fail1;
  22680         is_private = PROP_TYPE_PRIVATE;
  22681     } else {
  22682         goto invalid_prop;
  22683     }
  22684     if (prop_type != PROP_TYPE_IDENT && prop_type != PROP_TYPE_VAR &&
  22685         s->token.val != '(') {
  22686         JS_FreeAtom(s->ctx, name);
  22687     invalid_prop:
  22688         js_parse_error(s, "invalid property name");
  22689         goto fail;
  22690     }
  22691     *pname = name;
  22692     return prop_type | is_private;
  22693  fail1:
  22694     JS_FreeAtom(s->ctx, name);
  22695  fail:
  22696     *pname = JS_ATOM_NULL;
  22697     return -1;
  22698 }
  22699 
  22700 typedef struct JSParsePos {
  22701     int last_line_num;
  22702     int line_num;
  22703     BOOL got_lf;
  22704     const uint8_t *ptr;
  22705 } JSParsePos;
  22706 
  22707 static int js_parse_get_pos(JSParseState *s, JSParsePos *sp)
  22708 {
  22709     sp->last_line_num = s->last_line_num;
  22710     sp->line_num = s->token.line_num;
  22711     sp->ptr = s->token.ptr;
  22712     sp->got_lf = s->got_lf;
  22713     return 0;
  22714 }
  22715 
  22716 static __exception int js_parse_seek_token(JSParseState *s, const JSParsePos *sp)
  22717 {
  22718     s->token.line_num = sp->last_line_num;
  22719     s->line_num = sp->line_num;
  22720     s->buf_ptr = sp->ptr;
  22721     s->got_lf = sp->got_lf;
  22722     return next_token(s);
  22723 }
  22724 
  22725 /* return TRUE if a regexp literal is allowed after this token */
  22726 static BOOL is_regexp_allowed(int tok)
  22727 {
  22728     switch (tok) {
  22729     case TOK_NUMBER:
  22730     case TOK_STRING:
  22731     case TOK_REGEXP:
  22732     case TOK_DEC:
  22733     case TOK_INC:
  22734     case TOK_NULL:
  22735     case TOK_FALSE:
  22736     case TOK_TRUE:
  22737     case TOK_THIS:
  22738     case ')':
  22739     case ']':
  22740     case '}': /* XXX: regexp may occur after */
  22741     case TOK_IDENT:
  22742         return FALSE;
  22743     default:
  22744         return TRUE;
  22745     }
  22746 }
  22747 
  22748 #define SKIP_HAS_SEMI       (1 << 0)
  22749 #define SKIP_HAS_ELLIPSIS   (1 << 1)
  22750 #define SKIP_HAS_ASSIGNMENT (1 << 2)
  22751 
  22752 /* XXX: improve speed with early bailout */
  22753 /* XXX: no longer works if regexps are present. Could use previous
  22754    regexp parsing heuristics to handle most cases */
  22755 static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_terminator)
  22756 {
  22757     char state[256];
  22758     size_t level = 0;
  22759     JSParsePos pos;
  22760     int last_tok, tok = TOK_EOF;
  22761     int c, tok_len, bits = 0;
  22762 
  22763     /* protect from underflow */
  22764     state[level++] = 0;
  22765 
  22766     js_parse_get_pos(s, &pos);
  22767     last_tok = 0;
  22768     for (;;) {
  22769         switch(s->token.val) {
  22770         case '(':
  22771         case '[':
  22772         case '{':
  22773             if (level >= sizeof(state))
  22774                 goto done;
  22775             state[level++] = s->token.val;
  22776             break;
  22777         case ')':
  22778             if (state[--level] != '(')
  22779                 goto done;
  22780             break;
  22781         case ']':
  22782             if (state[--level] != '[')
  22783                 goto done;
  22784             break;
  22785         case '}':
  22786             c = state[--level];
  22787             if (c == '`') {
  22788                 /* continue the parsing of the template */
  22789                 free_token(s, &s->token);
  22790                 /* Resume TOK_TEMPLATE parsing (s->token.line_num and
  22791                  * s->token.ptr are OK) */
  22792                 s->got_lf = FALSE;
  22793                 s->last_line_num = s->token.line_num;
  22794                 if (js_parse_template_part(s, s->buf_ptr))
  22795                     goto done;
  22796                 goto handle_template;
  22797             } else if (c != '{') {
  22798                 goto done;
  22799             }
  22800             break;
  22801         case TOK_TEMPLATE:
  22802         handle_template:
  22803             if (s->token.u.str.sep != '`') {
  22804                 /* '${' inside the template : closing '}' and continue
  22805                    parsing the template */
  22806                 if (level >= sizeof(state))
  22807                     goto done;
  22808                 state[level++] = '`';
  22809             }
  22810             break;
  22811         case TOK_EOF:
  22812             goto done;
  22813         case ';':
  22814             if (level == 2) {
  22815                 bits |= SKIP_HAS_SEMI;
  22816             }
  22817             break;
  22818         case TOK_ELLIPSIS:
  22819             if (level == 2) {
  22820                 bits |= SKIP_HAS_ELLIPSIS;
  22821             }
  22822             break;
  22823         case '=':
  22824             bits |= SKIP_HAS_ASSIGNMENT;
  22825             break;
  22826 
  22827         case TOK_DIV_ASSIGN:
  22828             tok_len = 2;
  22829             goto parse_regexp;
  22830         case '/':
  22831             tok_len = 1;
  22832         parse_regexp:
  22833             if (is_regexp_allowed(last_tok)) {
  22834                 s->buf_ptr -= tok_len;
  22835                 if (js_parse_regexp(s)) {
  22836                     /* XXX: should clear the exception */
  22837                     goto done;
  22838                 }
  22839             }
  22840             break;
  22841         }
  22842         /* last_tok is only used to recognize regexps */
  22843         if (s->token.val == TOK_IDENT &&
  22844             (token_is_pseudo_keyword(s, JS_ATOM_of) ||
  22845              token_is_pseudo_keyword(s, JS_ATOM_yield))) {
  22846             last_tok = TOK_OF;
  22847         } else {
  22848             last_tok = s->token.val;
  22849         }
  22850         if (next_token(s)) {
  22851             /* XXX: should clear the exception generated by next_token() */
  22852             break;
  22853         }
  22854         if (level <= 1) {
  22855             tok = s->token.val;
  22856             if (token_is_pseudo_keyword(s, JS_ATOM_of))
  22857                 tok = TOK_OF;
  22858             if (no_line_terminator && s->last_line_num != s->token.line_num)
  22859                 tok = '\n';
  22860             break;
  22861         }
  22862     }
  22863  done:
  22864     if (pbits) {
  22865         *pbits = bits;
  22866     }
  22867     if (js_parse_seek_token(s, &pos))
  22868         return -1;
  22869     return tok;
  22870 }
  22871 
  22872 static void set_object_name(JSParseState *s, JSAtom name)
  22873 {
  22874     JSFunctionDef *fd = s->cur_func;
  22875     int opcode;
  22876 
  22877     opcode = get_prev_opcode(fd);
  22878     if (opcode == OP_set_name) {
  22879         /* XXX: should free atom after OP_set_name? */
  22880         fd->byte_code.size = fd->last_opcode_pos;
  22881         fd->last_opcode_pos = -1;
  22882         emit_op(s, OP_set_name);
  22883         emit_atom(s, name);
  22884     } else if (opcode == OP_set_class_name) {
  22885         int define_class_pos;
  22886         JSAtom atom;
  22887         define_class_pos = fd->last_opcode_pos + 1 -
  22888             get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
  22889         assert(fd->byte_code.buf[define_class_pos] == OP_define_class);
  22890         /* for consistency we free the previous atom which is
  22891            JS_ATOM_empty_string */
  22892         atom = get_u32(fd->byte_code.buf + define_class_pos + 1);
  22893         JS_FreeAtom(s->ctx, atom);
  22894         put_u32(fd->byte_code.buf + define_class_pos + 1,
  22895                 JS_DupAtom(s->ctx, name));
  22896         fd->last_opcode_pos = -1;
  22897     }
  22898 }
  22899 
  22900 static void set_object_name_computed(JSParseState *s)
  22901 {
  22902     JSFunctionDef *fd = s->cur_func;
  22903     int opcode;
  22904 
  22905     opcode = get_prev_opcode(fd);
  22906     if (opcode == OP_set_name) {
  22907         /* XXX: should free atom after OP_set_name? */
  22908         fd->byte_code.size = fd->last_opcode_pos;
  22909         fd->last_opcode_pos = -1;
  22910         emit_op(s, OP_set_name_computed);
  22911     } else if (opcode == OP_set_class_name) {
  22912         int define_class_pos;
  22913         define_class_pos = fd->last_opcode_pos + 1 -
  22914             get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
  22915         assert(fd->byte_code.buf[define_class_pos] == OP_define_class);
  22916         fd->byte_code.buf[define_class_pos] = OP_define_class_computed;
  22917         fd->last_opcode_pos = -1;
  22918     }
  22919 }
  22920 
  22921 static __exception int js_parse_object_literal(JSParseState *s)
  22922 {
  22923     JSAtom name = JS_ATOM_NULL;
  22924     const uint8_t *start_ptr;
  22925     int start_line, prop_type;
  22926     BOOL has_proto;
  22927 
  22928     if (next_token(s))
  22929         goto fail;
  22930     /* XXX: add an initial length that will be patched back */
  22931     emit_op(s, OP_object);
  22932     has_proto = FALSE;
  22933     while (s->token.val != '}') {
  22934         /* specific case for getter/setter */
  22935         start_ptr = s->token.ptr;
  22936         start_line = s->token.line_num;
  22937 
  22938         if (s->token.val == TOK_ELLIPSIS) {
  22939             if (next_token(s))
  22940                 return -1;
  22941             if (js_parse_assign_expr(s))
  22942                 return -1;
  22943             emit_op(s, OP_null);  /* dummy excludeList */
  22944             emit_op(s, OP_copy_data_properties);
  22945             emit_u8(s, 2 | (1 << 2) | (0 << 5));
  22946             emit_op(s, OP_drop); /* pop excludeList */
  22947             emit_op(s, OP_drop); /* pop src object */
  22948             goto next;
  22949         }
  22950 
  22951         prop_type = js_parse_property_name(s, &name, TRUE, TRUE, FALSE);
  22952         if (prop_type < 0)
  22953             goto fail;
  22954 
  22955         if (prop_type == PROP_TYPE_VAR) {
  22956             /* shortcut for x: x */
  22957             emit_op(s, OP_scope_get_var);
  22958             emit_atom(s, name);
  22959             emit_u16(s, s->cur_func->scope_level);
  22960             emit_op(s, OP_define_field);
  22961             emit_atom(s, name);
  22962         } else if (s->token.val == '(') {
  22963             BOOL is_getset = (prop_type == PROP_TYPE_GET ||
  22964                               prop_type == PROP_TYPE_SET);
  22965             JSParseFunctionEnum func_type;
  22966             JSFunctionKindEnum func_kind;
  22967             int op_flags;
  22968 
  22969             func_kind = JS_FUNC_NORMAL;
  22970             if (is_getset) {
  22971                 func_type = JS_PARSE_FUNC_GETTER + prop_type - PROP_TYPE_GET;
  22972             } else {
  22973                 func_type = JS_PARSE_FUNC_METHOD;
  22974                 if (prop_type == PROP_TYPE_STAR)
  22975                     func_kind = JS_FUNC_GENERATOR;
  22976                 else if (prop_type == PROP_TYPE_ASYNC)
  22977                     func_kind = JS_FUNC_ASYNC;
  22978                 else if (prop_type == PROP_TYPE_ASYNC_STAR)
  22979                     func_kind = JS_FUNC_ASYNC_GENERATOR;
  22980             }
  22981             if (js_parse_function_decl(s, func_type, func_kind, JS_ATOM_NULL,
  22982                                        start_ptr, start_line))
  22983                 goto fail;
  22984             if (name == JS_ATOM_NULL) {
  22985                 emit_op(s, OP_define_method_computed);
  22986             } else {
  22987                 emit_op(s, OP_define_method);
  22988                 emit_atom(s, name);
  22989             }
  22990             if (is_getset) {
  22991                 op_flags = OP_DEFINE_METHOD_GETTER +
  22992                     prop_type - PROP_TYPE_GET;
  22993             } else {
  22994                 op_flags = OP_DEFINE_METHOD_METHOD;
  22995             }
  22996             emit_u8(s, op_flags | OP_DEFINE_METHOD_ENUMERABLE);
  22997         } else {
  22998             if (js_parse_expect(s, ':'))
  22999                 goto fail;
  23000             if (js_parse_assign_expr(s))
  23001                 goto fail;
  23002             if (name == JS_ATOM_NULL) {
  23003                 set_object_name_computed(s);
  23004                 emit_op(s, OP_define_array_el);
  23005                 emit_op(s, OP_drop);
  23006             } else if (name == JS_ATOM___proto__) {
  23007                 if (has_proto) {
  23008                     js_parse_error(s, "duplicate __proto__ property name");
  23009                     goto fail;
  23010                 }
  23011                 emit_op(s, OP_set_proto);
  23012                 has_proto = TRUE;
  23013             } else {
  23014                 set_object_name(s, name);
  23015                 emit_op(s, OP_define_field);
  23016                 emit_atom(s, name);
  23017             }
  23018         }
  23019         JS_FreeAtom(s->ctx, name);
  23020     next:
  23021         name = JS_ATOM_NULL;
  23022         if (s->token.val != ',')
  23023             break;
  23024         if (next_token(s))
  23025             goto fail;
  23026     }
  23027     if (js_parse_expect(s, '}'))
  23028         goto fail;
  23029     return 0;
  23030  fail:
  23031     JS_FreeAtom(s->ctx, name);
  23032     return -1;
  23033 }
  23034 
  23035 /* allow the 'in' binary operator */
  23036 #define PF_IN_ACCEPTED  (1 << 0)
  23037 /* allow function calls parsing in js_parse_postfix_expr() */
  23038 #define PF_POSTFIX_CALL (1 << 1)
  23039 /* allow the exponentiation operator in js_parse_unary() */
  23040 #define PF_POW_ALLOWED  (1 << 2)
  23041 /* forbid the exponentiation operator in js_parse_unary() */
  23042 #define PF_POW_FORBIDDEN (1 << 3)
  23043 
  23044 static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags);
  23045 
  23046 static __exception int js_parse_left_hand_side_expr(JSParseState *s)
  23047 {
  23048     return js_parse_postfix_expr(s, PF_POSTFIX_CALL);
  23049 }
  23050 
  23051 /* XXX: could generate specific bytecode */
  23052 static __exception int js_parse_class_default_ctor(JSParseState *s,
  23053                                                    BOOL has_super,
  23054                                                    JSFunctionDef **pfd)
  23055 {
  23056     JSParsePos pos;
  23057     const char *str;
  23058     int ret, line_num;
  23059     JSParseFunctionEnum func_type;
  23060     const uint8_t *saved_buf_end;
  23061 
  23062     js_parse_get_pos(s, &pos);
  23063     if (has_super) {
  23064         /* spec change: no argument evaluation */
  23065         str = "(){super(...arguments);}";
  23066         func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR;
  23067     } else {
  23068         str = "(){}";
  23069         func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR;
  23070     }
  23071     line_num = s->token.line_num;
  23072     saved_buf_end = s->buf_end;
  23073     s->buf_ptr = (uint8_t *)str;
  23074     s->buf_end = (uint8_t *)(str + strlen(str));
  23075     ret = next_token(s);
  23076     if (!ret) {
  23077         ret = js_parse_function_decl2(s, func_type, JS_FUNC_NORMAL,
  23078                                       JS_ATOM_NULL, (uint8_t *)str,
  23079                                       line_num, JS_PARSE_EXPORT_NONE, pfd);
  23080     }
  23081     s->buf_end = saved_buf_end;
  23082     ret |= js_parse_seek_token(s, &pos);
  23083     return ret;
  23084 }
  23085 
  23086 /* find field in the current scope */
  23087 static int find_private_class_field(JSContext *ctx, JSFunctionDef *fd,
  23088                                     JSAtom name, int scope_level)
  23089 {
  23090     int idx;
  23091     idx = fd->scopes[scope_level].first;
  23092     while (idx != -1) {
  23093         if (fd->vars[idx].scope_level != scope_level)
  23094             break;
  23095         if (fd->vars[idx].var_name == name)
  23096             return idx;
  23097         idx = fd->vars[idx].scope_next;
  23098     }
  23099     return -1;
  23100 }
  23101 
  23102 /* initialize the class fields, called by the constructor. Note:
  23103    super() can be called in an arrow function, so <this> and
  23104    <class_fields_init> can be variable references */
  23105 static void emit_class_field_init(JSParseState *s)
  23106 {
  23107     int label_next;
  23108 
  23109     emit_op(s, OP_scope_get_var);
  23110     emit_atom(s, JS_ATOM_class_fields_init);
  23111     emit_u16(s, s->cur_func->scope_level);
  23112 
  23113     /* no need to call the class field initializer if not defined */
  23114     emit_op(s, OP_dup);
  23115     label_next = emit_goto(s, OP_if_false, -1);
  23116 
  23117     emit_op(s, OP_scope_get_var);
  23118     emit_atom(s, JS_ATOM_this);
  23119     emit_u16(s, 0);
  23120 
  23121     emit_op(s, OP_swap);
  23122 
  23123     emit_op(s, OP_call_method);
  23124     emit_u16(s, 0);
  23125 
  23126     emit_label(s, label_next);
  23127     emit_op(s, OP_drop);
  23128 }
  23129 
  23130 /* build a private setter function name from the private getter name */
  23131 static JSAtom get_private_setter_name(JSContext *ctx, JSAtom name)
  23132 {
  23133     return js_atom_concat_str(ctx, name, "<set>");
  23134 }
  23135 
  23136 typedef struct {
  23137     JSFunctionDef *fields_init_fd;
  23138     int computed_fields_count;
  23139     BOOL need_brand;
  23140     int brand_push_pos;
  23141     BOOL is_static;
  23142 } ClassFieldsDef;
  23143 
  23144 static __exception int emit_class_init_start(JSParseState *s,
  23145                                              ClassFieldsDef *cf)
  23146 {
  23147     int label_add_brand;
  23148 
  23149     cf->fields_init_fd = js_parse_function_class_fields_init(s);
  23150     if (!cf->fields_init_fd)
  23151         return -1;
  23152 
  23153     s->cur_func = cf->fields_init_fd;
  23154 
  23155     if (!cf->is_static) {
  23156         /* add the brand to the newly created instance */
  23157         /* XXX: would be better to add the code only if needed, maybe in a
  23158            later pass */
  23159         emit_op(s, OP_push_false); /* will be patched later */
  23160         cf->brand_push_pos = cf->fields_init_fd->last_opcode_pos;
  23161         label_add_brand = emit_goto(s, OP_if_false, -1);
  23162 
  23163         emit_op(s, OP_scope_get_var);
  23164         emit_atom(s, JS_ATOM_this);
  23165         emit_u16(s, 0);
  23166 
  23167         emit_op(s, OP_scope_get_var);
  23168         emit_atom(s, JS_ATOM_home_object);
  23169         emit_u16(s, 0);
  23170 
  23171         emit_op(s, OP_add_brand);
  23172 
  23173         emit_label(s, label_add_brand);
  23174     }
  23175     s->cur_func = s->cur_func->parent;
  23176     return 0;
  23177 }
  23178 
  23179 static void emit_class_init_end(JSParseState *s, ClassFieldsDef *cf)
  23180 {
  23181     int cpool_idx;
  23182 
  23183     s->cur_func = cf->fields_init_fd;
  23184     emit_op(s, OP_return_undef);
  23185     s->cur_func = s->cur_func->parent;
  23186 
  23187     cpool_idx = cpool_add(s, JS_NULL);
  23188     cf->fields_init_fd->parent_cpool_idx = cpool_idx;
  23189     emit_op(s, OP_fclosure);
  23190     emit_u32(s, cpool_idx);
  23191     emit_op(s, OP_set_home_object);
  23192 }
  23193 
  23194 
  23195 static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,
  23196                                       JSParseExportEnum export_flag)
  23197 {
  23198     JSContext *ctx = s->ctx;
  23199     JSFunctionDef *fd = s->cur_func;
  23200     JSAtom name = JS_ATOM_NULL, class_name = JS_ATOM_NULL, class_name1;
  23201     JSAtom class_var_name = JS_ATOM_NULL;
  23202     JSFunctionDef *method_fd, *ctor_fd;
  23203     int saved_js_mode, class_name_var_idx, prop_type, ctor_cpool_offset;
  23204     int class_flags = 0, i, define_class_offset;
  23205     BOOL is_static, is_private;
  23206     const uint8_t *class_start_ptr = s->token.ptr;
  23207     const uint8_t *start_ptr;
  23208     ClassFieldsDef class_fields[2];
  23209 
  23210     /* classes are parsed and executed in strict mode */
  23211     saved_js_mode = fd->js_mode;
  23212     fd->js_mode |= JS_MODE_STRICT;
  23213     if (next_token(s))
  23214         goto fail;
  23215     if (s->token.val == TOK_IDENT) {
  23216         if (s->token.u.ident.is_reserved) {
  23217             js_parse_error_reserved_identifier(s);
  23218             goto fail;
  23219         }
  23220         class_name = JS_DupAtom(ctx, s->token.u.ident.atom);
  23221         if (next_token(s))
  23222             goto fail;
  23223     } else if (!is_class_expr && export_flag != JS_PARSE_EXPORT_DEFAULT) {
  23224         js_parse_error(s, "class statement requires a name");
  23225         goto fail;
  23226     }
  23227     if (!is_class_expr) {
  23228         if (class_name == JS_ATOM_NULL)
  23229             class_var_name = JS_ATOM__default_; /* export default */
  23230         else
  23231             class_var_name = class_name;
  23232         class_var_name = JS_DupAtom(ctx, class_var_name);
  23233     }
  23234 
  23235     push_scope(s);
  23236 
  23237     if (s->token.val == TOK_EXTENDS) {
  23238         class_flags = JS_DEFINE_CLASS_HAS_HERITAGE;
  23239         if (next_token(s))
  23240             goto fail;
  23241         if (js_parse_left_hand_side_expr(s))
  23242             goto fail;
  23243     } else {
  23244         emit_op(s, OP_undefined);
  23245     }
  23246 
  23247     /* add a 'const' definition for the class name */
  23248     if (class_name != JS_ATOM_NULL) {
  23249         class_name_var_idx = define_var(s, fd, class_name, JS_VAR_DEF_CONST);
  23250         if (class_name_var_idx < 0)
  23251             goto fail;
  23252     }
  23253 
  23254     if (js_parse_expect(s, '{'))
  23255         goto fail;
  23256 
  23257     /* this scope contains the private fields */
  23258     push_scope(s);
  23259 
  23260     emit_op(s, OP_push_const);
  23261     ctor_cpool_offset = fd->byte_code.size;
  23262     emit_u32(s, 0); /* will be patched at the end of the class parsing */
  23263 
  23264     if (class_name == JS_ATOM_NULL) {
  23265         if (class_var_name != JS_ATOM_NULL)
  23266             class_name1 = JS_ATOM_default;
  23267         else
  23268             class_name1 = JS_ATOM_empty_string;
  23269     } else {
  23270         class_name1 = class_name;
  23271     }
  23272 
  23273     emit_op(s, OP_define_class);
  23274     emit_atom(s, class_name1);
  23275     emit_u8(s, class_flags);
  23276     define_class_offset = fd->last_opcode_pos;
  23277 
  23278     for(i = 0; i < 2; i++) {
  23279         ClassFieldsDef *cf = &class_fields[i];
  23280         cf->fields_init_fd = NULL;
  23281         cf->computed_fields_count = 0;
  23282         cf->need_brand = FALSE;
  23283         cf->is_static = i;
  23284     }
  23285 
  23286     ctor_fd = NULL;
  23287     while (s->token.val != '}') {
  23288         if (s->token.val == ';') {
  23289             if (next_token(s))
  23290                 goto fail;
  23291             continue;
  23292         }
  23293         is_static = FALSE;
  23294         if (s->token.val == TOK_STATIC) {
  23295             int next = peek_token(s, TRUE);
  23296             if (!(next == ';' || next == '}' || next == '(' || next == '='))
  23297                 is_static = TRUE;
  23298         }
  23299         prop_type = -1;
  23300         if (is_static) {
  23301             if (next_token(s))
  23302                 goto fail;
  23303             if (s->token.val == '{') {
  23304                 ClassFieldsDef *cf = &class_fields[is_static];
  23305                 JSFunctionDef *init;
  23306                 if (!cf->fields_init_fd) {
  23307                     if (emit_class_init_start(s, cf))
  23308                         goto fail;
  23309                 }
  23310                 s->cur_func = cf->fields_init_fd;
  23311                 /* XXX: could try to avoid creating a new function and
  23312                    reuse 'fields_init_fd' with a specific 'var'
  23313                    scope */
  23314                 // stack is now: <empty>
  23315                 if (js_parse_function_decl2(s, JS_PARSE_FUNC_CLASS_STATIC_INIT,
  23316                                             JS_FUNC_NORMAL, JS_ATOM_NULL,
  23317                                             s->token.ptr, s->token.line_num,
  23318                                             JS_PARSE_EXPORT_NONE, &init) < 0) {
  23319                     goto fail;
  23320                 }
  23321                 // stack is now: fclosure
  23322                 push_scope(s);
  23323                 emit_op(s, OP_scope_get_var);
  23324                 emit_atom(s, JS_ATOM_this);
  23325                 emit_u16(s, 0);
  23326                 // stack is now: fclosure this
  23327                 emit_op(s, OP_swap);
  23328                 // stack is now: this fclosure
  23329                 emit_op(s, OP_call_method);
  23330                 emit_u16(s, 0);
  23331                 // stack is now: returnvalue
  23332                 emit_op(s, OP_drop);
  23333                 // stack is now: <empty>
  23334                 pop_scope(s);
  23335                 s->cur_func = s->cur_func->parent;
  23336                 continue;
  23337             }
  23338             /* allow "static" field name */
  23339             if (s->token.val == ';' || s->token.val == '=') {
  23340                 is_static = FALSE;
  23341                 name = JS_DupAtom(ctx, JS_ATOM_static);
  23342                 prop_type = PROP_TYPE_IDENT;
  23343             }
  23344         }
  23345         if (is_static)
  23346             emit_op(s, OP_swap);
  23347         start_ptr = s->token.ptr;
  23348         if (prop_type < 0) {
  23349             prop_type = js_parse_property_name(s, &name, TRUE, FALSE, TRUE);
  23350             if (prop_type < 0)
  23351                 goto fail;
  23352         }
  23353         is_private = prop_type & PROP_TYPE_PRIVATE;
  23354         prop_type &= ~PROP_TYPE_PRIVATE;
  23355 
  23356         if ((name == JS_ATOM_constructor && !is_static &&
  23357              prop_type != PROP_TYPE_IDENT) ||
  23358             (name == JS_ATOM_prototype && is_static) ||
  23359             name == JS_ATOM_hash_constructor) {
  23360             js_parse_error(s, "invalid method name");
  23361             goto fail;
  23362         }
  23363         if (prop_type == PROP_TYPE_GET || prop_type == PROP_TYPE_SET) {
  23364             BOOL is_set = prop_type - PROP_TYPE_GET;
  23365             JSFunctionDef *method_fd;
  23366 
  23367             if (is_private) {
  23368                 int idx, var_kind, is_static1;
  23369                 idx = find_private_class_field(ctx, fd, name, fd->scope_level);
  23370                 if (idx >= 0) {
  23371                     var_kind = fd->vars[idx].var_kind;
  23372                     is_static1 = fd->vars[idx].is_static_private;
  23373                     if (var_kind == JS_VAR_PRIVATE_FIELD ||
  23374                         var_kind == JS_VAR_PRIVATE_METHOD ||
  23375                         var_kind == JS_VAR_PRIVATE_GETTER_SETTER ||
  23376                         var_kind == (JS_VAR_PRIVATE_GETTER + is_set) ||
  23377                         (var_kind == (JS_VAR_PRIVATE_GETTER + 1 - is_set) &&
  23378                          is_static != is_static1)) {
  23379                         goto private_field_already_defined;
  23380                     }
  23381                     fd->vars[idx].var_kind = JS_VAR_PRIVATE_GETTER_SETTER;
  23382                 } else {
  23383                     if (add_private_class_field(s, fd, name,
  23384                                                 JS_VAR_PRIVATE_GETTER + is_set, is_static) < 0)
  23385                         goto fail;
  23386                 }
  23387                 class_fields[is_static].need_brand = TRUE;
  23388             }
  23389 
  23390             if (js_parse_function_decl2(s, JS_PARSE_FUNC_GETTER + is_set,
  23391                                         JS_FUNC_NORMAL, JS_ATOM_NULL,
  23392                                         start_ptr, s->token.line_num,
  23393                                         JS_PARSE_EXPORT_NONE, &method_fd))
  23394                 goto fail;
  23395             if (is_private) {
  23396                 method_fd->need_home_object = TRUE; /* needed for brand check */
  23397                 emit_op(s, OP_set_home_object);
  23398                 /* XXX: missing function name */
  23399                 emit_op(s, OP_scope_put_var_init);
  23400                 if (is_set) {
  23401                     JSAtom setter_name;
  23402                     int ret;
  23403 
  23404                     setter_name = get_private_setter_name(ctx, name);
  23405                     if (setter_name == JS_ATOM_NULL)
  23406                         goto fail;
  23407                     emit_atom(s, setter_name);
  23408                     ret = add_private_class_field(s, fd, setter_name,
  23409                                                   JS_VAR_PRIVATE_SETTER, is_static);
  23410                     JS_FreeAtom(ctx, setter_name);
  23411                     if (ret < 0)
  23412                         goto fail;
  23413                 } else {
  23414                     emit_atom(s, name);
  23415                 }
  23416                 emit_u16(s, s->cur_func->scope_level);
  23417             } else {
  23418                 if (name == JS_ATOM_NULL) {
  23419                     emit_op(s, OP_define_method_computed);
  23420                 } else {
  23421                     emit_op(s, OP_define_method);
  23422                     emit_atom(s, name);
  23423                 }
  23424                 emit_u8(s, OP_DEFINE_METHOD_GETTER + is_set);
  23425             }
  23426         } else if (prop_type == PROP_TYPE_IDENT && s->token.val != '(') {
  23427             ClassFieldsDef *cf = &class_fields[is_static];
  23428             JSAtom field_var_name = JS_ATOM_NULL;
  23429 
  23430             /* class field */
  23431 
  23432             /* XXX: spec: not consistent with method name checks */
  23433             if (name == JS_ATOM_constructor || name == JS_ATOM_prototype) {
  23434                 js_parse_error(s, "invalid field name");
  23435                 goto fail;
  23436             }
  23437 
  23438             if (is_private) {
  23439                 if (find_private_class_field(ctx, fd, name,
  23440                                              fd->scope_level) >= 0) {
  23441                     goto private_field_already_defined;
  23442                 }
  23443                 if (add_private_class_field(s, fd, name,
  23444                                             JS_VAR_PRIVATE_FIELD, is_static) < 0)
  23445                     goto fail;
  23446                 emit_op(s, OP_private_symbol);
  23447                 emit_atom(s, name);
  23448                 emit_op(s, OP_scope_put_var_init);
  23449                 emit_atom(s, name);
  23450                 emit_u16(s, s->cur_func->scope_level);
  23451             }
  23452 
  23453             if (!cf->fields_init_fd) {
  23454                 if (emit_class_init_start(s, cf))
  23455                     goto fail;
  23456             }
  23457             if (name == JS_ATOM_NULL ) {
  23458                 /* save the computed field name into a variable */
  23459                 field_var_name = js_atom_concat_num(ctx, JS_ATOM_computed_field + is_static, cf->computed_fields_count);
  23460                 if (field_var_name == JS_ATOM_NULL)
  23461                     goto fail;
  23462                 if (define_var(s, fd, field_var_name, JS_VAR_DEF_CONST) < 0) {
  23463                     JS_FreeAtom(ctx, field_var_name);
  23464                     goto fail;
  23465                 }
  23466                 emit_op(s, OP_to_propkey);
  23467                 emit_op(s, OP_scope_put_var_init);
  23468                 emit_atom(s, field_var_name);
  23469                 emit_u16(s, s->cur_func->scope_level);
  23470             }
  23471             s->cur_func = cf->fields_init_fd;
  23472             emit_op(s, OP_scope_get_var);
  23473             emit_atom(s, JS_ATOM_this);
  23474             emit_u16(s, 0);
  23475 
  23476             if (name == JS_ATOM_NULL) {
  23477                 emit_op(s, OP_scope_get_var);
  23478                 emit_atom(s, field_var_name);
  23479                 emit_u16(s, s->cur_func->scope_level);
  23480                 cf->computed_fields_count++;
  23481                 JS_FreeAtom(ctx, field_var_name);
  23482             } else if (is_private) {
  23483                 emit_op(s, OP_scope_get_var);
  23484                 emit_atom(s, name);
  23485                 emit_u16(s, s->cur_func->scope_level);
  23486             }
  23487 
  23488             if (s->token.val == '=') {
  23489                 if (next_token(s))
  23490                     goto fail;
  23491                 if (js_parse_assign_expr(s))
  23492                     goto fail;
  23493             } else {
  23494                 emit_op(s, OP_undefined);
  23495             }
  23496             if (is_private) {
  23497                 set_object_name_computed(s);
  23498                 emit_op(s, OP_define_private_field);
  23499             } else if (name == JS_ATOM_NULL) {
  23500                 set_object_name_computed(s);
  23501                 emit_op(s, OP_define_array_el);
  23502                 emit_op(s, OP_drop);
  23503             } else {
  23504                 set_object_name(s, name);
  23505                 emit_op(s, OP_define_field);
  23506                 emit_atom(s, name);
  23507             }
  23508             s->cur_func = s->cur_func->parent;
  23509             if (js_parse_expect_semi(s))
  23510                 goto fail;
  23511         } else {
  23512             JSParseFunctionEnum func_type;
  23513             JSFunctionKindEnum func_kind;
  23514 
  23515             func_type = JS_PARSE_FUNC_METHOD;
  23516             func_kind = JS_FUNC_NORMAL;
  23517             if (prop_type == PROP_TYPE_STAR) {
  23518                 func_kind = JS_FUNC_GENERATOR;
  23519             } else if (prop_type == PROP_TYPE_ASYNC) {
  23520                 func_kind = JS_FUNC_ASYNC;
  23521             } else if (prop_type == PROP_TYPE_ASYNC_STAR) {
  23522                 func_kind = JS_FUNC_ASYNC_GENERATOR;
  23523             } else if (name == JS_ATOM_constructor && !is_static) {
  23524                 if (ctor_fd) {
  23525                     js_parse_error(s, "property constructor appears more than once");
  23526                     goto fail;
  23527                 }
  23528                 if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE)
  23529                     func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR;
  23530                 else
  23531                     func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR;
  23532             }
  23533             if (is_private) {
  23534                 class_fields[is_static].need_brand = TRUE;
  23535             }
  23536             if (js_parse_function_decl2(s, func_type, func_kind, JS_ATOM_NULL, start_ptr, s->token.line_num, JS_PARSE_EXPORT_NONE, &method_fd))
  23537                 goto fail;
  23538             if (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR ||
  23539                 func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) {
  23540                 ctor_fd = method_fd;
  23541             } else if (is_private) {
  23542                 method_fd->need_home_object = TRUE; /* needed for brand check */
  23543                 if (find_private_class_field(ctx, fd, name,
  23544                                              fd->scope_level) >= 0) {
  23545                 private_field_already_defined:
  23546                     js_parse_error(s, "private class field is already defined");
  23547                     goto fail;
  23548                 }
  23549                 if (add_private_class_field(s, fd, name,
  23550                                             JS_VAR_PRIVATE_METHOD, is_static) < 0)
  23551                     goto fail;
  23552                 emit_op(s, OP_set_home_object);
  23553                 emit_op(s, OP_set_name);
  23554                 emit_atom(s, name);
  23555                 emit_op(s, OP_scope_put_var_init);
  23556                 emit_atom(s, name);
  23557                 emit_u16(s, s->cur_func->scope_level);
  23558             } else {
  23559                 if (name == JS_ATOM_NULL) {
  23560                     emit_op(s, OP_define_method_computed);
  23561                 } else {
  23562                     emit_op(s, OP_define_method);
  23563                     emit_atom(s, name);
  23564                 }
  23565                 emit_u8(s, OP_DEFINE_METHOD_METHOD);
  23566             }
  23567         }
  23568         if (is_static)
  23569             emit_op(s, OP_swap);
  23570         JS_FreeAtom(ctx, name);
  23571         name = JS_ATOM_NULL;
  23572     }
  23573 
  23574     if (s->token.val != '}') {
  23575         js_parse_error(s, "expecting '%c'", '}');
  23576         goto fail;
  23577     }
  23578 
  23579     if (!ctor_fd) {
  23580         if (js_parse_class_default_ctor(s, class_flags & JS_DEFINE_CLASS_HAS_HERITAGE, &ctor_fd))
  23581             goto fail;
  23582     }
  23583     /* patch the constant pool index for the constructor */
  23584     put_u32(fd->byte_code.buf + ctor_cpool_offset, ctor_fd->parent_cpool_idx);
  23585 
  23586     /* store the class source code in the constructor. */
  23587     if (!(fd->js_mode & JS_MODE_STRIP)) {
  23588         js_free(ctx, ctor_fd->source);
  23589         ctor_fd->source_len = s->buf_ptr - class_start_ptr;
  23590         ctor_fd->source = js_strndup(ctx, (const char *)class_start_ptr,
  23591                                      ctor_fd->source_len);
  23592         if (!ctor_fd->source)
  23593             goto fail;
  23594     }
  23595 
  23596     /* consume the '}' */
  23597     if (next_token(s))
  23598         goto fail;
  23599 
  23600     {
  23601         ClassFieldsDef *cf = &class_fields[0];
  23602         int var_idx;
  23603 
  23604         if (cf->need_brand) {
  23605             /* add a private brand to the prototype */
  23606             emit_op(s, OP_dup);
  23607             emit_op(s, OP_null);
  23608             emit_op(s, OP_swap);
  23609             emit_op(s, OP_add_brand);
  23610 
  23611             /* define the brand field in 'this' of the initializer */
  23612             if (!cf->fields_init_fd) {
  23613                 if (emit_class_init_start(s, cf))
  23614                     goto fail;
  23615             }
  23616             /* patch the start of the function to enable the
  23617                OP_add_brand_instance code */
  23618             cf->fields_init_fd->byte_code.buf[cf->brand_push_pos] = OP_push_true;
  23619         }
  23620 
  23621         /* store the function to initialize the fields to that it can be
  23622            referenced by the constructor */
  23623         var_idx = define_var(s, fd, JS_ATOM_class_fields_init,
  23624                              JS_VAR_DEF_CONST);
  23625         if (var_idx < 0)
  23626             goto fail;
  23627         if (cf->fields_init_fd) {
  23628             emit_class_init_end(s, cf);
  23629         } else {
  23630             emit_op(s, OP_undefined);
  23631         }
  23632         emit_op(s, OP_scope_put_var_init);
  23633         emit_atom(s, JS_ATOM_class_fields_init);
  23634         emit_u16(s, s->cur_func->scope_level);
  23635     }
  23636 
  23637     /* drop the prototype */
  23638     emit_op(s, OP_drop);
  23639 
  23640     if (class_fields[1].need_brand) {
  23641         /* add a private brand to the class */
  23642         emit_op(s, OP_dup);
  23643         emit_op(s, OP_dup);
  23644         emit_op(s, OP_add_brand);
  23645     }
  23646 
  23647     if (class_name != JS_ATOM_NULL) {
  23648         /* store the class name in the scoped class name variable (it
  23649            is independent from the class statement variable
  23650            definition) */
  23651         emit_op(s, OP_dup);
  23652         emit_op(s, OP_scope_put_var_init);
  23653         emit_atom(s, class_name);
  23654         emit_u16(s, fd->scope_level);
  23655     }
  23656 
  23657     /* initialize the static fields */
  23658     if (class_fields[1].fields_init_fd != NULL) {
  23659         ClassFieldsDef *cf = &class_fields[1];
  23660         emit_op(s, OP_dup);
  23661         emit_class_init_end(s, cf);
  23662         emit_op(s, OP_call_method);
  23663         emit_u16(s, 0);
  23664         emit_op(s, OP_drop);
  23665     }
  23666 
  23667     pop_scope(s);
  23668     pop_scope(s);
  23669 
  23670     /* the class statements have a block level scope */
  23671     if (class_var_name != JS_ATOM_NULL) {
  23672         if (define_var(s, fd, class_var_name, JS_VAR_DEF_LET) < 0)
  23673             goto fail;
  23674         emit_op(s, OP_scope_put_var_init);
  23675         emit_atom(s, class_var_name);
  23676         emit_u16(s, fd->scope_level);
  23677     } else {
  23678         if (class_name == JS_ATOM_NULL) {
  23679             /* cannot use OP_set_name because the name of the class
  23680                must be defined before the static initializers are
  23681                executed */
  23682             emit_op(s, OP_set_class_name);
  23683             emit_u32(s, fd->last_opcode_pos + 1 - define_class_offset);
  23684         }
  23685     }
  23686 
  23687     if (export_flag != JS_PARSE_EXPORT_NONE) {
  23688         if (!add_export_entry(s, fd->module,
  23689                               class_var_name,
  23690                               export_flag == JS_PARSE_EXPORT_NAMED ? class_var_name : JS_ATOM_default,
  23691                               JS_EXPORT_TYPE_LOCAL))
  23692             goto fail;
  23693     }
  23694 
  23695     JS_FreeAtom(ctx, class_name);
  23696     JS_FreeAtom(ctx, class_var_name);
  23697     fd->js_mode = saved_js_mode;
  23698     return 0;
  23699  fail:
  23700     JS_FreeAtom(ctx, name);
  23701     JS_FreeAtom(ctx, class_name);
  23702     JS_FreeAtom(ctx, class_var_name);
  23703     fd->js_mode = saved_js_mode;
  23704     return -1;
  23705 }
  23706 
  23707 static __exception int js_parse_array_literal(JSParseState *s)
  23708 {
  23709     uint32_t idx;
  23710     BOOL need_length;
  23711 
  23712     if (next_token(s))
  23713         return -1;
  23714     /* small regular arrays are created on the stack */
  23715     idx = 0;
  23716     while (s->token.val != ']' && idx < 32) {
  23717         if (s->token.val == ',' || s->token.val == TOK_ELLIPSIS)
  23718             break;
  23719         if (js_parse_assign_expr(s))
  23720             return -1;
  23721         idx++;
  23722         /* accept trailing comma */
  23723         if (s->token.val == ',') {
  23724             if (next_token(s))
  23725                 return -1;
  23726         } else
  23727         if (s->token.val != ']')
  23728             goto done;
  23729     }
  23730     emit_op(s, OP_array_from);
  23731     emit_u16(s, idx);
  23732 
  23733     /* larger arrays and holes are handled with explicit indices */
  23734     need_length = FALSE;
  23735     while (s->token.val != ']' && idx < 0x7fffffff) {
  23736         if (s->token.val == TOK_ELLIPSIS)
  23737             break;
  23738         need_length = TRUE;
  23739         if (s->token.val != ',') {
  23740             if (js_parse_assign_expr(s))
  23741                 return -1;
  23742             emit_op(s, OP_define_field);
  23743             emit_u32(s, __JS_AtomFromUInt32(idx));
  23744             need_length = FALSE;
  23745         }
  23746         idx++;
  23747         /* accept trailing comma */
  23748         if (s->token.val == ',') {
  23749             if (next_token(s))
  23750                 return -1;
  23751         }
  23752     }
  23753     if (s->token.val == ']') {
  23754         if (need_length) {
  23755             /* Set the length: Cannot use OP_define_field because
  23756                length is not configurable */
  23757             emit_op(s, OP_dup);
  23758             emit_op(s, OP_push_i32);
  23759             emit_u32(s, idx);
  23760             emit_op(s, OP_put_field);
  23761             emit_atom(s, JS_ATOM_length);
  23762         }
  23763         goto done;
  23764     }
  23765 
  23766     /* huge arrays and spread elements require a dynamic index on the stack */
  23767     emit_op(s, OP_push_i32);
  23768     emit_u32(s, idx);
  23769 
  23770     /* stack has array, index */
  23771     while (s->token.val != ']') {
  23772         if (s->token.val == TOK_ELLIPSIS) {
  23773             if (next_token(s))
  23774                 return -1;
  23775             if (js_parse_assign_expr(s))
  23776                 return -1;
  23777 #if 1
  23778             emit_op(s, OP_append);
  23779 #else
  23780             int label_next, label_done;
  23781             label_next = new_label(s);
  23782             label_done = new_label(s);
  23783             /* enumerate object */
  23784             emit_op(s, OP_for_of_start);
  23785             emit_op(s, OP_rot5l);
  23786             emit_op(s, OP_rot5l);
  23787             emit_label(s, label_next);
  23788             /* on stack: enum_rec array idx */
  23789             emit_op(s, OP_for_of_next);
  23790             emit_u8(s, 2);
  23791             emit_goto(s, OP_if_true, label_done);
  23792             /* append element */
  23793             /* enum_rec array idx val -> enum_rec array new_idx */
  23794             emit_op(s, OP_define_array_el);
  23795             emit_op(s, OP_inc);
  23796             emit_goto(s, OP_goto, label_next);
  23797             emit_label(s, label_done);
  23798             /* close enumeration */
  23799             emit_op(s, OP_drop); /* drop undef val */
  23800             emit_op(s, OP_nip1); /* drop enum_rec */
  23801             emit_op(s, OP_nip1);
  23802             emit_op(s, OP_nip1);
  23803 #endif
  23804         } else {
  23805             need_length = TRUE;
  23806             if (s->token.val != ',') {
  23807                 if (js_parse_assign_expr(s))
  23808                     return -1;
  23809                 /* a idx val */
  23810                 emit_op(s, OP_define_array_el);
  23811                 need_length = FALSE;
  23812             }
  23813             emit_op(s, OP_inc);
  23814         }
  23815         if (s->token.val != ',')
  23816             break;
  23817         if (next_token(s))
  23818             return -1;
  23819     }
  23820     if (need_length) {
  23821         /* Set the length: cannot use OP_define_field because
  23822            length is not configurable */
  23823         emit_op(s, OP_dup1);    /* array length - array array length */
  23824         emit_op(s, OP_put_field);
  23825         emit_atom(s, JS_ATOM_length);
  23826     } else {
  23827         emit_op(s, OP_drop);    /* array length - array */
  23828     }
  23829 done:
  23830     return js_parse_expect(s, ']');
  23831 }
  23832 
  23833 /* XXX: remove */
  23834 static BOOL has_with_scope(JSFunctionDef *s, int scope_level)
  23835 {
  23836     /* check if scope chain contains a with statement */
  23837     while (s) {
  23838         int scope_idx = s->scopes[scope_level].first;
  23839         while (scope_idx >= 0) {
  23840             JSVarDef *vd = &s->vars[scope_idx];
  23841 
  23842             if (vd->var_name == JS_ATOM__with_)
  23843                 return TRUE;
  23844             scope_idx = vd->scope_next;
  23845         }
  23846         /* check parent scopes */
  23847         scope_level = s->parent_scope_level;
  23848         s = s->parent;
  23849     }
  23850     return FALSE;
  23851 }
  23852 
  23853 static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope,
  23854                                   JSAtom *pname, int *plabel, int *pdepth, BOOL keep,
  23855                                   int tok)
  23856 {
  23857     JSFunctionDef *fd;
  23858     int opcode, scope, label, depth;
  23859     JSAtom name;
  23860 
  23861     /* we check the last opcode to get the lvalue type */
  23862     fd = s->cur_func;
  23863     scope = 0;
  23864     name = JS_ATOM_NULL;
  23865     label = -1;
  23866     depth = 0;
  23867     switch(opcode = get_prev_opcode(fd)) {
  23868     case OP_scope_get_var:
  23869         name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
  23870         scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);
  23871         if ((name == JS_ATOM_arguments || name == JS_ATOM_eval) &&
  23872             (fd->js_mode & JS_MODE_STRICT)) {
  23873             return js_parse_error(s, "invalid lvalue in strict mode");
  23874         }
  23875         if (name == JS_ATOM_this || name == JS_ATOM_new_target)
  23876             goto invalid_lvalue;
  23877         depth = 2;  /* will generate OP_get_ref_value */
  23878         break;
  23879     case OP_get_field:
  23880         name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
  23881         depth = 1;
  23882         break;
  23883     case OP_scope_get_private_field:
  23884         name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
  23885         scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);
  23886         depth = 1;
  23887         break;
  23888     case OP_get_array_el:
  23889         depth = 2;
  23890         break;
  23891     case OP_get_super_value:
  23892         depth = 3;
  23893         break;
  23894     default:
  23895     invalid_lvalue:
  23896         if (tok == TOK_FOR) {
  23897             return js_parse_error(s, "invalid for in/of left hand-side");
  23898         } else if (tok == TOK_INC || tok == TOK_DEC) {
  23899             return js_parse_error(s, "invalid increment/decrement operand");
  23900         } else if (tok == '[' || tok == '{') {
  23901             return js_parse_error(s, "invalid destructuring target");
  23902         } else {
  23903             return js_parse_error(s, "invalid assignment left-hand side");
  23904         }
  23905     }
  23906     /* remove the last opcode */
  23907     fd->byte_code.size = fd->last_opcode_pos;
  23908     fd->last_opcode_pos = -1;
  23909 
  23910     if (keep) {
  23911         /* get the value but keep the object/fields on the stack */
  23912         switch(opcode) {
  23913         case OP_scope_get_var:
  23914             label = new_label(s);
  23915             emit_op(s, OP_scope_make_ref);
  23916             emit_atom(s, name);
  23917             emit_u32(s, label);
  23918             emit_u16(s, scope);
  23919             update_label(fd, label, 1);
  23920             emit_op(s, OP_get_ref_value);
  23921             opcode = OP_get_ref_value;
  23922             break;
  23923         case OP_get_field:
  23924             emit_op(s, OP_get_field2);
  23925             emit_atom(s, name);
  23926             break;
  23927         case OP_scope_get_private_field:
  23928             emit_op(s, OP_scope_get_private_field2);
  23929             emit_atom(s, name);
  23930             emit_u16(s, scope);
  23931             break;
  23932         case OP_get_array_el:
  23933             /* XXX: replace by a single opcode ? */
  23934             emit_op(s, OP_to_propkey2);
  23935             emit_op(s, OP_dup2);
  23936             emit_op(s, OP_get_array_el);
  23937             break;
  23938         case OP_get_super_value:
  23939             emit_op(s, OP_to_propkey);
  23940             emit_op(s, OP_dup3);
  23941             emit_op(s, OP_get_super_value);
  23942             break;
  23943         default:
  23944             abort();
  23945         }
  23946     } else {
  23947         switch(opcode) {
  23948         case OP_scope_get_var:
  23949             label = new_label(s);
  23950             emit_op(s, OP_scope_make_ref);
  23951             emit_atom(s, name);
  23952             emit_u32(s, label);
  23953             emit_u16(s, scope);
  23954             update_label(fd, label, 1);
  23955             opcode = OP_get_ref_value;
  23956             break;
  23957         case OP_get_array_el:
  23958             emit_op(s, OP_to_propkey2);
  23959             break;
  23960         case OP_get_super_value:
  23961             emit_op(s, OP_to_propkey);
  23962             break;
  23963         }
  23964     }
  23965 
  23966     *popcode = opcode;
  23967     *pscope = scope;
  23968     /* name has refcount for OP_get_field and OP_get_ref_value,
  23969        and JS_ATOM_NULL for other opcodes */
  23970     *pname = name;
  23971     *plabel = label;
  23972     if (pdepth)
  23973         *pdepth = depth;
  23974     return 0;
  23975 }
  23976 
  23977 typedef enum {
  23978     PUT_LVALUE_NOKEEP, /* [depth] v -> */
  23979     PUT_LVALUE_NOKEEP_DEPTH, /* [depth] v -> , keep depth (currently
  23980                                 just disable optimizations) */
  23981     PUT_LVALUE_KEEP_TOP,  /* [depth] v -> v */
  23982     PUT_LVALUE_KEEP_SECOND, /* [depth] v0 v -> v0 */
  23983     PUT_LVALUE_NOKEEP_BOTTOM, /* v [depth] -> */
  23984 } PutLValueEnum;
  23985 
  23986 /* name has a live reference. 'is_let' is only used with opcode =
  23987    OP_scope_get_var which is never generated by get_lvalue(). */
  23988 static void put_lvalue(JSParseState *s, int opcode, int scope,
  23989                        JSAtom name, int label, PutLValueEnum special,
  23990                        BOOL is_let)
  23991 {
  23992     switch(opcode) {
  23993     case OP_get_field:
  23994     case OP_scope_get_private_field:
  23995         /* depth = 1 */
  23996         switch(special) {
  23997         case PUT_LVALUE_NOKEEP:
  23998         case PUT_LVALUE_NOKEEP_DEPTH:
  23999             break;
  24000         case PUT_LVALUE_KEEP_TOP:
  24001             emit_op(s, OP_insert2); /* obj v -> v obj v */
  24002             break;
  24003         case PUT_LVALUE_KEEP_SECOND:
  24004             emit_op(s, OP_perm3); /* obj v0 v -> v0 obj v */
  24005             break;
  24006         case PUT_LVALUE_NOKEEP_BOTTOM:
  24007             emit_op(s, OP_swap);
  24008             break;
  24009         default:
  24010             abort();
  24011         }
  24012         break;
  24013     case OP_get_array_el:
  24014     case OP_get_ref_value:
  24015         /* depth = 2 */
  24016         if (opcode == OP_get_ref_value) {
  24017             JS_FreeAtom(s->ctx, name);
  24018             emit_label(s, label);
  24019         }
  24020         switch(special) {
  24021         case PUT_LVALUE_NOKEEP:
  24022             emit_op(s, OP_nop); /* will trigger optimization */
  24023             break;
  24024         case PUT_LVALUE_NOKEEP_DEPTH:
  24025             break;
  24026         case PUT_LVALUE_KEEP_TOP:
  24027             emit_op(s, OP_insert3); /* obj prop v -> v obj prop v */
  24028             break;
  24029         case PUT_LVALUE_KEEP_SECOND:
  24030             emit_op(s, OP_perm4); /* obj prop v0 v -> v0 obj prop v */
  24031             break;
  24032         case PUT_LVALUE_NOKEEP_BOTTOM:
  24033             emit_op(s, OP_rot3l);
  24034             break;
  24035         default:
  24036             abort();
  24037         }
  24038         break;
  24039     case OP_get_super_value:
  24040         /* depth = 3 */
  24041         switch(special) {
  24042         case PUT_LVALUE_NOKEEP:
  24043         case PUT_LVALUE_NOKEEP_DEPTH:
  24044             break;
  24045         case PUT_LVALUE_KEEP_TOP:
  24046             emit_op(s, OP_insert4); /* this obj prop v -> v this obj prop v */
  24047             break;
  24048         case PUT_LVALUE_KEEP_SECOND:
  24049             emit_op(s, OP_perm5); /* this obj prop v0 v -> v0 this obj prop v */
  24050             break;
  24051         case PUT_LVALUE_NOKEEP_BOTTOM:
  24052             emit_op(s, OP_rot4l);
  24053             break;
  24054         default:
  24055             abort();
  24056         }
  24057         break;
  24058     default:
  24059         break;
  24060     }
  24061 
  24062     switch(opcode) {
  24063     case OP_scope_get_var:  /* val -- */
  24064         assert(special == PUT_LVALUE_NOKEEP ||
  24065                special == PUT_LVALUE_NOKEEP_DEPTH);
  24066         emit_op(s, is_let ? OP_scope_put_var_init : OP_scope_put_var);
  24067         emit_u32(s, name);  /* has refcount */
  24068         emit_u16(s, scope);
  24069         break;
  24070     case OP_get_field:
  24071         emit_op(s, OP_put_field);
  24072         emit_u32(s, name);  /* name has refcount */
  24073         break;
  24074     case OP_scope_get_private_field:
  24075         emit_op(s, OP_scope_put_private_field);
  24076         emit_u32(s, name);  /* name has refcount */
  24077         emit_u16(s, scope);
  24078         break;
  24079     case OP_get_array_el:
  24080         emit_op(s, OP_put_array_el);
  24081         break;
  24082     case OP_get_ref_value:
  24083         emit_op(s, OP_put_ref_value);
  24084         break;
  24085     case OP_get_super_value:
  24086         emit_op(s, OP_put_super_value);
  24087         break;
  24088     default:
  24089         abort();
  24090     }
  24091 }
  24092 
  24093 static __exception int js_parse_expr_paren(JSParseState *s)
  24094 {
  24095     if (js_parse_expect(s, '('))
  24096         return -1;
  24097     if (js_parse_expr(s))
  24098         return -1;
  24099     if (js_parse_expect(s, ')'))
  24100         return -1;
  24101     return 0;
  24102 }
  24103 
  24104 static int js_unsupported_keyword(JSParseState *s, JSAtom atom)
  24105 {
  24106     char buf[ATOM_GET_STR_BUF_SIZE];
  24107     return js_parse_error(s, "unsupported keyword: %s",
  24108                           JS_AtomGetStr(s->ctx, buf, sizeof(buf), atom));
  24109 }
  24110 
  24111 static __exception int js_define_var(JSParseState *s, JSAtom name, int tok)
  24112 {
  24113     JSFunctionDef *fd = s->cur_func;
  24114     JSVarDefEnum var_def_type;
  24115 
  24116     if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) {
  24117         return js_parse_error(s, "yield is a reserved identifier");
  24118     }
  24119     if ((name == JS_ATOM_arguments || name == JS_ATOM_eval)
  24120     &&  (fd->js_mode & JS_MODE_STRICT)) {
  24121         return js_parse_error(s, "invalid variable name in strict mode");
  24122     }
  24123     if ((name == JS_ATOM_let || name == JS_ATOM_undefined)
  24124     &&  (tok == TOK_LET || tok == TOK_CONST)) {
  24125         return js_parse_error(s, "invalid lexical variable name");
  24126     }
  24127     switch(tok) {
  24128     case TOK_LET:
  24129         var_def_type = JS_VAR_DEF_LET;
  24130         break;
  24131     case TOK_CONST:
  24132         var_def_type = JS_VAR_DEF_CONST;
  24133         break;
  24134     case TOK_VAR:
  24135         var_def_type = JS_VAR_DEF_VAR;
  24136         break;
  24137     case TOK_CATCH:
  24138         var_def_type = JS_VAR_DEF_CATCH;
  24139         break;
  24140     default:
  24141         abort();
  24142     }
  24143     if (define_var(s, fd, name, var_def_type) < 0)
  24144         return -1;
  24145     return 0;
  24146 }
  24147 
  24148 static void js_emit_spread_code(JSParseState *s, int depth)
  24149 {
  24150     int label_rest_next, label_rest_done;
  24151 
  24152     /* XXX: could check if enum object is an actual array and optimize
  24153        slice extraction. enumeration record and target array are in a
  24154        different order from OP_append case. */
  24155     /* enum_rec xxx -- enum_rec xxx array 0 */
  24156     emit_op(s, OP_array_from);
  24157     emit_u16(s, 0);
  24158     emit_op(s, OP_push_i32);
  24159     emit_u32(s, 0);
  24160     emit_label(s, label_rest_next = new_label(s));
  24161     emit_op(s, OP_for_of_next);
  24162     emit_u8(s, 2 + depth);
  24163     label_rest_done = emit_goto(s, OP_if_true, -1);
  24164     /* array idx val -- array idx */
  24165     emit_op(s, OP_define_array_el);
  24166     emit_op(s, OP_inc);
  24167     emit_goto(s, OP_goto, label_rest_next);
  24168     emit_label(s, label_rest_done);
  24169     /* enum_rec xxx array idx undef -- enum_rec xxx array */
  24170     emit_op(s, OP_drop);
  24171     emit_op(s, OP_drop);
  24172 }
  24173 
  24174 static int js_parse_check_duplicate_parameter(JSParseState *s, JSAtom name)
  24175 {
  24176     /* Check for duplicate parameter names */
  24177     JSFunctionDef *fd = s->cur_func;
  24178     int i;
  24179     for (i = 0; i < fd->arg_count; i++) {
  24180         if (fd->args[i].var_name == name)
  24181             goto duplicate;
  24182     }
  24183     for (i = 0; i < fd->var_count; i++) {
  24184         if (fd->vars[i].var_name == name)
  24185             goto duplicate;
  24186     }
  24187     return 0;
  24188 
  24189 duplicate:
  24190     return js_parse_error(s, "duplicate parameter names not allowed in this context");
  24191 }
  24192 
  24193 static JSAtom js_parse_destructuring_var(JSParseState *s, int tok, int is_arg)
  24194 {
  24195     JSAtom name;
  24196 
  24197     if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)
  24198     ||  ((s->cur_func->js_mode & JS_MODE_STRICT) &&
  24199          (s->token.u.ident.atom == JS_ATOM_eval || s->token.u.ident.atom == JS_ATOM_arguments))) {
  24200         js_parse_error(s, "invalid destructuring target");
  24201         return JS_ATOM_NULL;
  24202     }
  24203     name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
  24204     if (is_arg && js_parse_check_duplicate_parameter(s, name))
  24205         goto fail;
  24206     if (next_token(s))
  24207         goto fail;
  24208 
  24209     return name;
  24210 fail:
  24211     JS_FreeAtom(s->ctx, name);
  24212     return JS_ATOM_NULL;
  24213 }
  24214 
  24215 /* Return -1 if error, 0 if no initializer, 1 if an initializer is
  24216    present at the top level. */
  24217 static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg,
  24218                                         int hasval, int has_ellipsis,
  24219                                         BOOL allow_initializer)
  24220 {
  24221     int label_parse, label_assign, label_done, label_lvalue, depth_lvalue;
  24222     int start_addr, assign_addr;
  24223     JSAtom prop_name, var_name;
  24224     int opcode, scope, tok1, skip_bits;
  24225     BOOL has_initializer;
  24226 
  24227     if (has_ellipsis < 0) {
  24228         /* pre-parse destructuration target for spread detection */
  24229         js_parse_skip_parens_token(s, &skip_bits, FALSE);
  24230         has_ellipsis = skip_bits & SKIP_HAS_ELLIPSIS;
  24231     }
  24232 
  24233     label_parse = new_label(s);
  24234     label_assign = new_label(s);
  24235 
  24236     start_addr = s->cur_func->byte_code.size;
  24237     if (hasval) {
  24238         /* consume value from the stack */
  24239         emit_op(s, OP_dup);
  24240         emit_op(s, OP_undefined);
  24241         emit_op(s, OP_strict_eq);
  24242         emit_goto(s, OP_if_true, label_parse);
  24243         emit_label(s, label_assign);
  24244     } else {
  24245         emit_goto(s, OP_goto, label_parse);
  24246         emit_label(s, label_assign);
  24247         /* leave value on the stack */
  24248         emit_op(s, OP_dup);
  24249     }
  24250     assign_addr = s->cur_func->byte_code.size;
  24251     if (s->token.val == '{') {
  24252         if (next_token(s))
  24253             return -1;
  24254         /* throw an exception if the value cannot be converted to an object */
  24255         emit_op(s, OP_to_object);
  24256         if (has_ellipsis) {
  24257             /* add excludeList on stack just below src object */
  24258             emit_op(s, OP_object);
  24259             emit_op(s, OP_swap);
  24260         }
  24261         while (s->token.val != '}') {
  24262             int prop_type;
  24263             if (s->token.val == TOK_ELLIPSIS) {
  24264                 if (!has_ellipsis) {
  24265                     JS_ThrowInternalError(s->ctx, "unexpected ellipsis token");
  24266                     return -1;
  24267                 }
  24268                 if (next_token(s))
  24269                     return -1;
  24270                 if (tok) {
  24271                     var_name = js_parse_destructuring_var(s, tok, is_arg);
  24272                     if (var_name == JS_ATOM_NULL)
  24273                         return -1;
  24274                     opcode = OP_scope_get_var;
  24275                     scope = s->cur_func->scope_level;
  24276                     label_lvalue = -1;
  24277                     depth_lvalue = 0;
  24278                 } else {
  24279                     if (js_parse_left_hand_side_expr(s))
  24280                         return -1;
  24281 
  24282                     if (get_lvalue(s, &opcode, &scope, &var_name,
  24283                                    &label_lvalue, &depth_lvalue, FALSE, '{'))
  24284                         return -1;
  24285                 }
  24286                 if (s->token.val != '}') {
  24287                     js_parse_error(s, "assignment rest property must be last");
  24288                     goto var_error;
  24289                 }
  24290                 emit_op(s, OP_object);  /* target */
  24291                 emit_op(s, OP_copy_data_properties);
  24292                 emit_u8(s, 0 | ((depth_lvalue + 1) << 2) | ((depth_lvalue + 2) << 5));
  24293                 goto set_val;
  24294             }
  24295             prop_type = js_parse_property_name(s, &prop_name, FALSE, TRUE, FALSE);
  24296             if (prop_type < 0)
  24297                 return -1;
  24298             var_name = JS_ATOM_NULL;
  24299             opcode = OP_scope_get_var;
  24300             scope = s->cur_func->scope_level;
  24301             label_lvalue = -1;
  24302             depth_lvalue = 0;
  24303             if (prop_type == PROP_TYPE_IDENT) {
  24304                 if (next_token(s))
  24305                     goto prop_error;
  24306                 if ((s->token.val == '[' || s->token.val == '{')
  24307                     &&  ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' ||
  24308                          tok1 == '=' || tok1 == '}')) {
  24309                     if (prop_name == JS_ATOM_NULL) {
  24310                         /* computed property name on stack */
  24311                         if (has_ellipsis) {
  24312                             /* define the property in excludeList */
  24313                             emit_op(s, OP_to_propkey); /* avoid calling ToString twice */
  24314                             emit_op(s, OP_perm3); /* TOS: src excludeList prop */
  24315                             emit_op(s, OP_null); /* TOS: src excludeList prop null */
  24316                             emit_op(s, OP_define_array_el); /* TOS: src excludeList prop */
  24317                             emit_op(s, OP_perm3); /* TOS: excludeList src prop */
  24318                         }
  24319                         /* get the computed property from the source object */
  24320                         emit_op(s, OP_get_array_el2);
  24321                     } else {
  24322                         /* named property */
  24323                         if (has_ellipsis) {
  24324                             /* define the property in excludeList */
  24325                             emit_op(s, OP_swap); /* TOS: src excludeList */
  24326                             emit_op(s, OP_null); /* TOS: src excludeList null */
  24327                             emit_op(s, OP_define_field); /* TOS: src excludeList */
  24328                             emit_atom(s, prop_name);
  24329                             emit_op(s, OP_swap); /* TOS: excludeList src */
  24330                         }
  24331                         /* get the named property from the source object */
  24332                         emit_op(s, OP_get_field2);
  24333                         emit_u32(s, prop_name);
  24334                     }
  24335                     if (js_parse_destructuring_element(s, tok, is_arg, TRUE, -1, TRUE) < 0)
  24336                         return -1;
  24337                     if (s->token.val == '}')
  24338                         break;
  24339                     /* accept a trailing comma before the '}' */
  24340                     if (js_parse_expect(s, ','))
  24341                         return -1;
  24342                     continue;
  24343                 }
  24344                 if (prop_name == JS_ATOM_NULL) {
  24345                     emit_op(s, OP_to_propkey2);
  24346                     if (has_ellipsis) {
  24347                         /* define the property in excludeList */
  24348                         emit_op(s, OP_perm3);
  24349                         emit_op(s, OP_null);
  24350                         emit_op(s, OP_define_array_el);
  24351                         emit_op(s, OP_perm3);
  24352                     }
  24353                     /* source prop -- source source prop */
  24354                     emit_op(s, OP_dup1);
  24355                 } else {
  24356                     if (has_ellipsis) {
  24357                         /* define the property in excludeList */
  24358                         emit_op(s, OP_swap);
  24359                         emit_op(s, OP_null);
  24360                         emit_op(s, OP_define_field);
  24361                         emit_atom(s, prop_name);
  24362                         emit_op(s, OP_swap);
  24363                     }
  24364                     /* source -- source source */
  24365                     emit_op(s, OP_dup);
  24366                 }
  24367                 if (tok) {
  24368                     var_name = js_parse_destructuring_var(s, tok, is_arg);
  24369                     if (var_name == JS_ATOM_NULL)
  24370                         goto prop_error;
  24371                 } else {
  24372                     if (js_parse_left_hand_side_expr(s))
  24373                         goto prop_error;
  24374                 lvalue:
  24375                     if (get_lvalue(s, &opcode, &scope, &var_name,
  24376                                    &label_lvalue, &depth_lvalue, FALSE, '{'))
  24377                         goto prop_error;
  24378                     /* swap ref and lvalue object if any */
  24379                     if (prop_name == JS_ATOM_NULL) {
  24380                         switch(depth_lvalue) {
  24381                         case 1:
  24382                             /* source prop x -> x source prop */
  24383                             emit_op(s, OP_rot3r);
  24384                             break;
  24385                         case 2:
  24386                             /* source prop x y -> x y source prop */
  24387                             emit_op(s, OP_swap2);   /* t p2 s p1 */
  24388                             break;
  24389                         case 3:
  24390                             /* source prop x y z -> x y z source prop */
  24391                             emit_op(s, OP_rot5l);
  24392                             emit_op(s, OP_rot5l);
  24393                             break;
  24394                         }
  24395                     } else {
  24396                         switch(depth_lvalue) {
  24397                         case 1:
  24398                             /* source x -> x source */
  24399                             emit_op(s, OP_swap);
  24400                             break;
  24401                         case 2:
  24402                             /* source x y -> x y source */
  24403                             emit_op(s, OP_rot3l);
  24404                             break;
  24405                         case 3:
  24406                             /* source x y z -> x y z source */
  24407                             emit_op(s, OP_rot4l);
  24408                             break;
  24409                         }
  24410                     }
  24411                 }
  24412                 if (prop_name == JS_ATOM_NULL) {
  24413                     /* computed property name on stack */
  24414                     /* XXX: should have OP_get_array_el2x with depth */
  24415                     /* source prop -- val */
  24416                     emit_op(s, OP_get_array_el);
  24417                 } else {
  24418                     /* named property */
  24419                     /* XXX: should have OP_get_field2x with depth */
  24420                     /* source -- val */
  24421                     emit_op(s, OP_get_field);
  24422                     emit_u32(s, prop_name);
  24423                 }
  24424             } else {
  24425                 /* prop_type = PROP_TYPE_VAR, cannot be a computed property */
  24426                 if (is_arg && js_parse_check_duplicate_parameter(s, prop_name))
  24427                     goto prop_error;
  24428                 if ((s->cur_func->js_mode & JS_MODE_STRICT) &&
  24429                     (prop_name == JS_ATOM_eval || prop_name == JS_ATOM_arguments)) {
  24430                     js_parse_error(s, "invalid destructuring target");
  24431                     goto prop_error;
  24432                 }
  24433                 if (has_ellipsis) {
  24434                     /* define the property in excludeList */
  24435                     emit_op(s, OP_swap);
  24436                     emit_op(s, OP_null);
  24437                     emit_op(s, OP_define_field);
  24438                     emit_atom(s, prop_name);
  24439                     emit_op(s, OP_swap);
  24440                 }
  24441                 if (!tok || tok == TOK_VAR) {
  24442                     /* generate reference */
  24443                     /* source -- source source */
  24444                     emit_op(s, OP_dup);
  24445                     emit_op(s, OP_scope_get_var);
  24446                     emit_atom(s, prop_name);
  24447                     emit_u16(s, s->cur_func->scope_level);
  24448                     goto lvalue;
  24449                 }
  24450                 var_name = JS_DupAtom(s->ctx, prop_name);
  24451                 /* source -- source val */
  24452                 emit_op(s, OP_get_field2);
  24453                 emit_u32(s, prop_name);
  24454             }
  24455         set_val:
  24456             if (tok) {
  24457                 if (js_define_var(s, var_name, tok))
  24458                     goto var_error;
  24459                 scope = s->cur_func->scope_level;
  24460             }
  24461             if (s->token.val == '=') {  /* handle optional default value */
  24462                 int label_hasval;
  24463                 emit_op(s, OP_dup);
  24464                 emit_op(s, OP_undefined);
  24465                 emit_op(s, OP_strict_eq);
  24466                 label_hasval = emit_goto(s, OP_if_false, -1);
  24467                 if (next_token(s))
  24468                     goto var_error;
  24469                 emit_op(s, OP_drop);
  24470                 if (js_parse_assign_expr(s))
  24471                     goto var_error;
  24472                 if (opcode == OP_scope_get_var || opcode == OP_get_ref_value)
  24473                     set_object_name(s, var_name);
  24474                 emit_label(s, label_hasval);
  24475             }
  24476             /* store value into lvalue object */
  24477             put_lvalue(s, opcode, scope, var_name, label_lvalue,
  24478                        PUT_LVALUE_NOKEEP_DEPTH,
  24479                        (tok == TOK_CONST || tok == TOK_LET));
  24480             if (s->token.val == '}')
  24481                 break;
  24482             /* accept a trailing comma before the '}' */
  24483             if (js_parse_expect(s, ','))
  24484                 return -1;
  24485         }
  24486         /* drop the source object */
  24487         emit_op(s, OP_drop);
  24488         if (has_ellipsis) {
  24489             emit_op(s, OP_drop); /* pop excludeList */
  24490         }
  24491         if (next_token(s))
  24492             return -1;
  24493     } else if (s->token.val == '[') {
  24494         BOOL has_spread;
  24495         int enum_depth;
  24496         BlockEnv block_env;
  24497 
  24498         if (next_token(s))
  24499             return -1;
  24500         /* the block environment is only needed in generators in case
  24501            'yield' triggers a 'return' */
  24502         push_break_entry(s->cur_func, &block_env,
  24503                          JS_ATOM_NULL, -1, -1, 2);
  24504         block_env.has_iterator = TRUE;
  24505         emit_op(s, OP_for_of_start);
  24506         has_spread = FALSE;
  24507         while (s->token.val != ']') {
  24508             /* get the next value */
  24509             if (s->token.val == TOK_ELLIPSIS) {
  24510                 if (next_token(s))
  24511                     return -1;
  24512                 if (s->token.val == ',' || s->token.val == ']')
  24513                     return js_parse_error(s, "missing binding pattern...");
  24514                 has_spread = TRUE;
  24515             }
  24516             if (s->token.val == ',') {
  24517                 /* do nothing, skip the value, has_spread is false */
  24518                 emit_op(s, OP_for_of_next);
  24519                 emit_u8(s, 0);
  24520                 emit_op(s, OP_drop);
  24521                 emit_op(s, OP_drop);
  24522             } else if ((s->token.val == '[' || s->token.val == '{')
  24523                    &&  ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' ||
  24524                         tok1 == '=' || tok1 == ']')) {
  24525                 if (has_spread) {
  24526                     if (tok1 == '=')
  24527                         return js_parse_error(s, "rest element cannot have a default value");
  24528                     js_emit_spread_code(s, 0);
  24529                 } else {
  24530                     emit_op(s, OP_for_of_next);
  24531                     emit_u8(s, 0);
  24532                     emit_op(s, OP_drop);
  24533                 }
  24534                 if (js_parse_destructuring_element(s, tok, is_arg, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)
  24535                     return -1;
  24536             } else {
  24537                 var_name = JS_ATOM_NULL;
  24538                 enum_depth = 0;
  24539                 if (tok) {
  24540                     var_name = js_parse_destructuring_var(s, tok, is_arg);
  24541                     if (var_name == JS_ATOM_NULL)
  24542                         goto var_error;
  24543                     if (js_define_var(s, var_name, tok))
  24544                         goto var_error;
  24545                     opcode = OP_scope_get_var;
  24546                     scope = s->cur_func->scope_level;
  24547                 } else {
  24548                     if (js_parse_left_hand_side_expr(s))
  24549                         return -1;
  24550                     if (get_lvalue(s, &opcode, &scope, &var_name,
  24551                                    &label_lvalue, &enum_depth, FALSE, '[')) {
  24552                         return -1;
  24553                     }
  24554                 }
  24555                 if (has_spread) {
  24556                     js_emit_spread_code(s, enum_depth);
  24557                 } else {
  24558                     emit_op(s, OP_for_of_next);
  24559                     emit_u8(s, enum_depth);
  24560                     emit_op(s, OP_drop);
  24561                 }
  24562                 if (s->token.val == '=' && !has_spread) {
  24563                     /* handle optional default value */
  24564                     int label_hasval;
  24565                     emit_op(s, OP_dup);
  24566                     emit_op(s, OP_undefined);
  24567                     emit_op(s, OP_strict_eq);
  24568                     label_hasval = emit_goto(s, OP_if_false, -1);
  24569                     if (next_token(s))
  24570                         goto var_error;
  24571                     emit_op(s, OP_drop);
  24572                     if (js_parse_assign_expr(s))
  24573                         goto var_error;
  24574                     if (opcode == OP_scope_get_var || opcode == OP_get_ref_value)
  24575                         set_object_name(s, var_name);
  24576                     emit_label(s, label_hasval);
  24577                 }
  24578                 /* store value into lvalue object */
  24579                 put_lvalue(s, opcode, scope, var_name,
  24580                            label_lvalue, PUT_LVALUE_NOKEEP_DEPTH,
  24581                            (tok == TOK_CONST || tok == TOK_LET));
  24582             }
  24583             if (s->token.val == ']')
  24584                 break;
  24585             if (has_spread)
  24586                 return js_parse_error(s, "rest element must be the last one");
  24587             /* accept a trailing comma before the ']' */
  24588             if (js_parse_expect(s, ','))
  24589                 return -1;
  24590         }
  24591         /* close iterator object:
  24592            if completed, enum_obj has been replaced by undefined */
  24593         emit_op(s, OP_iterator_close);
  24594         pop_break_entry(s->cur_func);
  24595         if (next_token(s))
  24596             return -1;
  24597     } else {
  24598         return js_parse_error(s, "invalid assignment syntax");
  24599     }
  24600     if (s->token.val == '=' && allow_initializer) {
  24601         label_done = emit_goto(s, OP_goto, -1);
  24602         if (next_token(s))
  24603             return -1;
  24604         emit_label(s, label_parse);
  24605         if (hasval)
  24606             emit_op(s, OP_drop);
  24607         if (js_parse_assign_expr(s))
  24608             return -1;
  24609         emit_goto(s, OP_goto, label_assign);
  24610         emit_label(s, label_done);
  24611         has_initializer = TRUE;
  24612     } else {
  24613         /* normally hasval is true except if
  24614            js_parse_skip_parens_token() was wrong in the parsing */
  24615         //        assert(hasval);
  24616         if (!hasval) {
  24617             js_parse_error(s, "too complicated destructuring expression");
  24618             return -1;
  24619         }
  24620         /* remove test and decrement label ref count */
  24621         memset(s->cur_func->byte_code.buf + start_addr, OP_nop,
  24622                assign_addr - start_addr);
  24623         s->cur_func->label_slots[label_parse].ref_count--;
  24624         has_initializer = FALSE;
  24625     }
  24626     return has_initializer;
  24627 
  24628  prop_error:
  24629     JS_FreeAtom(s->ctx, prop_name);
  24630  var_error:
  24631     JS_FreeAtom(s->ctx, var_name);
  24632     return -1;
  24633 }
  24634 
  24635 typedef enum FuncCallType {
  24636     FUNC_CALL_NORMAL,
  24637     FUNC_CALL_NEW,
  24638     FUNC_CALL_SUPER_CTOR,
  24639     FUNC_CALL_TEMPLATE,
  24640 } FuncCallType;
  24641 
  24642 static void optional_chain_test(JSParseState *s, int *poptional_chaining_label,
  24643                                 int drop_count)
  24644 {
  24645     int label_next, i;
  24646     if (*poptional_chaining_label < 0)
  24647         *poptional_chaining_label = new_label(s);
  24648    /* XXX: could be more efficient with a specific opcode */
  24649     emit_op(s, OP_dup);
  24650     emit_op(s, OP_is_undefined_or_null);
  24651     label_next = emit_goto(s, OP_if_false, -1);
  24652     for(i = 0; i < drop_count; i++)
  24653         emit_op(s, OP_drop);
  24654     emit_op(s, OP_undefined);
  24655     emit_goto(s, OP_goto, *poptional_chaining_label);
  24656     emit_label(s, label_next);
  24657 }
  24658 
  24659 /* allowed parse_flags: PF_POSTFIX_CALL */
  24660 static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
  24661 {
  24662     FuncCallType call_type;
  24663     int optional_chaining_label;
  24664     BOOL accept_lparen = (parse_flags & PF_POSTFIX_CALL) != 0;
  24665 
  24666     call_type = FUNC_CALL_NORMAL;
  24667     switch(s->token.val) {
  24668     case TOK_NUMBER:
  24669         {
  24670             JSValue val;
  24671             val = s->token.u.num.val;
  24672 
  24673             if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) {
  24674                 emit_op(s, OP_push_i32);
  24675                 emit_u32(s, JS_VALUE_GET_INT(val));
  24676             } else
  24677 #ifdef CONFIG_BIGNUM
  24678             if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {
  24679                 slimb_t e;
  24680                 int ret;
  24681 
  24682                 /* need a runtime conversion */
  24683                 /* XXX: could add a cache and/or do it once at
  24684                    the start of the function */
  24685                 if (emit_push_const(s, val, 0) < 0)
  24686                     return -1;
  24687                 e = s->token.u.num.exponent;
  24688                 if (e == (int32_t)e) {
  24689                     emit_op(s, OP_push_i32);
  24690                     emit_u32(s, e);
  24691                 } else {
  24692                     val = JS_NewBigInt64_1(s->ctx, e);
  24693                     if (JS_IsException(val))
  24694                         return -1;
  24695                     ret = emit_push_const(s, val, 0);
  24696                     JS_FreeValue(s->ctx, val);
  24697                     if (ret < 0)
  24698                         return -1;
  24699                 }
  24700                 emit_op(s, OP_mul_pow10);
  24701             } else
  24702 #endif
  24703             {
  24704                 if (emit_push_const(s, val, 0) < 0)
  24705                     return -1;
  24706             }
  24707         }
  24708         if (next_token(s))
  24709             return -1;
  24710         break;
  24711     case TOK_TEMPLATE:
  24712         if (js_parse_template(s, 0, NULL))
  24713             return -1;
  24714         break;
  24715     case TOK_STRING:
  24716         if (emit_push_const(s, s->token.u.str.str, 1))
  24717             return -1;
  24718         if (next_token(s))
  24719             return -1;
  24720         break;
  24721 
  24722     case TOK_DIV_ASSIGN:
  24723         s->buf_ptr -= 2;
  24724         goto parse_regexp;
  24725     case '/':
  24726         s->buf_ptr--;
  24727     parse_regexp:
  24728         {
  24729             JSValue str;
  24730             int ret, backtrace_flags;
  24731             if (!s->ctx->compile_regexp)
  24732                 return js_parse_error(s, "RegExp are not supported");
  24733             /* the previous token is '/' or '/=', so no need to free */
  24734             if (js_parse_regexp(s))
  24735                 return -1;
  24736             ret = emit_push_const(s, s->token.u.regexp.body, 0);
  24737             str = s->ctx->compile_regexp(s->ctx, s->token.u.regexp.body,
  24738                                          s->token.u.regexp.flags);
  24739             if (JS_IsException(str)) {
  24740                 /* add the line number info */
  24741                 backtrace_flags = 0;
  24742                 if (s->cur_func && s->cur_func->backtrace_barrier)
  24743                     backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL;
  24744                 build_backtrace(s->ctx, s->ctx->rt->current_exception,
  24745                                 s->filename, s->token.line_num,
  24746                                 backtrace_flags);
  24747                 return -1;
  24748             }
  24749             ret = emit_push_const(s, str, 0);
  24750             JS_FreeValue(s->ctx, str);
  24751             if (ret)
  24752                 return -1;
  24753             /* we use a specific opcode to be sure the correct
  24754                function is called (otherwise the bytecode would have
  24755                to be verified by the RegExp constructor) */
  24756             emit_op(s, OP_regexp);
  24757             if (next_token(s))
  24758                 return -1;
  24759         }
  24760         break;
  24761     case '(':
  24762         if (js_parse_expr_paren(s))
  24763             return -1;
  24764         break;
  24765     case TOK_FUNCTION:
  24766         if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR,
  24767                                    JS_FUNC_NORMAL, JS_ATOM_NULL,
  24768                                    s->token.ptr, s->token.line_num))
  24769             return -1;
  24770         break;
  24771     case TOK_CLASS:
  24772         if (js_parse_class(s, TRUE, JS_PARSE_EXPORT_NONE))
  24773             return -1;
  24774         break;
  24775     case TOK_NULL:
  24776         if (next_token(s))
  24777             return -1;
  24778         emit_op(s, OP_null);
  24779         break;
  24780     case TOK_THIS:
  24781         if (next_token(s))
  24782             return -1;
  24783         emit_op(s, OP_scope_get_var);
  24784         emit_atom(s, JS_ATOM_this);
  24785         emit_u16(s, 0);
  24786         break;
  24787     case TOK_FALSE:
  24788         if (next_token(s))
  24789             return -1;
  24790         emit_op(s, OP_push_false);
  24791         break;
  24792     case TOK_TRUE:
  24793         if (next_token(s))
  24794             return -1;
  24795         emit_op(s, OP_push_true);
  24796         break;
  24797     case TOK_IDENT:
  24798         {
  24799             JSAtom name;
  24800             if (s->token.u.ident.is_reserved) {
  24801                 return js_parse_error_reserved_identifier(s);
  24802             }
  24803             if (token_is_pseudo_keyword(s, JS_ATOM_async) &&
  24804                 peek_token(s, TRUE) != '\n') {
  24805                 const uint8_t *source_ptr;
  24806                 int source_line_num;
  24807 
  24808                 source_ptr = s->token.ptr;
  24809                 source_line_num = s->token.line_num;
  24810                 if (next_token(s))
  24811                     return -1;
  24812                 if (s->token.val == TOK_FUNCTION) {
  24813                     if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR,
  24814                                                JS_FUNC_ASYNC, JS_ATOM_NULL,
  24815                                                source_ptr, source_line_num))
  24816                         return -1;
  24817                 } else {
  24818                     name = JS_DupAtom(s->ctx, JS_ATOM_async);
  24819                     goto do_get_var;
  24820                 }
  24821             } else {
  24822                 if (s->token.u.ident.atom == JS_ATOM_arguments &&
  24823                     !s->cur_func->arguments_allowed) {
  24824                     js_parse_error(s, "'arguments' identifier is not allowed in class field initializer");
  24825                     return -1;
  24826                 }
  24827                 name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
  24828                 if (next_token(s)) {  /* update line number before emitting code */
  24829                     JS_FreeAtom(s->ctx, name);
  24830                     return -1;
  24831                 }
  24832             do_get_var:
  24833                 emit_op(s, OP_scope_get_var);
  24834                 emit_u32(s, name);
  24835                 emit_u16(s, s->cur_func->scope_level);
  24836             }
  24837         }
  24838         break;
  24839     case '{':
  24840     case '[':
  24841         {
  24842             int skip_bits;
  24843             if (js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') {
  24844                 if (js_parse_destructuring_element(s, 0, 0, FALSE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)
  24845                     return -1;
  24846             } else {
  24847                 if (s->token.val == '{') {
  24848                     if (js_parse_object_literal(s))
  24849                         return -1;
  24850                 } else {
  24851                     if (js_parse_array_literal(s))
  24852                         return -1;
  24853                 }
  24854             }
  24855         }
  24856         break;
  24857     case TOK_NEW:
  24858         if (next_token(s))
  24859             return -1;
  24860         if (s->token.val == '.') {
  24861             if (next_token(s))
  24862                 return -1;
  24863             if (!token_is_pseudo_keyword(s, JS_ATOM_target))
  24864                 return js_parse_error(s, "expecting target");
  24865             if (!s->cur_func->new_target_allowed)
  24866                 return js_parse_error(s, "new.target only allowed within functions");
  24867             if (next_token(s))
  24868                 return -1;
  24869             emit_op(s, OP_scope_get_var);
  24870             emit_atom(s, JS_ATOM_new_target);
  24871             emit_u16(s, 0);
  24872         } else {
  24873             if (js_parse_postfix_expr(s, 0))
  24874                 return -1;
  24875             accept_lparen = TRUE;
  24876             if (s->token.val != '(') {
  24877                 /* new operator on an object */
  24878                 emit_op(s, OP_dup);
  24879                 emit_op(s, OP_call_constructor);
  24880                 emit_u16(s, 0);
  24881             } else {
  24882                 call_type = FUNC_CALL_NEW;
  24883             }
  24884         }
  24885         break;
  24886     case TOK_SUPER:
  24887         if (next_token(s))
  24888             return -1;
  24889         if (s->token.val == '(') {
  24890             if (!s->cur_func->super_call_allowed)
  24891                 return js_parse_error(s, "super() is only valid in a derived class constructor");
  24892             call_type = FUNC_CALL_SUPER_CTOR;
  24893         } else if (s->token.val == '.' || s->token.val == '[') {
  24894             if (!s->cur_func->super_allowed)
  24895                 return js_parse_error(s, "'super' is only valid in a method");
  24896             emit_op(s, OP_scope_get_var);
  24897             emit_atom(s, JS_ATOM_this);
  24898             emit_u16(s, 0);
  24899             emit_op(s, OP_scope_get_var);
  24900             emit_atom(s, JS_ATOM_home_object);
  24901             emit_u16(s, 0);
  24902             emit_op(s, OP_get_super);
  24903         } else {
  24904             return js_parse_error(s, "invalid use of 'super'");
  24905         }
  24906         break;
  24907     case TOK_IMPORT:
  24908         if (next_token(s))
  24909             return -1;
  24910         if (s->token.val == '.') {
  24911             if (next_token(s))
  24912                 return -1;
  24913             if (!token_is_pseudo_keyword(s, JS_ATOM_meta))
  24914                 return js_parse_error(s, "meta expected");
  24915             if (!s->is_module)
  24916                 return js_parse_error(s, "import.meta only valid in module code");
  24917             if (next_token(s))
  24918                 return -1;
  24919             emit_op(s, OP_special_object);
  24920             emit_u8(s, OP_SPECIAL_OBJECT_IMPORT_META);
  24921         } else {
  24922             if (js_parse_expect(s, '('))
  24923                 return -1;
  24924             if (!accept_lparen)
  24925                 return js_parse_error(s, "invalid use of 'import()'");
  24926             if (js_parse_assign_expr(s))
  24927                 return -1;
  24928             if (js_parse_expect(s, ')'))
  24929                 return -1;
  24930             emit_op(s, OP_import);
  24931         }
  24932         break;
  24933     default:
  24934         return js_parse_error(s, "unexpected token in expression: '%.*s'",
  24935                               (int)(s->buf_ptr - s->token.ptr), s->token.ptr);
  24936     }
  24937 
  24938     optional_chaining_label = -1;
  24939     for(;;) {
  24940         JSFunctionDef *fd = s->cur_func;
  24941         BOOL has_optional_chain = FALSE;
  24942 
  24943         if (s->token.val == TOK_QUESTION_MARK_DOT) {
  24944             /* optional chaining */
  24945             if (next_token(s))
  24946                 return -1;
  24947             has_optional_chain = TRUE;
  24948             if (s->token.val == '(' && accept_lparen) {
  24949                 goto parse_func_call;
  24950             } else if (s->token.val == '[') {
  24951                 goto parse_array_access;
  24952             } else {
  24953                 goto parse_property;
  24954             }
  24955         } else if (s->token.val == TOK_TEMPLATE &&
  24956                    call_type == FUNC_CALL_NORMAL) {
  24957             if (optional_chaining_label >= 0) {
  24958                 return js_parse_error(s, "template literal cannot appear in an optional chain");
  24959             }
  24960             call_type = FUNC_CALL_TEMPLATE;
  24961             goto parse_func_call2;
  24962         } else if (s->token.val == '(' && accept_lparen) {
  24963             int opcode, arg_count, drop_count;
  24964 
  24965             /* function call */
  24966         parse_func_call:
  24967             if (next_token(s))
  24968                 return -1;
  24969 
  24970             if (call_type == FUNC_CALL_NORMAL) {
  24971             parse_func_call2:
  24972                 switch(opcode = get_prev_opcode(fd)) {
  24973                 case OP_get_field:
  24974                     /* keep the object on the stack */
  24975                     fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2;
  24976                     drop_count = 2;
  24977                     break;
  24978                 case OP_get_field_opt_chain:
  24979                     {
  24980                         int opt_chain_label, next_label;
  24981                         opt_chain_label = get_u32(fd->byte_code.buf +
  24982                                                   fd->last_opcode_pos + 1 + 4 + 1);
  24983                         /* keep the object on the stack */
  24984                         fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2;
  24985                         fd->byte_code.size = fd->last_opcode_pos + 1 + 4;
  24986                         next_label = emit_goto(s, OP_goto, -1);
  24987                         emit_label(s, opt_chain_label);
  24988                         /* need an additional undefined value for the
  24989                            case where the optional field does not
  24990                            exists */
  24991                         emit_op(s, OP_undefined);
  24992                         emit_label(s, next_label);
  24993                         drop_count = 2;
  24994                         opcode = OP_get_field;
  24995                     }
  24996                     break;
  24997                 case OP_scope_get_private_field:
  24998                     /* keep the object on the stack */
  24999                     fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_private_field2;
  25000                     drop_count = 2;
  25001                     break;
  25002                 case OP_get_array_el:
  25003                     /* keep the object on the stack */
  25004                     fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2;
  25005                     drop_count = 2;
  25006                     break;
  25007                 case OP_get_array_el_opt_chain:
  25008                     {
  25009                         int opt_chain_label, next_label;
  25010                         opt_chain_label = get_u32(fd->byte_code.buf +
  25011                                                   fd->last_opcode_pos + 1 + 1);
  25012                         /* keep the object on the stack */
  25013                         fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2;
  25014                         fd->byte_code.size = fd->last_opcode_pos + 1;
  25015                         next_label = emit_goto(s, OP_goto, -1);
  25016                         emit_label(s, opt_chain_label);
  25017                         /* need an additional undefined value for the
  25018                            case where the optional field does not
  25019                            exists */
  25020                         emit_op(s, OP_undefined);
  25021                         emit_label(s, next_label);
  25022                         drop_count = 2;
  25023                         opcode = OP_get_array_el;
  25024                     }
  25025                     break;
  25026                 case OP_scope_get_var:
  25027                     {
  25028                         JSAtom name;
  25029                         int scope;
  25030                         name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
  25031                         scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);
  25032                         if (name == JS_ATOM_eval && call_type == FUNC_CALL_NORMAL && !has_optional_chain) {
  25033                             /* direct 'eval' */
  25034                             opcode = OP_eval;
  25035                         } else {
  25036                             /* verify if function name resolves to a simple
  25037                                get_loc/get_arg: a function call inside a `with`
  25038                                statement can resolve to a method call of the
  25039                                `with` context object
  25040                              */
  25041                             /* XXX: always generate the OP_scope_get_ref
  25042                                and remove it in variable resolution
  25043                                pass ? */
  25044                             if (has_with_scope(fd, scope)) {
  25045                                 opcode = OP_scope_get_ref;
  25046                                 fd->byte_code.buf[fd->last_opcode_pos] = opcode;
  25047                             }
  25048                         }
  25049                         drop_count = 1;
  25050                     }
  25051                     break;
  25052                 case OP_get_super_value:
  25053                     fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el;
  25054                     /* on stack: this func_obj */
  25055                     opcode = OP_get_array_el;
  25056                     drop_count = 2;
  25057                     break;
  25058                 default:
  25059                     opcode = OP_invalid;
  25060                     drop_count = 1;
  25061                     break;
  25062                 }
  25063                 if (has_optional_chain) {
  25064                     optional_chain_test(s, &optional_chaining_label,
  25065                                         drop_count);
  25066                 }
  25067             } else {
  25068                 opcode = OP_invalid;
  25069             }
  25070 
  25071             if (call_type == FUNC_CALL_TEMPLATE) {
  25072                 if (js_parse_template(s, 1, &arg_count))
  25073                     return -1;
  25074                 goto emit_func_call;
  25075             } else if (call_type == FUNC_CALL_SUPER_CTOR) {
  25076                 emit_op(s, OP_scope_get_var);
  25077                 emit_atom(s, JS_ATOM_this_active_func);
  25078                 emit_u16(s, 0);
  25079 
  25080                 emit_op(s, OP_get_super);
  25081 
  25082                 emit_op(s, OP_scope_get_var);
  25083                 emit_atom(s, JS_ATOM_new_target);
  25084                 emit_u16(s, 0);
  25085             } else if (call_type == FUNC_CALL_NEW) {
  25086                 emit_op(s, OP_dup); /* new.target = function */
  25087             }
  25088 
  25089             /* parse arguments */
  25090             arg_count = 0;
  25091             while (s->token.val != ')') {
  25092                 if (arg_count >= 65535) {
  25093                     return js_parse_error(s, "Too many call arguments");
  25094                 }
  25095                 if (s->token.val == TOK_ELLIPSIS)
  25096                     break;
  25097                 if (js_parse_assign_expr(s))
  25098                     return -1;
  25099                 arg_count++;
  25100                 if (s->token.val == ')')
  25101                     break;
  25102                 /* accept a trailing comma before the ')' */
  25103                 if (js_parse_expect(s, ','))
  25104                     return -1;
  25105             }
  25106             if (s->token.val == TOK_ELLIPSIS) {
  25107                 emit_op(s, OP_array_from);
  25108                 emit_u16(s, arg_count);
  25109                 emit_op(s, OP_push_i32);
  25110                 emit_u32(s, arg_count);
  25111 
  25112                 /* on stack: array idx */
  25113                 while (s->token.val != ')') {
  25114                     if (s->token.val == TOK_ELLIPSIS) {
  25115                         if (next_token(s))
  25116                             return -1;
  25117                         if (js_parse_assign_expr(s))
  25118                             return -1;
  25119 #if 1
  25120                         /* XXX: could pass is_last indicator? */
  25121                         emit_op(s, OP_append);
  25122 #else
  25123                         int label_next, label_done;
  25124                         label_next = new_label(s);
  25125                         label_done = new_label(s);
  25126                         /* push enumerate object below array/idx pair */
  25127                         emit_op(s, OP_for_of_start);
  25128                         emit_op(s, OP_rot5l);
  25129                         emit_op(s, OP_rot5l);
  25130                         emit_label(s, label_next);
  25131                         /* on stack: enum_rec array idx */
  25132                         emit_op(s, OP_for_of_next);
  25133                         emit_u8(s, 2);
  25134                         emit_goto(s, OP_if_true, label_done);
  25135                         /* append element */
  25136                         /* enum_rec array idx val -> enum_rec array new_idx */
  25137                         emit_op(s, OP_define_array_el);
  25138                         emit_op(s, OP_inc);
  25139                         emit_goto(s, OP_goto, label_next);
  25140                         emit_label(s, label_done);
  25141                         /* close enumeration, drop enum_rec and idx */
  25142                         emit_op(s, OP_drop); /* drop undef */
  25143                         emit_op(s, OP_nip1); /* drop enum_rec */
  25144                         emit_op(s, OP_nip1);
  25145                         emit_op(s, OP_nip1);
  25146 #endif
  25147                     } else {
  25148                         if (js_parse_assign_expr(s))
  25149                             return -1;
  25150                         /* array idx val */
  25151                         emit_op(s, OP_define_array_el);
  25152                         emit_op(s, OP_inc);
  25153                     }
  25154                     if (s->token.val == ')')
  25155                         break;
  25156                     /* accept a trailing comma before the ')' */
  25157                     if (js_parse_expect(s, ','))
  25158                         return -1;
  25159                 }
  25160                 if (next_token(s))
  25161                     return -1;
  25162                 /* drop the index */
  25163                 emit_op(s, OP_drop);
  25164 
  25165                 /* apply function call */
  25166                 switch(opcode) {
  25167                 case OP_get_field:
  25168                 case OP_scope_get_private_field:
  25169                 case OP_get_array_el:
  25170                 case OP_scope_get_ref:
  25171                     /* obj func array -> func obj array */
  25172                     emit_op(s, OP_perm3);
  25173                     emit_op(s, OP_apply);
  25174                     emit_u16(s, call_type == FUNC_CALL_NEW);
  25175                     break;
  25176                 case OP_eval:
  25177                     emit_op(s, OP_apply_eval);
  25178                     emit_u16(s, fd->scope_level);
  25179                     fd->has_eval_call = TRUE;
  25180                     break;
  25181                 default:
  25182                     if (call_type == FUNC_CALL_SUPER_CTOR) {
  25183                         emit_op(s, OP_apply);
  25184                         emit_u16(s, 1);
  25185                         /* set the 'this' value */
  25186                         emit_op(s, OP_dup);
  25187                         emit_op(s, OP_scope_put_var_init);
  25188                         emit_atom(s, JS_ATOM_this);
  25189                         emit_u16(s, 0);
  25190 
  25191                         emit_class_field_init(s);
  25192                     } else if (call_type == FUNC_CALL_NEW) {
  25193                         /* obj func array -> func obj array */
  25194                         emit_op(s, OP_perm3);
  25195                         emit_op(s, OP_apply);
  25196                         emit_u16(s, 1);
  25197                     } else {
  25198                         /* func array -> func undef array */
  25199                         emit_op(s, OP_undefined);
  25200                         emit_op(s, OP_swap);
  25201                         emit_op(s, OP_apply);
  25202                         emit_u16(s, 0);
  25203                     }
  25204                     break;
  25205                 }
  25206             } else {
  25207                 if (next_token(s))
  25208                     return -1;
  25209             emit_func_call:
  25210                 switch(opcode) {
  25211                 case OP_get_field:
  25212                 case OP_scope_get_private_field:
  25213                 case OP_get_array_el:
  25214                 case OP_scope_get_ref:
  25215                     emit_op(s, OP_call_method);
  25216                     emit_u16(s, arg_count);
  25217                     break;
  25218                 case OP_eval:
  25219                     emit_op(s, OP_eval);
  25220                     emit_u16(s, arg_count);
  25221                     emit_u16(s, fd->scope_level);
  25222                     fd->has_eval_call = TRUE;
  25223                     break;
  25224                 default:
  25225                     if (call_type == FUNC_CALL_SUPER_CTOR) {
  25226                         emit_op(s, OP_call_constructor);
  25227                         emit_u16(s, arg_count);
  25228 
  25229                         /* set the 'this' value */
  25230                         emit_op(s, OP_dup);
  25231                         emit_op(s, OP_scope_put_var_init);
  25232                         emit_atom(s, JS_ATOM_this);
  25233                         emit_u16(s, 0);
  25234 
  25235                         emit_class_field_init(s);
  25236                     } else if (call_type == FUNC_CALL_NEW) {
  25237                         emit_op(s, OP_call_constructor);
  25238                         emit_u16(s, arg_count);
  25239                     } else {
  25240                         emit_op(s, OP_call);
  25241                         emit_u16(s, arg_count);
  25242                     }
  25243                     break;
  25244                 }
  25245             }
  25246             call_type = FUNC_CALL_NORMAL;
  25247         } else if (s->token.val == '.') {
  25248             if (next_token(s))
  25249                 return -1;
  25250         parse_property:
  25251             if (s->token.val == TOK_PRIVATE_NAME) {
  25252                 /* private class field */
  25253                 if (get_prev_opcode(fd) == OP_get_super) {
  25254                     return js_parse_error(s, "private class field forbidden after super");
  25255                 }
  25256                 if (has_optional_chain) {
  25257                     optional_chain_test(s, &optional_chaining_label, 1);
  25258                 }
  25259                 emit_op(s, OP_scope_get_private_field);
  25260                 emit_atom(s, s->token.u.ident.atom);
  25261                 emit_u16(s, s->cur_func->scope_level);
  25262             } else {
  25263                 if (!token_is_ident(s->token.val)) {
  25264                     return js_parse_error(s, "expecting field name");
  25265                 }
  25266                 if (get_prev_opcode(fd) == OP_get_super) {
  25267                     JSValue val;
  25268                     int ret;
  25269                     val = JS_AtomToValue(s->ctx, s->token.u.ident.atom);
  25270                     ret = emit_push_const(s, val, 1);
  25271                     JS_FreeValue(s->ctx, val);
  25272                     if (ret)
  25273                         return -1;
  25274                     emit_op(s, OP_get_super_value);
  25275                 } else {
  25276                     if (has_optional_chain) {
  25277                         optional_chain_test(s, &optional_chaining_label, 1);
  25278                     }
  25279                     emit_op(s, OP_get_field);
  25280                     emit_atom(s, s->token.u.ident.atom);
  25281                 }
  25282             }
  25283             if (next_token(s))
  25284                 return -1;
  25285         } else if (s->token.val == '[') {
  25286             int prev_op;
  25287 
  25288         parse_array_access:
  25289             prev_op = get_prev_opcode(fd);
  25290             if (has_optional_chain) {
  25291                 optional_chain_test(s, &optional_chaining_label, 1);
  25292             }
  25293             if (next_token(s))
  25294                 return -1;
  25295             if (js_parse_expr(s))
  25296                 return -1;
  25297             if (js_parse_expect(s, ']'))
  25298                 return -1;
  25299             if (prev_op == OP_get_super) {
  25300                 emit_op(s, OP_get_super_value);
  25301             } else {
  25302                 emit_op(s, OP_get_array_el);
  25303             }
  25304         } else {
  25305             break;
  25306         }
  25307     }
  25308     if (optional_chaining_label >= 0) {
  25309         JSFunctionDef *fd = s->cur_func;
  25310         int opcode;
  25311         emit_label_raw(s, optional_chaining_label);
  25312         /* modify the last opcode so that it is an indicator of an
  25313            optional chain */
  25314         opcode = get_prev_opcode(fd);
  25315         if (opcode == OP_get_field || opcode == OP_get_array_el) {
  25316             if (opcode == OP_get_field)
  25317                 opcode = OP_get_field_opt_chain;
  25318             else
  25319                 opcode = OP_get_array_el_opt_chain;
  25320             fd->byte_code.buf[fd->last_opcode_pos] = opcode;
  25321         } else {
  25322             fd->last_opcode_pos = -1;
  25323         }
  25324     }
  25325     return 0;
  25326 }
  25327 
  25328 static __exception int js_parse_delete(JSParseState *s)
  25329 {
  25330     JSFunctionDef *fd = s->cur_func;
  25331     JSAtom name;
  25332     int opcode;
  25333 
  25334     if (next_token(s))
  25335         return -1;
  25336     if (js_parse_unary(s, PF_POW_FORBIDDEN))
  25337         return -1;
  25338     switch(opcode = get_prev_opcode(fd)) {
  25339     case OP_get_field:
  25340     case OP_get_field_opt_chain:
  25341         {
  25342             JSValue val;
  25343             int ret, opt_chain_label, next_label;
  25344             if (opcode == OP_get_field_opt_chain) {
  25345                 opt_chain_label = get_u32(fd->byte_code.buf +
  25346                                           fd->last_opcode_pos + 1 + 4 + 1);
  25347             } else {
  25348                 opt_chain_label = -1;
  25349             }
  25350             name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
  25351             fd->byte_code.size = fd->last_opcode_pos;
  25352             val = JS_AtomToValue(s->ctx, name);
  25353             ret = emit_push_const(s, val, 1);
  25354             JS_FreeValue(s->ctx, val);
  25355             JS_FreeAtom(s->ctx, name);
  25356             if (ret)
  25357                 return ret;
  25358             emit_op(s, OP_delete);
  25359             if (opt_chain_label >= 0) {
  25360                 next_label = emit_goto(s, OP_goto, -1);
  25361                 emit_label(s, opt_chain_label);
  25362                 /* if the optional chain is not taken, return 'true' */
  25363                 emit_op(s, OP_drop);
  25364                 emit_op(s, OP_push_true);
  25365                 emit_label(s, next_label);
  25366             }
  25367             fd->last_opcode_pos = -1;
  25368         }
  25369         break;
  25370     case OP_get_array_el:
  25371         fd->byte_code.size = fd->last_opcode_pos;
  25372         fd->last_opcode_pos = -1;
  25373         emit_op(s, OP_delete);
  25374         break;
  25375     case OP_get_array_el_opt_chain:
  25376         {
  25377             int opt_chain_label, next_label;
  25378             opt_chain_label = get_u32(fd->byte_code.buf +
  25379                                       fd->last_opcode_pos + 1 + 1);
  25380             fd->byte_code.size = fd->last_opcode_pos;
  25381             emit_op(s, OP_delete);
  25382             next_label = emit_goto(s, OP_goto, -1);
  25383             emit_label(s, opt_chain_label);
  25384             /* if the optional chain is not taken, return 'true' */
  25385             emit_op(s, OP_drop);
  25386             emit_op(s, OP_push_true);
  25387             emit_label(s, next_label);
  25388             fd->last_opcode_pos = -1;
  25389         }
  25390         break;
  25391     case OP_scope_get_var:
  25392         /* 'delete this': this is not a reference */
  25393         name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
  25394         if (name == JS_ATOM_this || name == JS_ATOM_new_target)
  25395             goto ret_true;
  25396         if (fd->js_mode & JS_MODE_STRICT) {
  25397             return js_parse_error(s, "cannot delete a direct reference in strict mode");
  25398         } else {
  25399             fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_delete_var;
  25400         }
  25401         break;
  25402     case OP_scope_get_private_field:
  25403         return js_parse_error(s, "cannot delete a private class field");
  25404     case OP_get_super_value:
  25405         fd->byte_code.size = fd->last_opcode_pos;
  25406         fd->last_opcode_pos = -1;
  25407         emit_op(s, OP_throw_error);
  25408         emit_atom(s, JS_ATOM_NULL);
  25409         emit_u8(s, JS_THROW_ERROR_DELETE_SUPER);
  25410         break;
  25411     default:
  25412     ret_true:
  25413         emit_op(s, OP_drop);
  25414         emit_op(s, OP_push_true);
  25415         break;
  25416     }
  25417     return 0;
  25418 }
  25419 
  25420 /* allowed parse_flags: PF_POW_ALLOWED, PF_POW_FORBIDDEN */
  25421 static __exception int js_parse_unary(JSParseState *s, int parse_flags)
  25422 {
  25423     int op;
  25424 
  25425     switch(s->token.val) {
  25426     case '+':
  25427     case '-':
  25428     case '!':
  25429     case '~':
  25430     case TOK_VOID:
  25431         op = s->token.val;
  25432         if (next_token(s))
  25433             return -1;
  25434         if (js_parse_unary(s, PF_POW_FORBIDDEN))
  25435             return -1;
  25436         switch(op) {
  25437         case '-':
  25438             emit_op(s, OP_neg);
  25439             break;
  25440         case '+':
  25441             emit_op(s, OP_plus);
  25442             break;
  25443         case '!':
  25444             emit_op(s, OP_lnot);
  25445             break;
  25446         case '~':
  25447             emit_op(s, OP_not);
  25448             break;
  25449         case TOK_VOID:
  25450             emit_op(s, OP_drop);
  25451             emit_op(s, OP_undefined);
  25452             break;
  25453         default:
  25454             abort();
  25455         }
  25456         parse_flags = 0;
  25457         break;
  25458     case TOK_DEC:
  25459     case TOK_INC:
  25460         {
  25461             int opcode, op, scope, label;
  25462             JSAtom name;
  25463             op = s->token.val;
  25464             if (next_token(s))
  25465                 return -1;
  25466             if (js_parse_unary(s, 0))
  25467                 return -1;
  25468             if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op))
  25469                 return -1;
  25470             emit_op(s, OP_dec + op - TOK_DEC);
  25471             put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP,
  25472                        FALSE);
  25473         }
  25474         break;
  25475     case TOK_TYPEOF:
  25476         {
  25477             JSFunctionDef *fd;
  25478             if (next_token(s))
  25479                 return -1;
  25480             if (js_parse_unary(s, PF_POW_FORBIDDEN))
  25481                 return -1;
  25482             /* reference access should not return an exception, so we
  25483                patch the get_var */
  25484             fd = s->cur_func;
  25485             if (get_prev_opcode(fd) == OP_scope_get_var) {
  25486                 fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_var_undef;
  25487             }
  25488             emit_op(s, OP_typeof);
  25489             parse_flags = 0;
  25490         }
  25491         break;
  25492     case TOK_DELETE:
  25493         if (js_parse_delete(s))
  25494             return -1;
  25495         parse_flags = 0;
  25496         break;
  25497     case TOK_AWAIT:
  25498         if (!(s->cur_func->func_kind & JS_FUNC_ASYNC))
  25499             return js_parse_error(s, "unexpected 'await' keyword");
  25500         if (!s->cur_func->in_function_body)
  25501             return js_parse_error(s, "await in default expression");
  25502         if (next_token(s))
  25503             return -1;
  25504         if (js_parse_unary(s, PF_POW_FORBIDDEN))
  25505             return -1;
  25506         s->cur_func->has_await = TRUE;
  25507         emit_op(s, OP_await);
  25508         parse_flags = 0;
  25509         break;
  25510     default:
  25511         if (js_parse_postfix_expr(s, PF_POSTFIX_CALL))
  25512             return -1;
  25513         if (!s->got_lf &&
  25514             (s->token.val == TOK_DEC || s->token.val == TOK_INC)) {
  25515             int opcode, op, scope, label;
  25516             JSAtom name;
  25517             op = s->token.val;
  25518             if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op))
  25519                 return -1;
  25520             emit_op(s, OP_post_dec + op - TOK_DEC);
  25521             put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_SECOND,
  25522                        FALSE);
  25523             if (next_token(s))
  25524                 return -1;
  25525         }
  25526         break;
  25527     }
  25528     if (parse_flags & (PF_POW_ALLOWED | PF_POW_FORBIDDEN)) {
  25529 #ifdef CONFIG_BIGNUM
  25530         if (s->token.val == TOK_POW || s->token.val == TOK_MATH_POW) {
  25531             /* Extended exponentiation syntax rules: we extend the ES7
  25532                grammar in order to have more intuitive semantics:
  25533                -2**2 evaluates to -4. */
  25534             if (!(s->cur_func->js_mode & JS_MODE_MATH)) {
  25535                 if (parse_flags & PF_POW_FORBIDDEN) {
  25536                     JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'");
  25537                     return -1;
  25538                 }
  25539             }
  25540             if (next_token(s))
  25541                 return -1;
  25542             if (js_parse_unary(s, PF_POW_ALLOWED))
  25543                 return -1;
  25544             emit_op(s, OP_pow);
  25545         }
  25546 #else
  25547         if (s->token.val == TOK_POW) {
  25548             /* Strict ES7 exponentiation syntax rules: To solve
  25549                conficting semantics between different implementations
  25550                regarding the precedence of prefix operators and the
  25551                postifx exponential, ES7 specifies that -2**2 is a
  25552                syntax error. */
  25553             if (parse_flags & PF_POW_FORBIDDEN) {
  25554                 JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'");
  25555                 return -1;
  25556             }
  25557             if (next_token(s))
  25558                 return -1;
  25559             if (js_parse_unary(s, PF_POW_ALLOWED))
  25560                 return -1;
  25561             emit_op(s, OP_pow);
  25562         }
  25563 #endif
  25564     }
  25565     return 0;
  25566 }
  25567 
  25568 /* allowed parse_flags: PF_IN_ACCEPTED */
  25569 static __exception int js_parse_expr_binary(JSParseState *s, int level,
  25570                                             int parse_flags)
  25571 {
  25572     int op, opcode;
  25573 
  25574     if (level == 0) {
  25575         return js_parse_unary(s, PF_POW_ALLOWED);
  25576     } else if (s->token.val == TOK_PRIVATE_NAME &&
  25577                (parse_flags & PF_IN_ACCEPTED) && level == 4 &&
  25578                peek_token(s, FALSE) == TOK_IN) {
  25579         JSAtom atom;
  25580 
  25581         atom = JS_DupAtom(s->ctx, s->token.u.ident.atom);
  25582         if (next_token(s))
  25583             goto fail_private_in;
  25584         if (s->token.val != TOK_IN)
  25585             goto fail_private_in;
  25586         if (next_token(s))
  25587             goto fail_private_in;
  25588         if (js_parse_expr_binary(s, level - 1, parse_flags)) {
  25589         fail_private_in:
  25590             JS_FreeAtom(s->ctx, atom);
  25591             return -1;
  25592         }
  25593         emit_op(s, OP_scope_in_private_field);
  25594         emit_atom(s, atom);
  25595         emit_u16(s, s->cur_func->scope_level);
  25596         JS_FreeAtom(s->ctx, atom);
  25597         return 0;
  25598     } else {
  25599         if (js_parse_expr_binary(s, level - 1, parse_flags))
  25600             return -1;
  25601     }
  25602     for(;;) {
  25603         op = s->token.val;
  25604         switch(level) {
  25605         case 1:
  25606             switch(op) {
  25607             case '*':
  25608                 opcode = OP_mul;
  25609                 break;
  25610             case '/':
  25611                 opcode = OP_div;
  25612                 break;
  25613             case '%':
  25614 #ifdef CONFIG_BIGNUM
  25615                 if (s->cur_func->js_mode & JS_MODE_MATH)
  25616                     opcode = OP_math_mod;
  25617                 else
  25618 #endif
  25619                     opcode = OP_mod;
  25620                 break;
  25621             default:
  25622                 return 0;
  25623             }
  25624             break;
  25625         case 2:
  25626             switch(op) {
  25627             case '+':
  25628                 opcode = OP_add;
  25629                 break;
  25630             case '-':
  25631                 opcode = OP_sub;
  25632                 break;
  25633             default:
  25634                 return 0;
  25635             }
  25636             break;
  25637         case 3:
  25638             switch(op) {
  25639             case TOK_SHL:
  25640                 opcode = OP_shl;
  25641                 break;
  25642             case TOK_SAR:
  25643                 opcode = OP_sar;
  25644                 break;
  25645             case TOK_SHR:
  25646                 opcode = OP_shr;
  25647                 break;
  25648             default:
  25649                 return 0;
  25650             }
  25651             break;
  25652         case 4:
  25653             switch(op) {
  25654             case '<':
  25655                 opcode = OP_lt;
  25656                 break;
  25657             case '>':
  25658                 opcode = OP_gt;
  25659                 break;
  25660             case TOK_LTE:
  25661                 opcode = OP_lte;
  25662                 break;
  25663             case TOK_GTE:
  25664                 opcode = OP_gte;
  25665                 break;
  25666             case TOK_INSTANCEOF:
  25667                 opcode = OP_instanceof;
  25668                 break;
  25669             case TOK_IN:
  25670                 if (parse_flags & PF_IN_ACCEPTED) {
  25671                     opcode = OP_in;
  25672                 } else {
  25673                     return 0;
  25674                 }
  25675                 break;
  25676             default:
  25677                 return 0;
  25678             }
  25679             break;
  25680         case 5:
  25681             switch(op) {
  25682             case TOK_EQ:
  25683                 opcode = OP_eq;
  25684                 break;
  25685             case TOK_NEQ:
  25686                 opcode = OP_neq;
  25687                 break;
  25688             case TOK_STRICT_EQ:
  25689                 opcode = OP_strict_eq;
  25690                 break;
  25691             case TOK_STRICT_NEQ:
  25692                 opcode = OP_strict_neq;
  25693                 break;
  25694             default:
  25695                 return 0;
  25696             }
  25697             break;
  25698         case 6:
  25699             switch(op) {
  25700             case '&':
  25701                 opcode = OP_and;
  25702                 break;
  25703             default:
  25704                 return 0;
  25705             }
  25706             break;
  25707         case 7:
  25708             switch(op) {
  25709             case '^':
  25710                 opcode = OP_xor;
  25711                 break;
  25712             default:
  25713                 return 0;
  25714             }
  25715             break;
  25716         case 8:
  25717             switch(op) {
  25718             case '|':
  25719                 opcode = OP_or;
  25720                 break;
  25721             default:
  25722                 return 0;
  25723             }
  25724             break;
  25725         default:
  25726             abort();
  25727         }
  25728         if (next_token(s))
  25729             return -1;
  25730         if (js_parse_expr_binary(s, level - 1, parse_flags))
  25731             return -1;
  25732         emit_op(s, opcode);
  25733     }
  25734     return 0;
  25735 }
  25736 
  25737 /* allowed parse_flags: PF_IN_ACCEPTED */
  25738 static __exception int js_parse_logical_and_or(JSParseState *s, int op,
  25739                                                int parse_flags)
  25740 {
  25741     int label1;
  25742 
  25743     if (op == TOK_LAND) {
  25744         if (js_parse_expr_binary(s, 8, parse_flags))
  25745             return -1;
  25746     } else {
  25747         if (js_parse_logical_and_or(s, TOK_LAND, parse_flags))
  25748             return -1;
  25749     }
  25750     if (s->token.val == op) {
  25751         label1 = new_label(s);
  25752 
  25753         for(;;) {
  25754             if (next_token(s))
  25755                 return -1;
  25756             emit_op(s, OP_dup);
  25757             emit_goto(s, op == TOK_LAND ? OP_if_false : OP_if_true, label1);
  25758             emit_op(s, OP_drop);
  25759 
  25760             if (op == TOK_LAND) {
  25761                 if (js_parse_expr_binary(s, 8, parse_flags))
  25762                     return -1;
  25763             } else {
  25764                 if (js_parse_logical_and_or(s, TOK_LAND,
  25765                                             parse_flags))
  25766                     return -1;
  25767             }
  25768             if (s->token.val != op) {
  25769                 if (s->token.val == TOK_DOUBLE_QUESTION_MARK)
  25770                     return js_parse_error(s, "cannot mix ?? with && or ||");
  25771                 break;
  25772             }
  25773         }
  25774 
  25775         emit_label(s, label1);
  25776     }
  25777     return 0;
  25778 }
  25779 
  25780 static __exception int js_parse_coalesce_expr(JSParseState *s, int parse_flags)
  25781 {
  25782     int label1;
  25783 
  25784     if (js_parse_logical_and_or(s, TOK_LOR, parse_flags))
  25785         return -1;
  25786     if (s->token.val == TOK_DOUBLE_QUESTION_MARK) {
  25787         label1 = new_label(s);
  25788         for(;;) {
  25789             if (next_token(s))
  25790                 return -1;
  25791 
  25792             emit_op(s, OP_dup);
  25793             emit_op(s, OP_is_undefined_or_null);
  25794             emit_goto(s, OP_if_false, label1);
  25795             emit_op(s, OP_drop);
  25796 
  25797             if (js_parse_expr_binary(s, 8, parse_flags))
  25798                 return -1;
  25799             if (s->token.val != TOK_DOUBLE_QUESTION_MARK)
  25800                 break;
  25801         }
  25802         emit_label(s, label1);
  25803     }
  25804     return 0;
  25805 }
  25806 
  25807 /* allowed parse_flags: PF_IN_ACCEPTED */
  25808 static __exception int js_parse_cond_expr(JSParseState *s, int parse_flags)
  25809 {
  25810     int label1, label2;
  25811 
  25812     if (js_parse_coalesce_expr(s, parse_flags))
  25813         return -1;
  25814     if (s->token.val == '?') {
  25815         if (next_token(s))
  25816             return -1;
  25817         label1 = emit_goto(s, OP_if_false, -1);
  25818 
  25819         if (js_parse_assign_expr(s))
  25820             return -1;
  25821         if (js_parse_expect(s, ':'))
  25822             return -1;
  25823 
  25824         label2 = emit_goto(s, OP_goto, -1);
  25825 
  25826         emit_label(s, label1);
  25827 
  25828         if (js_parse_assign_expr2(s, parse_flags & PF_IN_ACCEPTED))
  25829             return -1;
  25830 
  25831         emit_label(s, label2);
  25832     }
  25833     return 0;
  25834 }
  25835 
  25836 static void emit_return(JSParseState *s, BOOL hasval);
  25837 
  25838 /* allowed parse_flags: PF_IN_ACCEPTED */
  25839 static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags)
  25840 {
  25841     int opcode, op, scope;
  25842     JSAtom name0 = JS_ATOM_NULL;
  25843     JSAtom name;
  25844 
  25845     if (s->token.val == TOK_YIELD) {
  25846         BOOL is_star = FALSE, is_async;
  25847 
  25848         if (!(s->cur_func->func_kind & JS_FUNC_GENERATOR))
  25849             return js_parse_error(s, "unexpected 'yield' keyword");
  25850         if (!s->cur_func->in_function_body)
  25851             return js_parse_error(s, "yield in default expression");
  25852         if (next_token(s))
  25853             return -1;
  25854         /* XXX: is there a better method to detect 'yield' without
  25855            parameters ? */
  25856         if (s->token.val != ';' && s->token.val != ')' &&
  25857             s->token.val != ']' && s->token.val != '}' &&
  25858             s->token.val != ',' && s->token.val != ':' && !s->got_lf) {
  25859             if (s->token.val == '*') {
  25860                 is_star = TRUE;
  25861                 if (next_token(s))
  25862                     return -1;
  25863             }
  25864             if (js_parse_assign_expr2(s, parse_flags))
  25865                 return -1;
  25866         } else {
  25867             emit_op(s, OP_undefined);
  25868         }
  25869         is_async = (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR);
  25870 
  25871         if (is_star) {
  25872             int label_loop, label_return, label_next;
  25873             int label_return1, label_yield, label_throw, label_throw1;
  25874             int label_throw2;
  25875 
  25876             label_loop = new_label(s);
  25877             label_yield = new_label(s);
  25878 
  25879             emit_op(s, is_async ? OP_for_await_of_start : OP_for_of_start);
  25880 
  25881             /* remove the catch offset (XXX: could avoid pushing back
  25882                undefined) */
  25883             emit_op(s, OP_drop);
  25884             emit_op(s, OP_undefined);
  25885 
  25886             emit_op(s, OP_undefined); /* initial value */
  25887 
  25888             emit_label(s, label_loop);
  25889             emit_op(s, OP_iterator_next);
  25890             if (is_async)
  25891                 emit_op(s, OP_await);
  25892             emit_op(s, OP_iterator_check_object);
  25893             emit_op(s, OP_get_field2);
  25894             emit_atom(s, JS_ATOM_done);
  25895             label_next = emit_goto(s, OP_if_true, -1); /* end of loop */
  25896             emit_label(s, label_yield);
  25897             if (is_async) {
  25898                 /* OP_async_yield_star takes the value as parameter */
  25899                 emit_op(s, OP_get_field);
  25900                 emit_atom(s, JS_ATOM_value);
  25901                 emit_op(s, OP_async_yield_star);
  25902             } else {
  25903                 /* OP_yield_star takes (value, done) as parameter */
  25904                 emit_op(s, OP_yield_star);
  25905             }
  25906             emit_op(s, OP_dup);
  25907             label_return = emit_goto(s, OP_if_true, -1);
  25908             emit_op(s, OP_drop);
  25909             emit_goto(s, OP_goto, label_loop);
  25910 
  25911             emit_label(s, label_return);
  25912             emit_op(s, OP_push_i32);
  25913             emit_u32(s, 2);
  25914             emit_op(s, OP_strict_eq);
  25915             label_throw = emit_goto(s, OP_if_true, -1);
  25916 
  25917             /* return handling */
  25918             if (is_async)
  25919                 emit_op(s, OP_await);
  25920             emit_op(s, OP_iterator_call);
  25921             emit_u8(s, 0);
  25922             label_return1 = emit_goto(s, OP_if_true, -1);
  25923             if (is_async)
  25924                 emit_op(s, OP_await);
  25925             emit_op(s, OP_iterator_check_object);
  25926             emit_op(s, OP_get_field2);
  25927             emit_atom(s, JS_ATOM_done);
  25928             emit_goto(s, OP_if_false, label_yield);
  25929 
  25930             emit_op(s, OP_get_field);
  25931             emit_atom(s, JS_ATOM_value);
  25932 
  25933             emit_label(s, label_return1);
  25934             emit_op(s, OP_nip);
  25935             emit_op(s, OP_nip);
  25936             emit_op(s, OP_nip);
  25937             emit_return(s, TRUE);
  25938 
  25939             /* throw handling */
  25940             emit_label(s, label_throw);
  25941             emit_op(s, OP_iterator_call);
  25942             emit_u8(s, 1);
  25943             label_throw1 = emit_goto(s, OP_if_true, -1);
  25944             if (is_async)
  25945                 emit_op(s, OP_await);
  25946             emit_op(s, OP_iterator_check_object);
  25947             emit_op(s, OP_get_field2);
  25948             emit_atom(s, JS_ATOM_done);
  25949             emit_goto(s, OP_if_false, label_yield);
  25950             emit_goto(s, OP_goto, label_next);
  25951             /* close the iterator and throw a type error exception */
  25952             emit_label(s, label_throw1);
  25953             emit_op(s, OP_iterator_call);
  25954             emit_u8(s, 2);
  25955             label_throw2 = emit_goto(s, OP_if_true, -1);
  25956             if (is_async)
  25957                 emit_op(s, OP_await);
  25958             emit_label(s, label_throw2);
  25959 
  25960             emit_op(s, OP_throw_error);
  25961             emit_atom(s, JS_ATOM_NULL);
  25962             emit_u8(s, JS_THROW_ERROR_ITERATOR_THROW);
  25963 
  25964             emit_label(s, label_next);
  25965             emit_op(s, OP_get_field);
  25966             emit_atom(s, JS_ATOM_value);
  25967             emit_op(s, OP_nip); /* keep the value associated with
  25968                                    done = true */
  25969             emit_op(s, OP_nip);
  25970             emit_op(s, OP_nip);
  25971         } else {
  25972             int label_next;
  25973 
  25974             if (is_async)
  25975                 emit_op(s, OP_await);
  25976             emit_op(s, OP_yield);
  25977             label_next = emit_goto(s, OP_if_false, -1);
  25978             emit_return(s, TRUE);
  25979             emit_label(s, label_next);
  25980         }
  25981         return 0;
  25982     } else if (s->token.val == '(' &&
  25983                js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) {
  25984         return js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,
  25985                                       JS_FUNC_NORMAL, JS_ATOM_NULL,
  25986                                       s->token.ptr, s->token.line_num);
  25987     } else if (token_is_pseudo_keyword(s, JS_ATOM_async)) {
  25988         const uint8_t *source_ptr;
  25989         int source_line_num, tok;
  25990         JSParsePos pos;
  25991 
  25992         /* fast test */
  25993         tok = peek_token(s, TRUE);
  25994         if (tok == TOK_FUNCTION || tok == '\n')
  25995             goto next;
  25996 
  25997         source_ptr = s->token.ptr;
  25998         source_line_num = s->token.line_num;
  25999         js_parse_get_pos(s, &pos);
  26000         if (next_token(s))
  26001             return -1;
  26002         if ((s->token.val == '(' &&
  26003              js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) ||
  26004             (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved &&
  26005              peek_token(s, TRUE) == TOK_ARROW)) {
  26006             return js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,
  26007                                           JS_FUNC_ASYNC, JS_ATOM_NULL,
  26008                                           source_ptr, source_line_num);
  26009         } else {
  26010             /* undo the token parsing */
  26011             if (js_parse_seek_token(s, &pos))
  26012                 return -1;
  26013         }
  26014     } else if (s->token.val == TOK_IDENT &&
  26015                peek_token(s, TRUE) == TOK_ARROW) {
  26016         return js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,
  26017                                       JS_FUNC_NORMAL, JS_ATOM_NULL,
  26018                                       s->token.ptr, s->token.line_num);
  26019     }
  26020  next:
  26021     if (s->token.val == TOK_IDENT) {
  26022         /* name0 is used to check for OP_set_name pattern, not duplicated */
  26023         name0 = s->token.u.ident.atom;
  26024     }
  26025     if (js_parse_cond_expr(s, parse_flags))
  26026         return -1;
  26027 
  26028     op = s->token.val;
  26029     if (op == '=' || (op >= TOK_MUL_ASSIGN && op <= TOK_POW_ASSIGN)) {
  26030         int label;
  26031         if (next_token(s))
  26032             return -1;
  26033         if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, (op != '='), op) < 0)
  26034             return -1;
  26035 
  26036         if (js_parse_assign_expr2(s, parse_flags)) {
  26037             JS_FreeAtom(s->ctx, name);
  26038             return -1;
  26039         }
  26040 
  26041         if (op == '=') {
  26042             if (opcode == OP_get_ref_value && name == name0) {
  26043                 set_object_name(s, name);
  26044             }
  26045         } else {
  26046             static const uint8_t assign_opcodes[] = {
  26047                 OP_mul, OP_div, OP_mod, OP_add, OP_sub,
  26048                 OP_shl, OP_sar, OP_shr, OP_and, OP_xor, OP_or,
  26049 #ifdef CONFIG_BIGNUM
  26050                 OP_pow,
  26051 #endif
  26052                 OP_pow,
  26053             };
  26054             op = assign_opcodes[op - TOK_MUL_ASSIGN];
  26055 #ifdef CONFIG_BIGNUM
  26056             if (s->cur_func->js_mode & JS_MODE_MATH) {
  26057                 if (op == OP_mod)
  26058                     op = OP_math_mod;
  26059             }
  26060 #endif
  26061             emit_op(s, op);
  26062         }
  26063         put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, FALSE);
  26064     } else if (op >= TOK_LAND_ASSIGN && op <= TOK_DOUBLE_QUESTION_MARK_ASSIGN) {
  26065         int label, label1, depth_lvalue, label2;
  26066 
  26067         if (next_token(s))
  26068             return -1;
  26069         if (get_lvalue(s, &opcode, &scope, &name, &label,
  26070                        &depth_lvalue, TRUE, op) < 0)
  26071             return -1;
  26072 
  26073         emit_op(s, OP_dup);
  26074         if (op == TOK_DOUBLE_QUESTION_MARK_ASSIGN)
  26075             emit_op(s, OP_is_undefined_or_null);
  26076         label1 = emit_goto(s, op == TOK_LOR_ASSIGN ? OP_if_true : OP_if_false,
  26077                            -1);
  26078         emit_op(s, OP_drop);
  26079 
  26080         if (js_parse_assign_expr2(s, parse_flags)) {
  26081             JS_FreeAtom(s->ctx, name);
  26082             return -1;
  26083         }
  26084 
  26085         if (opcode == OP_get_ref_value && name == name0) {
  26086             set_object_name(s, name);
  26087         }
  26088 
  26089         switch(depth_lvalue) {
  26090         case 1:
  26091             emit_op(s, OP_insert2);
  26092             break;
  26093         case 2:
  26094             emit_op(s, OP_insert3);
  26095             break;
  26096         case 3:
  26097             emit_op(s, OP_insert4);
  26098             break;
  26099         default:
  26100             abort();
  26101         }
  26102 
  26103         /* XXX: we disable the OP_put_ref_value optimization by not
  26104            using put_lvalue() otherwise depth_lvalue is not correct */
  26105         put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_NOKEEP_DEPTH,
  26106                    FALSE);
  26107         label2 = emit_goto(s, OP_goto, -1);
  26108 
  26109         emit_label(s, label1);
  26110 
  26111         /* remove the lvalue stack entries */
  26112         while (depth_lvalue != 0) {
  26113             emit_op(s, OP_nip);
  26114             depth_lvalue--;
  26115         }
  26116 
  26117         emit_label(s, label2);
  26118     }
  26119     return 0;
  26120 }
  26121 
  26122 static __exception int js_parse_assign_expr(JSParseState *s)
  26123 {
  26124     return js_parse_assign_expr2(s, PF_IN_ACCEPTED);
  26125 }
  26126 
  26127 /* allowed parse_flags: PF_IN_ACCEPTED */
  26128 static __exception int js_parse_expr2(JSParseState *s, int parse_flags)
  26129 {
  26130     BOOL comma = FALSE;
  26131     for(;;) {
  26132         if (js_parse_assign_expr2(s, parse_flags))
  26133             return -1;
  26134         if (comma) {
  26135             /* prevent get_lvalue from using the last expression
  26136                as an lvalue. This also prevents the conversion of
  26137                of get_var to get_ref for method lookup in function
  26138                call inside `with` statement.
  26139              */
  26140             s->cur_func->last_opcode_pos = -1;
  26141         }
  26142         if (s->token.val != ',')
  26143             break;
  26144         comma = TRUE;
  26145         if (next_token(s))
  26146             return -1;
  26147         emit_op(s, OP_drop);
  26148     }
  26149     return 0;
  26150 }
  26151 
  26152 static __exception int js_parse_expr(JSParseState *s)
  26153 {
  26154     return js_parse_expr2(s, PF_IN_ACCEPTED);
  26155 }
  26156 
  26157 static void push_break_entry(JSFunctionDef *fd, BlockEnv *be,
  26158                              JSAtom label_name,
  26159                              int label_break, int label_cont,
  26160                              int drop_count)
  26161 {
  26162     be->prev = fd->top_break;
  26163     fd->top_break = be;
  26164     be->label_name = label_name;
  26165     be->label_break = label_break;
  26166     be->label_cont = label_cont;
  26167     be->drop_count = drop_count;
  26168     be->label_finally = -1;
  26169     be->scope_level = fd->scope_level;
  26170     be->has_iterator = FALSE;
  26171 }
  26172 
  26173 static void pop_break_entry(JSFunctionDef *fd)
  26174 {
  26175     BlockEnv *be;
  26176     be = fd->top_break;
  26177     fd->top_break = be->prev;
  26178 }
  26179 
  26180 static __exception int emit_break(JSParseState *s, JSAtom name, int is_cont)
  26181 {
  26182     BlockEnv *top;
  26183     int i, scope_level;
  26184 
  26185     scope_level = s->cur_func->scope_level;
  26186     top = s->cur_func->top_break;
  26187     while (top != NULL) {
  26188         close_scopes(s, scope_level, top->scope_level);
  26189         scope_level = top->scope_level;
  26190         if (is_cont &&
  26191             top->label_cont != -1 &&
  26192             (name == JS_ATOM_NULL || top->label_name == name)) {
  26193             /* continue stays inside the same block */
  26194             emit_goto(s, OP_goto, top->label_cont);
  26195             return 0;
  26196         }
  26197         if (!is_cont &&
  26198             top->label_break != -1 &&
  26199             (name == JS_ATOM_NULL || top->label_name == name)) {
  26200             emit_goto(s, OP_goto, top->label_break);
  26201             return 0;
  26202         }
  26203         i = 0;
  26204         if (top->has_iterator) {
  26205             emit_op(s, OP_iterator_close);
  26206             i += 3;
  26207         }
  26208         for(; i < top->drop_count; i++)
  26209             emit_op(s, OP_drop);
  26210         if (top->label_finally != -1) {
  26211             /* must push dummy value to keep same stack depth */
  26212             emit_op(s, OP_undefined);
  26213             emit_goto(s, OP_gosub, top->label_finally);
  26214             emit_op(s, OP_drop);
  26215         }
  26216         top = top->prev;
  26217     }
  26218     if (name == JS_ATOM_NULL) {
  26219         if (is_cont)
  26220             return js_parse_error(s, "continue must be inside loop");
  26221         else
  26222             return js_parse_error(s, "break must be inside loop or switch");
  26223     } else {
  26224         return js_parse_error(s, "break/continue label not found");
  26225     }
  26226 }
  26227 
  26228 /* execute the finally blocks before return */
  26229 static void emit_return(JSParseState *s, BOOL hasval)
  26230 {
  26231     BlockEnv *top;
  26232 
  26233     if (s->cur_func->func_kind != JS_FUNC_NORMAL) {
  26234         if (!hasval) {
  26235             /* no value: direct return in case of async generator */
  26236             emit_op(s, OP_undefined);
  26237             hasval = TRUE;
  26238         } else if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {
  26239             /* the await must be done before handling the "finally" in
  26240                case it raises an exception */
  26241             emit_op(s, OP_await);
  26242         }
  26243     }
  26244 
  26245     top = s->cur_func->top_break;
  26246     while (top != NULL) {
  26247         if (top->has_iterator || top->label_finally != -1) {
  26248             if (!hasval) {
  26249                 emit_op(s, OP_undefined);
  26250                 hasval = TRUE;
  26251             }
  26252             /* Remove the stack elements up to and including the catch
  26253                offset. When 'yield' is used in an expression we have
  26254                no easy way to count them, so we use this specific
  26255                instruction instead. */
  26256             emit_op(s, OP_nip_catch);
  26257             /* stack: iter_obj next ret_val */
  26258             if (top->has_iterator) {
  26259                 if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {
  26260                     int label_next, label_next2;
  26261                     emit_op(s, OP_nip); /* next */
  26262                     emit_op(s, OP_swap);
  26263                     emit_op(s, OP_get_field2);
  26264                     emit_atom(s, JS_ATOM_return);
  26265                     /* stack: iter_obj return_func */
  26266                     emit_op(s, OP_dup);
  26267                     emit_op(s, OP_is_undefined_or_null);
  26268                     label_next = emit_goto(s, OP_if_true, -1);
  26269                     emit_op(s, OP_call_method);
  26270                     emit_u16(s, 0);
  26271                     emit_op(s, OP_iterator_check_object);
  26272                     emit_op(s, OP_await);
  26273                     label_next2 = emit_goto(s, OP_goto, -1);
  26274                     emit_label(s, label_next);
  26275                     emit_op(s, OP_drop);
  26276                     emit_label(s, label_next2);
  26277                     emit_op(s, OP_drop);
  26278                 } else {
  26279                     emit_op(s, OP_rot3r);
  26280                     emit_op(s, OP_undefined); /* dummy catch offset */
  26281                     emit_op(s, OP_iterator_close);
  26282                 }
  26283             } else {
  26284                 /* execute the "finally" block */
  26285                 emit_goto(s, OP_gosub, top->label_finally);
  26286             }
  26287         }
  26288         top = top->prev;
  26289     }
  26290     if (s->cur_func->is_derived_class_constructor) {
  26291         int label_return;
  26292 
  26293         /* 'this' can be uninitialized, so it may be accessed only if
  26294            the derived class constructor does not return an object */
  26295         if (hasval) {
  26296             emit_op(s, OP_check_ctor_return);
  26297             label_return = emit_goto(s, OP_if_false, -1);
  26298             emit_op(s, OP_drop);
  26299         } else {
  26300             label_return = -1;
  26301         }
  26302 
  26303         /* The error should be raised in the caller context, so we use
  26304            a specific opcode */
  26305         emit_op(s, OP_scope_get_var_checkthis);
  26306         emit_atom(s, JS_ATOM_this);
  26307         emit_u16(s, 0);
  26308 
  26309         emit_label(s, label_return);
  26310         emit_op(s, OP_return);
  26311     } else if (s->cur_func->func_kind != JS_FUNC_NORMAL) {
  26312         emit_op(s, OP_return_async);
  26313     } else {
  26314         emit_op(s, hasval ? OP_return : OP_return_undef);
  26315     }
  26316 }
  26317 
  26318 #define DECL_MASK_FUNC  (1 << 0) /* allow normal function declaration */
  26319 /* ored with DECL_MASK_FUNC if function declarations are allowed with a label */
  26320 #define DECL_MASK_FUNC_WITH_LABEL (1 << 1)
  26321 #define DECL_MASK_OTHER (1 << 2) /* all other declarations */
  26322 #define DECL_MASK_ALL   (DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL | DECL_MASK_OTHER)
  26323 
  26324 static __exception int js_parse_statement_or_decl(JSParseState *s,
  26325                                                   int decl_mask);
  26326 
  26327 static __exception int js_parse_statement(JSParseState *s)
  26328 {
  26329     return js_parse_statement_or_decl(s, 0);
  26330 }
  26331 
  26332 static __exception int js_parse_block(JSParseState *s)
  26333 {
  26334     if (js_parse_expect(s, '{'))
  26335         return -1;
  26336     if (s->token.val != '}') {
  26337         push_scope(s);
  26338         for(;;) {
  26339             if (js_parse_statement_or_decl(s, DECL_MASK_ALL))
  26340                 return -1;
  26341             if (s->token.val == '}')
  26342                 break;
  26343         }
  26344         pop_scope(s);
  26345     }
  26346     if (next_token(s))
  26347         return -1;
  26348     return 0;
  26349 }
  26350 
  26351 /* allowed parse_flags: PF_IN_ACCEPTED */
  26352 static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok,
  26353                                     BOOL export_flag)
  26354 {
  26355     JSContext *ctx = s->ctx;
  26356     JSFunctionDef *fd = s->cur_func;
  26357     JSAtom name = JS_ATOM_NULL;
  26358 
  26359     for (;;) {
  26360         if (s->token.val == TOK_IDENT) {
  26361             if (s->token.u.ident.is_reserved) {
  26362                 return js_parse_error_reserved_identifier(s);
  26363             }
  26364             name = JS_DupAtom(ctx, s->token.u.ident.atom);
  26365             if (name == JS_ATOM_let && (tok == TOK_LET || tok == TOK_CONST)) {
  26366                 js_parse_error(s, "'let' is not a valid lexical identifier");
  26367                 goto var_error;
  26368             }
  26369             if (next_token(s))
  26370                 goto var_error;
  26371             if (js_define_var(s, name, tok))
  26372                 goto var_error;
  26373             if (export_flag) {
  26374                 if (!add_export_entry(s, s->cur_func->module, name, name,
  26375                                       JS_EXPORT_TYPE_LOCAL))
  26376                     goto var_error;
  26377             }
  26378 
  26379             if (s->token.val == '=') {
  26380                 if (next_token(s))
  26381                     goto var_error;
  26382                 if (tok == TOK_VAR) {
  26383                     /* Must make a reference for proper `with` semantics */
  26384                     int opcode, scope, label;
  26385                     JSAtom name1;
  26386 
  26387                     emit_op(s, OP_scope_get_var);
  26388                     emit_atom(s, name);
  26389                     emit_u16(s, fd->scope_level);
  26390                     if (get_lvalue(s, &opcode, &scope, &name1, &label, NULL, FALSE, '=') < 0)
  26391                         goto var_error;
  26392                     if (js_parse_assign_expr2(s, parse_flags)) {
  26393                         JS_FreeAtom(ctx, name1);
  26394                         goto var_error;
  26395                     }
  26396                     set_object_name(s, name);
  26397                     put_lvalue(s, opcode, scope, name1, label,
  26398                                PUT_LVALUE_NOKEEP, FALSE);
  26399                 } else {
  26400                     if (js_parse_assign_expr2(s, parse_flags))
  26401                         goto var_error;
  26402                     set_object_name(s, name);
  26403                     emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ?
  26404                         OP_scope_put_var_init : OP_scope_put_var);
  26405                     emit_atom(s, name);
  26406                     emit_u16(s, fd->scope_level);
  26407                 }
  26408             } else {
  26409                 if (tok == TOK_CONST) {
  26410                     js_parse_error(s, "missing initializer for const variable");
  26411                     goto var_error;
  26412                 }
  26413                 if (tok == TOK_LET) {
  26414                     /* initialize lexical variable upon entering its scope */
  26415                     emit_op(s, OP_undefined);
  26416                     emit_op(s, OP_scope_put_var_init);
  26417                     emit_atom(s, name);
  26418                     emit_u16(s, fd->scope_level);
  26419                 }
  26420             }
  26421             JS_FreeAtom(ctx, name);
  26422         } else {
  26423             int skip_bits;
  26424             if ((s->token.val == '[' || s->token.val == '{')
  26425             &&  js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') {
  26426                 emit_op(s, OP_undefined);
  26427                 if (js_parse_destructuring_element(s, tok, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)
  26428                     return -1;
  26429             } else {
  26430                 return js_parse_error(s, "variable name expected");
  26431             }
  26432         }
  26433         if (s->token.val != ',')
  26434             break;
  26435         if (next_token(s))
  26436             return -1;
  26437     }
  26438     return 0;
  26439 
  26440  var_error:
  26441     JS_FreeAtom(ctx, name);
  26442     return -1;
  26443 }
  26444 
  26445 /* test if the current token is a label. Use simplistic look-ahead scanner */
  26446 static BOOL is_label(JSParseState *s)
  26447 {
  26448     return (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved &&
  26449             peek_token(s, FALSE) == ':');
  26450 }
  26451 
  26452 /* test if the current token is a let keyword. Use simplistic look-ahead scanner */
  26453 static int is_let(JSParseState *s, int decl_mask)
  26454 {
  26455     int res = FALSE;
  26456 
  26457     if (token_is_pseudo_keyword(s, JS_ATOM_let)) {
  26458         JSParsePos pos;
  26459         js_parse_get_pos(s, &pos);
  26460         for (;;) {
  26461             if (next_token(s)) {
  26462                 res = -1;
  26463                 break;
  26464             }
  26465             if (s->token.val == '[') {
  26466                 /* let [ is a syntax restriction:
  26467                    it never introduces an ExpressionStatement */
  26468                 res = TRUE;
  26469                 break;
  26470             }
  26471             if (s->token.val == '{' ||
  26472                 (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved) ||
  26473                 s->token.val == TOK_LET ||
  26474                 s->token.val == TOK_YIELD ||
  26475                 s->token.val == TOK_AWAIT) {
  26476                 /* Check for possible ASI if not scanning for Declaration */
  26477                 /* XXX: should also check that `{` introduces a BindingPattern,
  26478                    but Firefox does not and rejects eval("let=1;let\n{if(1)2;}") */
  26479                 if (s->last_line_num == s->token.line_num || (decl_mask & DECL_MASK_OTHER)) {
  26480                     res = TRUE;
  26481                     break;
  26482                 }
  26483                 break;
  26484             }
  26485             break;
  26486         }
  26487         if (js_parse_seek_token(s, &pos)) {
  26488             res = -1;
  26489         }
  26490     }
  26491     return res;
  26492 }
  26493 
  26494 /* XXX: handle IteratorClose when exiting the loop before the
  26495    enumeration is done */
  26496 static __exception int js_parse_for_in_of(JSParseState *s, int label_name,
  26497                                           BOOL is_async)
  26498 {
  26499     JSContext *ctx = s->ctx;
  26500     JSFunctionDef *fd = s->cur_func;
  26501     JSAtom var_name;
  26502     BOOL has_initializer, is_for_of, has_destructuring;
  26503     int tok, tok1, opcode, scope, block_scope_level;
  26504     int label_next, label_expr, label_cont, label_body, label_break;
  26505     int pos_next, pos_expr;
  26506     BlockEnv break_entry;
  26507 
  26508     has_initializer = FALSE;
  26509     has_destructuring = FALSE;
  26510     is_for_of = FALSE;
  26511     block_scope_level = fd->scope_level;
  26512     label_cont = new_label(s);
  26513     label_body = new_label(s);
  26514     label_break = new_label(s);
  26515     label_next = new_label(s);
  26516 
  26517     /* create scope for the lexical variables declared in the enumeration
  26518        expressions. XXX: Not completely correct because of weird capturing
  26519        semantics in `for (i of o) a.push(function(){return i})` */
  26520     push_scope(s);
  26521 
  26522     /* local for_in scope starts here so individual elements
  26523        can be closed in statement. */
  26524     push_break_entry(s->cur_func, &break_entry,
  26525                      label_name, label_break, label_cont, 1);
  26526     break_entry.scope_level = block_scope_level;
  26527 
  26528     label_expr = emit_goto(s, OP_goto, -1);
  26529 
  26530     pos_next = s->cur_func->byte_code.size;
  26531     emit_label(s, label_next);
  26532 
  26533     tok = s->token.val;
  26534     switch (is_let(s, DECL_MASK_OTHER)) {
  26535     case TRUE:
  26536         tok = TOK_LET;
  26537         break;
  26538     case FALSE:
  26539         break;
  26540     default:
  26541         return -1;
  26542     }
  26543     if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) {
  26544         if (next_token(s))
  26545             return -1;
  26546 
  26547         if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) {
  26548             if (s->token.val == '[' || s->token.val == '{') {
  26549                 if (js_parse_destructuring_element(s, tok, 0, TRUE, -1, FALSE) < 0)
  26550                     return -1;
  26551                 has_destructuring = TRUE;
  26552             } else {
  26553                 return js_parse_error(s, "variable name expected");
  26554             }
  26555             var_name = JS_ATOM_NULL;
  26556         } else {
  26557             var_name = JS_DupAtom(ctx, s->token.u.ident.atom);
  26558             if (next_token(s)) {
  26559                 JS_FreeAtom(s->ctx, var_name);
  26560                 return -1;
  26561             }
  26562             if (js_define_var(s, var_name, tok)) {
  26563                 JS_FreeAtom(s->ctx, var_name);
  26564                 return -1;
  26565             }
  26566             emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ?
  26567                     OP_scope_put_var_init : OP_scope_put_var);
  26568             emit_atom(s, var_name);
  26569             emit_u16(s, fd->scope_level);
  26570         }
  26571     } else if (!is_async && token_is_pseudo_keyword(s, JS_ATOM_async) &&
  26572                peek_token(s, FALSE) == TOK_OF) {
  26573         return js_parse_error(s, "'for of' expression cannot start with 'async'");
  26574     } else {
  26575         int skip_bits;
  26576         if ((s->token.val == '[' || s->token.val == '{')
  26577         &&  ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == TOK_IN || tok1 == TOK_OF)) {
  26578             if (js_parse_destructuring_element(s, 0, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)
  26579                 return -1;
  26580         } else {
  26581             int lvalue_label;
  26582             if (js_parse_left_hand_side_expr(s))
  26583                 return -1;
  26584             if (get_lvalue(s, &opcode, &scope, &var_name, &lvalue_label,
  26585                            NULL, FALSE, TOK_FOR))
  26586                 return -1;
  26587             put_lvalue(s, opcode, scope, var_name, lvalue_label,
  26588                        PUT_LVALUE_NOKEEP_BOTTOM, FALSE);
  26589         }
  26590         var_name = JS_ATOM_NULL;
  26591     }
  26592     emit_goto(s, OP_goto, label_body);
  26593 
  26594     pos_expr = s->cur_func->byte_code.size;
  26595     emit_label(s, label_expr);
  26596     if (s->token.val == '=') {
  26597         /* XXX: potential scoping issue if inside `with` statement */
  26598         has_initializer = TRUE;
  26599         /* parse and evaluate initializer prior to evaluating the
  26600            object (only used with "for in" with a non lexical variable
  26601            in non strict mode */
  26602         if (next_token(s) || js_parse_assign_expr2(s, 0)) {
  26603             JS_FreeAtom(ctx, var_name);
  26604             return -1;
  26605         }
  26606         if (var_name != JS_ATOM_NULL) {
  26607             emit_op(s, OP_scope_put_var);
  26608             emit_atom(s, var_name);
  26609             emit_u16(s, fd->scope_level);
  26610         }
  26611     }
  26612     JS_FreeAtom(ctx, var_name);
  26613 
  26614     if (token_is_pseudo_keyword(s, JS_ATOM_of)) {
  26615         break_entry.has_iterator = is_for_of = TRUE;
  26616         break_entry.drop_count += 2;
  26617         if (has_initializer)
  26618             goto initializer_error;
  26619     } else if (s->token.val == TOK_IN) {
  26620         if (is_async)
  26621             return js_parse_error(s, "'for await' loop should be used with 'of'");
  26622         if (has_initializer &&
  26623             (tok != TOK_VAR || (fd->js_mode & JS_MODE_STRICT) ||
  26624              has_destructuring)) {
  26625         initializer_error:
  26626             return js_parse_error(s, "a declaration in the head of a for-%s loop can't have an initializer",
  26627                                   is_for_of ? "of" : "in");
  26628         }
  26629     } else {
  26630         return js_parse_error(s, "expected 'of' or 'in' in for control expression");
  26631     }
  26632     if (next_token(s))
  26633         return -1;
  26634     if (is_for_of) {
  26635         if (js_parse_assign_expr(s))
  26636             return -1;
  26637     } else {
  26638         if (js_parse_expr(s))
  26639             return -1;
  26640     }
  26641     /* close the scope after having evaluated the expression so that
  26642        the TDZ values are in the closures */
  26643     close_scopes(s, s->cur_func->scope_level, block_scope_level);
  26644     if (is_for_of) {
  26645         if (is_async)
  26646             emit_op(s, OP_for_await_of_start);
  26647         else
  26648             emit_op(s, OP_for_of_start);
  26649         /* on stack: enum_rec */
  26650     } else {
  26651         emit_op(s, OP_for_in_start);
  26652         /* on stack: enum_obj */
  26653     }
  26654     emit_goto(s, OP_goto, label_cont);
  26655 
  26656     if (js_parse_expect(s, ')'))
  26657         return -1;
  26658 
  26659     if (OPTIMIZE) {
  26660         /* move the `next` code here */
  26661         DynBuf *bc = &s->cur_func->byte_code;
  26662         int chunk_size = pos_expr - pos_next;
  26663         int offset = bc->size - pos_next;
  26664         int i;
  26665         dbuf_realloc(bc, bc->size + chunk_size);
  26666         dbuf_put(bc, bc->buf + pos_next, chunk_size);
  26667         memset(bc->buf + pos_next, OP_nop, chunk_size);
  26668         /* `next` part ends with a goto */
  26669         s->cur_func->last_opcode_pos = bc->size - 5;
  26670         /* relocate labels */
  26671         for (i = label_cont; i < s->cur_func->label_count; i++) {
  26672             LabelSlot *ls = &s->cur_func->label_slots[i];
  26673             if (ls->pos >= pos_next && ls->pos < pos_expr)
  26674                 ls->pos += offset;
  26675         }
  26676     }
  26677 
  26678     emit_label(s, label_body);
  26679     if (js_parse_statement(s))
  26680         return -1;
  26681 
  26682     close_scopes(s, s->cur_func->scope_level, block_scope_level);
  26683 
  26684     emit_label(s, label_cont);
  26685     if (is_for_of) {
  26686         if (is_async) {
  26687             /* call the next method */
  26688             /* stack: iter_obj next catch_offset */
  26689             emit_op(s, OP_dup3);
  26690             emit_op(s, OP_drop);
  26691             emit_op(s, OP_call_method);
  26692             emit_u16(s, 0);
  26693             /* get the result of the promise */
  26694             emit_op(s, OP_await);
  26695             /* unwrap the value and done values */
  26696             emit_op(s, OP_iterator_get_value_done);
  26697         } else {
  26698             emit_op(s, OP_for_of_next);
  26699             emit_u8(s, 0);
  26700         }
  26701     } else {
  26702         emit_op(s, OP_for_in_next);
  26703     }
  26704     /* on stack: enum_rec / enum_obj value bool */
  26705     emit_goto(s, OP_if_false, label_next);
  26706     /* drop the undefined value from for_xx_next */
  26707     emit_op(s, OP_drop);
  26708 
  26709     emit_label(s, label_break);
  26710     if (is_for_of) {
  26711         /* close and drop enum_rec */
  26712         emit_op(s, OP_iterator_close);
  26713     } else {
  26714         emit_op(s, OP_drop);
  26715     }
  26716     pop_break_entry(s->cur_func);
  26717     pop_scope(s);
  26718     return 0;
  26719 }
  26720 
  26721 static void set_eval_ret_undefined(JSParseState *s)
  26722 {
  26723     if (s->cur_func->eval_ret_idx >= 0) {
  26724         emit_op(s, OP_undefined);
  26725         emit_op(s, OP_put_loc);
  26726         emit_u16(s, s->cur_func->eval_ret_idx);
  26727     }
  26728 }
  26729 
  26730 static __exception int js_parse_statement_or_decl(JSParseState *s,
  26731                                                   int decl_mask)
  26732 {
  26733     JSContext *ctx = s->ctx;
  26734     JSAtom label_name;
  26735     int tok;
  26736 
  26737     /* specific label handling */
  26738     /* XXX: support multiple labels on loop statements */
  26739     label_name = JS_ATOM_NULL;
  26740     if (is_label(s)) {
  26741         BlockEnv *be;
  26742 
  26743         label_name = JS_DupAtom(ctx, s->token.u.ident.atom);
  26744 
  26745         for (be = s->cur_func->top_break; be; be = be->prev) {
  26746             if (be->label_name == label_name) {
  26747                 js_parse_error(s, "duplicate label name");
  26748                 goto fail;
  26749             }
  26750         }
  26751 
  26752         if (next_token(s))
  26753             goto fail;
  26754         if (js_parse_expect(s, ':'))
  26755             goto fail;
  26756         if (s->token.val != TOK_FOR
  26757         &&  s->token.val != TOK_DO
  26758         &&  s->token.val != TOK_WHILE) {
  26759             /* labelled regular statement */
  26760             int label_break, mask;
  26761             BlockEnv break_entry;
  26762 
  26763             label_break = new_label(s);
  26764             push_break_entry(s->cur_func, &break_entry,
  26765                              label_name, label_break, -1, 0);
  26766             if (!(s->cur_func->js_mode & JS_MODE_STRICT) &&
  26767                 (decl_mask & DECL_MASK_FUNC_WITH_LABEL)) {
  26768                 mask = DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL;
  26769             } else {
  26770                 mask = 0;
  26771             }
  26772             if (js_parse_statement_or_decl(s, mask))
  26773                 goto fail;
  26774             emit_label(s, label_break);
  26775             pop_break_entry(s->cur_func);
  26776             goto done;
  26777         }
  26778     }
  26779 
  26780     switch(tok = s->token.val) {
  26781     case '{':
  26782         if (js_parse_block(s))
  26783             goto fail;
  26784         break;
  26785     case TOK_RETURN:
  26786         if (s->cur_func->is_eval) {
  26787             js_parse_error(s, "return not in a function");
  26788             goto fail;
  26789         }
  26790         if (s->cur_func->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT) {
  26791             js_parse_error(s, "return in a static initializer block");
  26792             goto fail;
  26793         }
  26794         if (next_token(s))
  26795             goto fail;
  26796         if (s->token.val != ';' && s->token.val != '}' && !s->got_lf) {
  26797             if (js_parse_expr(s))
  26798                 goto fail;
  26799             emit_return(s, TRUE);
  26800         } else {
  26801             emit_return(s, FALSE);
  26802         }
  26803         if (js_parse_expect_semi(s))
  26804             goto fail;
  26805         break;
  26806     case TOK_THROW:
  26807         if (next_token(s))
  26808             goto fail;
  26809         if (s->got_lf) {
  26810             js_parse_error(s, "line terminator not allowed after throw");
  26811             goto fail;
  26812         }
  26813         if (js_parse_expr(s))
  26814             goto fail;
  26815         emit_op(s, OP_throw);
  26816         if (js_parse_expect_semi(s))
  26817             goto fail;
  26818         break;
  26819     case TOK_LET:
  26820     case TOK_CONST:
  26821     haslet:
  26822         if (!(decl_mask & DECL_MASK_OTHER)) {
  26823             js_parse_error(s, "lexical declarations can't appear in single-statement context");
  26824             goto fail;
  26825         }
  26826         /* fall thru */
  26827     case TOK_VAR:
  26828         if (next_token(s))
  26829             goto fail;
  26830         if (js_parse_var(s, TRUE, tok, FALSE))
  26831             goto fail;
  26832         if (js_parse_expect_semi(s))
  26833             goto fail;
  26834         break;
  26835     case TOK_IF:
  26836         {
  26837             int label1, label2, mask;
  26838             if (next_token(s))
  26839                 goto fail;
  26840             /* create a new scope for `let f;if(1) function f(){}` */
  26841             push_scope(s);
  26842             set_eval_ret_undefined(s);
  26843             if (js_parse_expr_paren(s))
  26844                 goto fail;
  26845             label1 = emit_goto(s, OP_if_false, -1);
  26846             if (s->cur_func->js_mode & JS_MODE_STRICT)
  26847                 mask = 0;
  26848             else
  26849                 mask = DECL_MASK_FUNC; /* Annex B.3.4 */
  26850 
  26851             if (js_parse_statement_or_decl(s, mask))
  26852                 goto fail;
  26853 
  26854             if (s->token.val == TOK_ELSE) {
  26855                 label2 = emit_goto(s, OP_goto, -1);
  26856                 if (next_token(s))
  26857                     goto fail;
  26858 
  26859                 emit_label(s, label1);
  26860                 if (js_parse_statement_or_decl(s, mask))
  26861                     goto fail;
  26862 
  26863                 label1 = label2;
  26864             }
  26865             emit_label(s, label1);
  26866             pop_scope(s);
  26867         }
  26868         break;
  26869     case TOK_WHILE:
  26870         {
  26871             int label_cont, label_break;
  26872             BlockEnv break_entry;
  26873 
  26874             label_cont = new_label(s);
  26875             label_break = new_label(s);
  26876 
  26877             push_break_entry(s->cur_func, &break_entry,
  26878                              label_name, label_break, label_cont, 0);
  26879 
  26880             if (next_token(s))
  26881                 goto fail;
  26882 
  26883             set_eval_ret_undefined(s);
  26884 
  26885             emit_label(s, label_cont);
  26886             if (js_parse_expr_paren(s))
  26887                 goto fail;
  26888             emit_goto(s, OP_if_false, label_break);
  26889 
  26890             if (js_parse_statement(s))
  26891                 goto fail;
  26892             emit_goto(s, OP_goto, label_cont);
  26893 
  26894             emit_label(s, label_break);
  26895 
  26896             pop_break_entry(s->cur_func);
  26897         }
  26898         break;
  26899     case TOK_DO:
  26900         {
  26901             int label_cont, label_break, label1;
  26902             BlockEnv break_entry;
  26903 
  26904             label_cont = new_label(s);
  26905             label_break = new_label(s);
  26906             label1 = new_label(s);
  26907 
  26908             push_break_entry(s->cur_func, &break_entry,
  26909                              label_name, label_break, label_cont, 0);
  26910 
  26911             if (next_token(s))
  26912                 goto fail;
  26913 
  26914             emit_label(s, label1);
  26915 
  26916             set_eval_ret_undefined(s);
  26917 
  26918             if (js_parse_statement(s))
  26919                 goto fail;
  26920 
  26921             emit_label(s, label_cont);
  26922             if (js_parse_expect(s, TOK_WHILE))
  26923                 goto fail;
  26924             if (js_parse_expr_paren(s))
  26925                 goto fail;
  26926             /* Insert semicolon if missing */
  26927             if (s->token.val == ';') {
  26928                 if (next_token(s))
  26929                     goto fail;
  26930             }
  26931             emit_goto(s, OP_if_true, label1);
  26932 
  26933             emit_label(s, label_break);
  26934 
  26935             pop_break_entry(s->cur_func);
  26936         }
  26937         break;
  26938     case TOK_FOR:
  26939         {
  26940             int label_cont, label_break, label_body, label_test;
  26941             int pos_cont, pos_body, block_scope_level;
  26942             BlockEnv break_entry;
  26943             int tok, bits;
  26944             BOOL is_async;
  26945 
  26946             if (next_token(s))
  26947                 goto fail;
  26948 
  26949             set_eval_ret_undefined(s);
  26950             bits = 0;
  26951             is_async = FALSE;
  26952             if (s->token.val == '(') {
  26953                 js_parse_skip_parens_token(s, &bits, FALSE);
  26954             } else if (s->token.val == TOK_AWAIT) {
  26955                 if (!(s->cur_func->func_kind & JS_FUNC_ASYNC)) {
  26956                     js_parse_error(s, "for await is only valid in asynchronous functions");
  26957                     goto fail;
  26958                 }
  26959                 is_async = TRUE;
  26960                 if (next_token(s))
  26961                     goto fail;
  26962                 s->cur_func->has_await = TRUE;
  26963             }
  26964             if (js_parse_expect(s, '('))
  26965                 goto fail;
  26966 
  26967             if (!(bits & SKIP_HAS_SEMI)) {
  26968                 /* parse for/in or for/of */
  26969                 if (js_parse_for_in_of(s, label_name, is_async))
  26970                     goto fail;
  26971                 break;
  26972             }
  26973             block_scope_level = s->cur_func->scope_level;
  26974 
  26975             /* create scope for the lexical variables declared in the initial,
  26976                test and increment expressions */
  26977             push_scope(s);
  26978             /* initial expression */
  26979             tok = s->token.val;
  26980             if (tok != ';') {
  26981                 switch (is_let(s, DECL_MASK_OTHER)) {
  26982                 case TRUE:
  26983                     tok = TOK_LET;
  26984                     break;
  26985                 case FALSE:
  26986                     break;
  26987                 default:
  26988                     goto fail;
  26989                 }
  26990                 if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) {
  26991                     if (next_token(s))
  26992                         goto fail;
  26993                     if (js_parse_var(s, FALSE, tok, FALSE))
  26994                         goto fail;
  26995                 } else {
  26996                     if (js_parse_expr2(s, FALSE))
  26997                         goto fail;
  26998                     emit_op(s, OP_drop);
  26999                 }
  27000 
  27001                 /* close the closures before the first iteration */
  27002                 close_scopes(s, s->cur_func->scope_level, block_scope_level);
  27003             }
  27004             if (js_parse_expect(s, ';'))
  27005                 goto fail;
  27006 
  27007             label_test = new_label(s);
  27008             label_cont = new_label(s);
  27009             label_body = new_label(s);
  27010             label_break = new_label(s);
  27011 
  27012             push_break_entry(s->cur_func, &break_entry,
  27013                              label_name, label_break, label_cont, 0);
  27014 
  27015             /* test expression */
  27016             if (s->token.val == ';') {
  27017                 /* no test expression */
  27018                 label_test = label_body;
  27019             } else {
  27020                 emit_label(s, label_test);
  27021                 if (js_parse_expr(s))
  27022                     goto fail;
  27023                 emit_goto(s, OP_if_false, label_break);
  27024             }
  27025             if (js_parse_expect(s, ';'))
  27026                 goto fail;
  27027 
  27028             if (s->token.val == ')') {
  27029                 /* no end expression */
  27030                 break_entry.label_cont = label_cont = label_test;
  27031                 pos_cont = 0; /* avoid warning */
  27032             } else {
  27033                 /* skip the end expression */
  27034                 emit_goto(s, OP_goto, label_body);
  27035 
  27036                 pos_cont = s->cur_func->byte_code.size;
  27037                 emit_label(s, label_cont);
  27038                 if (js_parse_expr(s))
  27039                     goto fail;
  27040                 emit_op(s, OP_drop);
  27041                 if (label_test != label_body)
  27042                     emit_goto(s, OP_goto, label_test);
  27043             }
  27044             if (js_parse_expect(s, ')'))
  27045                 goto fail;
  27046 
  27047             pos_body = s->cur_func->byte_code.size;
  27048             emit_label(s, label_body);
  27049             if (js_parse_statement(s))
  27050                 goto fail;
  27051 
  27052             /* close the closures before the next iteration */
  27053             /* XXX: check continue case */
  27054             close_scopes(s, s->cur_func->scope_level, block_scope_level);
  27055 
  27056             if (OPTIMIZE && label_test != label_body && label_cont != label_test) {
  27057                 /* move the increment code here */
  27058                 DynBuf *bc = &s->cur_func->byte_code;
  27059                 int chunk_size = pos_body - pos_cont;
  27060                 int offset = bc->size - pos_cont;
  27061                 int i;
  27062                 dbuf_realloc(bc, bc->size + chunk_size);
  27063                 dbuf_put(bc, bc->buf + pos_cont, chunk_size);
  27064                 memset(bc->buf + pos_cont, OP_nop, chunk_size);
  27065                 /* increment part ends with a goto */
  27066                 s->cur_func->last_opcode_pos = bc->size - 5;
  27067                 /* relocate labels */
  27068                 for (i = label_cont; i < s->cur_func->label_count; i++) {
  27069                     LabelSlot *ls = &s->cur_func->label_slots[i];
  27070                     if (ls->pos >= pos_cont && ls->pos < pos_body)
  27071                         ls->pos += offset;
  27072                 }
  27073             } else {
  27074                 emit_goto(s, OP_goto, label_cont);
  27075             }
  27076 
  27077             emit_label(s, label_break);
  27078 
  27079             pop_break_entry(s->cur_func);
  27080             pop_scope(s);
  27081         }
  27082         break;
  27083     case TOK_BREAK:
  27084     case TOK_CONTINUE:
  27085         {
  27086             int is_cont = s->token.val - TOK_BREAK;
  27087             int label;
  27088 
  27089             if (next_token(s))
  27090                 goto fail;
  27091             if (!s->got_lf && s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)
  27092                 label = s->token.u.ident.atom;
  27093             else
  27094                 label = JS_ATOM_NULL;
  27095             if (emit_break(s, label, is_cont))
  27096                 goto fail;
  27097             if (label != JS_ATOM_NULL) {
  27098                 if (next_token(s))
  27099                     goto fail;
  27100             }
  27101             if (js_parse_expect_semi(s))
  27102                 goto fail;
  27103         }
  27104         break;
  27105     case TOK_SWITCH:
  27106         {
  27107             int label_case, label_break, label1;
  27108             int default_label_pos;
  27109             BlockEnv break_entry;
  27110 
  27111             if (next_token(s))
  27112                 goto fail;
  27113 
  27114             set_eval_ret_undefined(s);
  27115             if (js_parse_expr_paren(s))
  27116                 goto fail;
  27117 
  27118             push_scope(s);
  27119             label_break = new_label(s);
  27120             push_break_entry(s->cur_func, &break_entry,
  27121                              label_name, label_break, -1, 1);
  27122 
  27123             if (js_parse_expect(s, '{'))
  27124                 goto fail;
  27125 
  27126             default_label_pos = -1;
  27127             label_case = -1;
  27128             while (s->token.val != '}') {
  27129                 if (s->token.val == TOK_CASE) {
  27130                     label1 = -1;
  27131                     if (label_case >= 0) {
  27132                         /* skip the case if needed */
  27133                         label1 = emit_goto(s, OP_goto, -1);
  27134                     }
  27135                     emit_label(s, label_case);
  27136                     label_case = -1;
  27137                     for (;;) {
  27138                         /* parse a sequence of case clauses */
  27139                         if (next_token(s))
  27140                             goto fail;
  27141                         emit_op(s, OP_dup);
  27142                         if (js_parse_expr(s))
  27143                             goto fail;
  27144                         if (js_parse_expect(s, ':'))
  27145                             goto fail;
  27146                         emit_op(s, OP_strict_eq);
  27147                         if (s->token.val == TOK_CASE) {
  27148                             label1 = emit_goto(s, OP_if_true, label1);
  27149                         } else {
  27150                             label_case = emit_goto(s, OP_if_false, -1);
  27151                             emit_label(s, label1);
  27152                             break;
  27153                         }
  27154                     }
  27155                 } else if (s->token.val == TOK_DEFAULT) {
  27156                     if (next_token(s))
  27157                         goto fail;
  27158                     if (js_parse_expect(s, ':'))
  27159                         goto fail;
  27160                     if (default_label_pos >= 0) {
  27161                         js_parse_error(s, "duplicate default");
  27162                         goto fail;
  27163                     }
  27164                     if (label_case < 0) {
  27165                         /* falling thru direct from switch expression */
  27166                         label_case = emit_goto(s, OP_goto, -1);
  27167                     }
  27168                     /* Emit a dummy label opcode. Label will be patched after
  27169                        the end of the switch body. Do not use emit_label(s, 0)
  27170                        because it would clobber label 0 address, preventing
  27171                        proper optimizer operation.
  27172                      */
  27173                     emit_op(s, OP_label);
  27174                     emit_u32(s, 0);
  27175                     default_label_pos = s->cur_func->byte_code.size - 4;
  27176                 } else {
  27177                     if (label_case < 0) {
  27178                         /* falling thru direct from switch expression */
  27179                         js_parse_error(s, "invalid switch statement");
  27180                         goto fail;
  27181                     }
  27182                     if (js_parse_statement_or_decl(s, DECL_MASK_ALL))
  27183                         goto fail;
  27184                 }
  27185             }
  27186             if (js_parse_expect(s, '}'))
  27187                 goto fail;
  27188             if (default_label_pos >= 0) {
  27189                 /* Ugly patch for the the `default` label, shameful and risky */
  27190                 put_u32(s->cur_func->byte_code.buf + default_label_pos,
  27191                         label_case);
  27192                 s->cur_func->label_slots[label_case].pos = default_label_pos + 4;
  27193             } else {
  27194                 emit_label(s, label_case);
  27195             }
  27196             emit_label(s, label_break);
  27197             emit_op(s, OP_drop); /* drop the switch expression */
  27198 
  27199             pop_break_entry(s->cur_func);
  27200             pop_scope(s);
  27201         }
  27202         break;
  27203     case TOK_TRY:
  27204         {
  27205             int label_catch, label_catch2, label_finally, label_end;
  27206             JSAtom name;
  27207             BlockEnv block_env;
  27208 
  27209             set_eval_ret_undefined(s);
  27210             if (next_token(s))
  27211                 goto fail;
  27212             label_catch = new_label(s);
  27213             label_catch2 = new_label(s);
  27214             label_finally = new_label(s);
  27215             label_end = new_label(s);
  27216 
  27217             emit_goto(s, OP_catch, label_catch);
  27218 
  27219             push_break_entry(s->cur_func, &block_env,
  27220                              JS_ATOM_NULL, -1, -1, 1);
  27221             block_env.label_finally = label_finally;
  27222 
  27223             if (js_parse_block(s))
  27224                 goto fail;
  27225 
  27226             pop_break_entry(s->cur_func);
  27227 
  27228             if (js_is_live_code(s)) {
  27229                 /* drop the catch offset */
  27230                 emit_op(s, OP_drop);
  27231                 /* must push dummy value to keep same stack size */
  27232                 emit_op(s, OP_undefined);
  27233                 emit_goto(s, OP_gosub, label_finally);
  27234                 emit_op(s, OP_drop);
  27235 
  27236                 emit_goto(s, OP_goto, label_end);
  27237             }
  27238 
  27239             if (s->token.val == TOK_CATCH) {
  27240                 if (next_token(s))
  27241                     goto fail;
  27242 
  27243                 push_scope(s);  /* catch variable */
  27244                 emit_label(s, label_catch);
  27245 
  27246                 if (s->token.val == '{') {
  27247                     /* support optional-catch-binding feature */
  27248                     emit_op(s, OP_drop);    /* pop the exception object */
  27249                 } else {
  27250                     if (js_parse_expect(s, '('))
  27251                         goto fail;
  27252                     if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) {
  27253                         if (s->token.val == '[' || s->token.val == '{') {
  27254                             /* XXX: TOK_LET is not completely correct */
  27255                             if (js_parse_destructuring_element(s, TOK_LET, 0, TRUE, -1, TRUE) < 0)
  27256                                 goto fail;
  27257                         } else {
  27258                             js_parse_error(s, "identifier expected");
  27259                             goto fail;
  27260                         }
  27261                     } else {
  27262                         name = JS_DupAtom(ctx, s->token.u.ident.atom);
  27263                         if (next_token(s)
  27264                         ||  js_define_var(s, name, TOK_CATCH) < 0) {
  27265                             JS_FreeAtom(ctx, name);
  27266                             goto fail;
  27267                         }
  27268                         /* store the exception value in the catch variable */
  27269                         emit_op(s, OP_scope_put_var);
  27270                         emit_u32(s, name);
  27271                         emit_u16(s, s->cur_func->scope_level);
  27272                     }
  27273                     if (js_parse_expect(s, ')'))
  27274                         goto fail;
  27275                 }
  27276                 /* XXX: should keep the address to nop it out if there is no finally block */
  27277                 emit_goto(s, OP_catch, label_catch2);
  27278 
  27279                 push_scope(s);  /* catch block */
  27280                 push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL,
  27281                                  -1, -1, 1);
  27282                 block_env.label_finally = label_finally;
  27283 
  27284                 if (js_parse_block(s))
  27285                     goto fail;
  27286 
  27287                 pop_break_entry(s->cur_func);
  27288                 pop_scope(s);  /* catch block */
  27289                 pop_scope(s);  /* catch variable */
  27290 
  27291                 if (js_is_live_code(s)) {
  27292                     /* drop the catch2 offset */
  27293                     emit_op(s, OP_drop);
  27294                     /* XXX: should keep the address to nop it out if there is no finally block */
  27295                     /* must push dummy value to keep same stack size */
  27296                     emit_op(s, OP_undefined);
  27297                     emit_goto(s, OP_gosub, label_finally);
  27298                     emit_op(s, OP_drop);
  27299                     emit_goto(s, OP_goto, label_end);
  27300                 }
  27301                 /* catch exceptions thrown in the catch block to execute the
  27302                  * finally clause and rethrow the exception */
  27303                 emit_label(s, label_catch2);
  27304                 /* catch value is at TOS, no need to push undefined */
  27305                 emit_goto(s, OP_gosub, label_finally);
  27306                 emit_op(s, OP_throw);
  27307 
  27308             } else if (s->token.val == TOK_FINALLY) {
  27309                 /* finally without catch : execute the finally clause
  27310                  * and rethrow the exception */
  27311                 emit_label(s, label_catch);
  27312                 /* catch value is at TOS, no need to push undefined */
  27313                 emit_goto(s, OP_gosub, label_finally);
  27314                 emit_op(s, OP_throw);
  27315             } else {
  27316                 js_parse_error(s, "expecting catch or finally");
  27317                 goto fail;
  27318             }
  27319             emit_label(s, label_finally);
  27320             if (s->token.val == TOK_FINALLY) {
  27321                 int saved_eval_ret_idx = 0; /* avoid warning */
  27322 
  27323                 if (next_token(s))
  27324                     goto fail;
  27325                 /* on the stack: ret_value gosub_ret_value */
  27326                 push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL,
  27327                                  -1, -1, 2);
  27328 
  27329                 if (s->cur_func->eval_ret_idx >= 0) {
  27330                     /* 'finally' updates eval_ret only if not a normal
  27331                        termination */
  27332                     saved_eval_ret_idx =
  27333                         add_var(s->ctx, s->cur_func, JS_ATOM__ret_);
  27334                     if (saved_eval_ret_idx < 0)
  27335                         goto fail;
  27336                     emit_op(s, OP_get_loc);
  27337                     emit_u16(s, s->cur_func->eval_ret_idx);
  27338                     emit_op(s, OP_put_loc);
  27339                     emit_u16(s, saved_eval_ret_idx);
  27340                     set_eval_ret_undefined(s);
  27341                 }
  27342 
  27343                 if (js_parse_block(s))
  27344                     goto fail;
  27345 
  27346                 if (s->cur_func->eval_ret_idx >= 0) {
  27347                     emit_op(s, OP_get_loc);
  27348                     emit_u16(s, saved_eval_ret_idx);
  27349                     emit_op(s, OP_put_loc);
  27350                     emit_u16(s, s->cur_func->eval_ret_idx);
  27351                 }
  27352                 pop_break_entry(s->cur_func);
  27353             }
  27354             emit_op(s, OP_ret);
  27355             emit_label(s, label_end);
  27356         }
  27357         break;
  27358     case ';':
  27359         /* empty statement */
  27360         if (next_token(s))
  27361             goto fail;
  27362         break;
  27363     case TOK_WITH:
  27364         if (s->cur_func->js_mode & JS_MODE_STRICT) {
  27365             js_parse_error(s, "invalid keyword: with");
  27366             goto fail;
  27367         } else {
  27368             int with_idx;
  27369 
  27370             if (next_token(s))
  27371                 goto fail;
  27372 
  27373             if (js_parse_expr_paren(s))
  27374                 goto fail;
  27375 
  27376             push_scope(s);
  27377             with_idx = define_var(s, s->cur_func, JS_ATOM__with_,
  27378                                   JS_VAR_DEF_WITH);
  27379             if (with_idx < 0)
  27380                 goto fail;
  27381             emit_op(s, OP_to_object);
  27382             emit_op(s, OP_put_loc);
  27383             emit_u16(s, with_idx);
  27384 
  27385             set_eval_ret_undefined(s);
  27386             if (js_parse_statement(s))
  27387                 goto fail;
  27388 
  27389             /* Popping scope drops lexical context for the with object variable */
  27390             pop_scope(s);
  27391         }
  27392         break;
  27393     case TOK_FUNCTION:
  27394         /* ES6 Annex B.3.2 and B.3.3 semantics */
  27395         if (!(decl_mask & DECL_MASK_FUNC))
  27396             goto func_decl_error;
  27397         if (!(decl_mask & DECL_MASK_OTHER) && peek_token(s, FALSE) == '*')
  27398             goto func_decl_error;
  27399         goto parse_func_var;
  27400     case TOK_IDENT:
  27401         if (s->token.u.ident.is_reserved) {
  27402             js_parse_error_reserved_identifier(s);
  27403             goto fail;
  27404         }
  27405         /* Determine if `let` introduces a Declaration or an ExpressionStatement */
  27406         switch (is_let(s, decl_mask)) {
  27407         case TRUE:
  27408             tok = TOK_LET;
  27409             goto haslet;
  27410         case FALSE:
  27411             break;
  27412         default:
  27413             goto fail;
  27414         }
  27415         if (token_is_pseudo_keyword(s, JS_ATOM_async) &&
  27416             peek_token(s, TRUE) == TOK_FUNCTION) {
  27417             if (!(decl_mask & DECL_MASK_OTHER)) {
  27418             func_decl_error:
  27419                 js_parse_error(s, "function declarations can't appear in single-statement context");
  27420                 goto fail;
  27421             }
  27422         parse_func_var:
  27423             if (js_parse_function_decl(s, JS_PARSE_FUNC_VAR,
  27424                                        JS_FUNC_NORMAL, JS_ATOM_NULL,
  27425                                        s->token.ptr, s->token.line_num))
  27426                 goto fail;
  27427             break;
  27428         }
  27429         goto hasexpr;
  27430 
  27431     case TOK_CLASS:
  27432         if (!(decl_mask & DECL_MASK_OTHER)) {
  27433             js_parse_error(s, "class declarations can't appear in single-statement context");
  27434             goto fail;
  27435         }
  27436         if (js_parse_class(s, FALSE, JS_PARSE_EXPORT_NONE))
  27437             return -1;
  27438         break;
  27439 
  27440     case TOK_DEBUGGER:
  27441         /* currently no debugger, so just skip the keyword */
  27442         if (next_token(s))
  27443             goto fail;
  27444         if (js_parse_expect_semi(s))
  27445             goto fail;
  27446         break;
  27447 
  27448     case TOK_ENUM:
  27449     case TOK_EXPORT:
  27450     case TOK_EXTENDS:
  27451         js_unsupported_keyword(s, s->token.u.ident.atom);
  27452         goto fail;
  27453 
  27454     default:
  27455     hasexpr:
  27456         if (js_parse_expr(s))
  27457             goto fail;
  27458         if (s->cur_func->eval_ret_idx >= 0) {
  27459             /* store the expression value so that it can be returned
  27460                by eval() */
  27461             emit_op(s, OP_put_loc);
  27462             emit_u16(s, s->cur_func->eval_ret_idx);
  27463         } else {
  27464             emit_op(s, OP_drop); /* drop the result */
  27465         }
  27466         if (js_parse_expect_semi(s))
  27467             goto fail;
  27468         break;
  27469     }
  27470 done:
  27471     JS_FreeAtom(ctx, label_name);
  27472     return 0;
  27473 fail:
  27474     JS_FreeAtom(ctx, label_name);
  27475     return -1;
  27476 }
  27477 
  27478 /* 'name' is freed */
  27479 static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name)
  27480 {
  27481     JSModuleDef *m;
  27482     m = js_mallocz(ctx, sizeof(*m));
  27483     if (!m) {
  27484         JS_FreeAtom(ctx, name);
  27485         return NULL;
  27486     }
  27487     m->header.ref_count = 1;
  27488     m->module_name = name;
  27489     m->module_ns = JS_UNDEFINED;
  27490     m->func_obj = JS_UNDEFINED;
  27491     m->eval_exception = JS_UNDEFINED;
  27492     m->meta_obj = JS_UNDEFINED;
  27493     m->promise = JS_UNDEFINED;
  27494     m->resolving_funcs[0] = JS_UNDEFINED;
  27495     m->resolving_funcs[1] = JS_UNDEFINED;
  27496     list_add_tail(&m->link, &ctx->loaded_modules);
  27497     return m;
  27498 }
  27499 
  27500 static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,
  27501                                JS_MarkFunc *mark_func)
  27502 {
  27503     int i;
  27504 
  27505     for(i = 0; i < m->export_entries_count; i++) {
  27506         JSExportEntry *me = &m->export_entries[i];
  27507         if (me->export_type == JS_EXPORT_TYPE_LOCAL &&
  27508             me->u.local.var_ref) {
  27509             mark_func(rt, &me->u.local.var_ref->header);
  27510         }
  27511     }
  27512 
  27513     JS_MarkValue(rt, m->module_ns, mark_func);
  27514     JS_MarkValue(rt, m->func_obj, mark_func);
  27515     JS_MarkValue(rt, m->eval_exception, mark_func);
  27516     JS_MarkValue(rt, m->meta_obj, mark_func);
  27517     JS_MarkValue(rt, m->promise, mark_func);
  27518     JS_MarkValue(rt, m->resolving_funcs[0], mark_func);
  27519     JS_MarkValue(rt, m->resolving_funcs[1], mark_func);
  27520 }
  27521 
  27522 static void js_free_module_def(JSContext *ctx, JSModuleDef *m)
  27523 {
  27524     int i;
  27525 
  27526     JS_FreeAtom(ctx, m->module_name);
  27527 
  27528     for(i = 0; i < m->req_module_entries_count; i++) {
  27529         JSReqModuleEntry *rme = &m->req_module_entries[i];
  27530         JS_FreeAtom(ctx, rme->module_name);
  27531     }
  27532     js_free(ctx, m->req_module_entries);
  27533 
  27534     for(i = 0; i < m->export_entries_count; i++) {
  27535         JSExportEntry *me = &m->export_entries[i];
  27536         if (me->export_type == JS_EXPORT_TYPE_LOCAL)
  27537             free_var_ref(ctx->rt, me->u.local.var_ref);
  27538         JS_FreeAtom(ctx, me->export_name);
  27539         JS_FreeAtom(ctx, me->local_name);
  27540     }
  27541     js_free(ctx, m->export_entries);
  27542 
  27543     js_free(ctx, m->star_export_entries);
  27544 
  27545     for(i = 0; i < m->import_entries_count; i++) {
  27546         JSImportEntry *mi = &m->import_entries[i];
  27547         JS_FreeAtom(ctx, mi->import_name);
  27548     }
  27549     js_free(ctx, m->import_entries);
  27550     js_free(ctx, m->async_parent_modules);
  27551 
  27552     JS_FreeValue(ctx, m->module_ns);
  27553     JS_FreeValue(ctx, m->func_obj);
  27554     JS_FreeValue(ctx, m->eval_exception);
  27555     JS_FreeValue(ctx, m->meta_obj);
  27556     JS_FreeValue(ctx, m->promise);
  27557     JS_FreeValue(ctx, m->resolving_funcs[0]);
  27558     JS_FreeValue(ctx, m->resolving_funcs[1]);
  27559     list_del(&m->link);
  27560     js_free(ctx, m);
  27561 }
  27562 
  27563 static int add_req_module_entry(JSContext *ctx, JSModuleDef *m,
  27564                                 JSAtom module_name)
  27565 {
  27566     JSReqModuleEntry *rme;
  27567     int i;
  27568 
  27569     /* no need to add the module request if it is already present */
  27570     for(i = 0; i < m->req_module_entries_count; i++) {
  27571         rme = &m->req_module_entries[i];
  27572         if (rme->module_name == module_name)
  27573             return i;
  27574     }
  27575 
  27576     if (js_resize_array(ctx, (void **)&m->req_module_entries,
  27577                         sizeof(JSReqModuleEntry),
  27578                         &m->req_module_entries_size,
  27579                         m->req_module_entries_count + 1))
  27580         return -1;
  27581     rme = &m->req_module_entries[m->req_module_entries_count++];
  27582     rme->module_name = JS_DupAtom(ctx, module_name);
  27583     rme->module = NULL;
  27584     return i;
  27585 }
  27586 
  27587 static JSExportEntry *find_export_entry(JSContext *ctx, JSModuleDef *m,
  27588                                         JSAtom export_name)
  27589 {
  27590     JSExportEntry *me;
  27591     int i;
  27592     for(i = 0; i < m->export_entries_count; i++) {
  27593         me = &m->export_entries[i];
  27594         if (me->export_name == export_name)
  27595             return me;
  27596     }
  27597     return NULL;
  27598 }
  27599 
  27600 static JSExportEntry *add_export_entry2(JSContext *ctx,
  27601                                         JSParseState *s, JSModuleDef *m,
  27602                                        JSAtom local_name, JSAtom export_name,
  27603                                        JSExportTypeEnum export_type)
  27604 {
  27605     JSExportEntry *me;
  27606 
  27607     if (find_export_entry(ctx, m, export_name)) {
  27608         char buf1[ATOM_GET_STR_BUF_SIZE];
  27609         if (s) {
  27610             js_parse_error(s, "duplicate exported name '%s'",
  27611                            JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name));
  27612         } else {
  27613             JS_ThrowSyntaxErrorAtom(ctx, "duplicate exported name '%s'", export_name);
  27614         }
  27615         return NULL;
  27616     }
  27617 
  27618     if (js_resize_array(ctx, (void **)&m->export_entries,
  27619                         sizeof(JSExportEntry),
  27620                         &m->export_entries_size,
  27621                         m->export_entries_count + 1))
  27622         return NULL;
  27623     me = &m->export_entries[m->export_entries_count++];
  27624     memset(me, 0, sizeof(*me));
  27625     me->local_name = JS_DupAtom(ctx, local_name);
  27626     me->export_name = JS_DupAtom(ctx, export_name);
  27627     me->export_type = export_type;
  27628     return me;
  27629 }
  27630 
  27631 static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m,
  27632                                        JSAtom local_name, JSAtom export_name,
  27633                                        JSExportTypeEnum export_type)
  27634 {
  27635     return add_export_entry2(s->ctx, s, m, local_name, export_name,
  27636                              export_type);
  27637 }
  27638 
  27639 static int add_star_export_entry(JSContext *ctx, JSModuleDef *m,
  27640                                  int req_module_idx)
  27641 {
  27642     JSStarExportEntry *se;
  27643 
  27644     if (js_resize_array(ctx, (void **)&m->star_export_entries,
  27645                         sizeof(JSStarExportEntry),
  27646                         &m->star_export_entries_size,
  27647                         m->star_export_entries_count + 1))
  27648         return -1;
  27649     se = &m->star_export_entries[m->star_export_entries_count++];
  27650     se->req_module_idx = req_module_idx;
  27651     return 0;
  27652 }
  27653 
  27654 /* create a C module */
  27655 JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str,
  27656                            JSModuleInitFunc *func)
  27657 {
  27658     JSModuleDef *m;
  27659     JSAtom name;
  27660     name = JS_NewAtom(ctx, name_str);
  27661     if (name == JS_ATOM_NULL)
  27662         return NULL;
  27663     m = js_new_module_def(ctx, name);
  27664     m->init_func = func;
  27665     return m;
  27666 }
  27667 
  27668 int JS_AddModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name)
  27669 {
  27670     JSExportEntry *me;
  27671     JSAtom name;
  27672     name = JS_NewAtom(ctx, export_name);
  27673     if (name == JS_ATOM_NULL)
  27674         return -1;
  27675     me = add_export_entry2(ctx, NULL, m, JS_ATOM_NULL, name,
  27676                            JS_EXPORT_TYPE_LOCAL);
  27677     JS_FreeAtom(ctx, name);
  27678     if (!me)
  27679         return -1;
  27680     else
  27681         return 0;
  27682 }
  27683 
  27684 int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name,
  27685                        JSValue val)
  27686 {
  27687     JSExportEntry *me;
  27688     JSAtom name;
  27689     name = JS_NewAtom(ctx, export_name);
  27690     if (name == JS_ATOM_NULL)
  27691         goto fail;
  27692     me = find_export_entry(ctx, m, name);
  27693     JS_FreeAtom(ctx, name);
  27694     if (!me)
  27695         goto fail;
  27696     set_value(ctx, me->u.local.var_ref->pvalue, val);
  27697     return 0;
  27698  fail:
  27699     JS_FreeValue(ctx, val);
  27700     return -1;
  27701 }
  27702 
  27703 void JS_SetModuleLoaderFunc(JSRuntime *rt,
  27704                             JSModuleNormalizeFunc *module_normalize,
  27705                             JSModuleLoaderFunc *module_loader, void *opaque)
  27706 {
  27707     rt->module_normalize_func = module_normalize;
  27708     rt->module_loader_func = module_loader;
  27709     rt->module_loader_opaque = opaque;
  27710 }
  27711 
  27712 /* default module filename normalizer */
  27713 static char *js_default_module_normalize_name(JSContext *ctx,
  27714                                               const char *base_name,
  27715                                               const char *name)
  27716 {
  27717     char *filename, *p;
  27718     const char *r;
  27719     int cap;
  27720     int len;
  27721 
  27722     if (name[0] != '.') {
  27723         /* if no initial dot, the module name is not modified */
  27724         return js_strdup(ctx, name);
  27725     }
  27726 
  27727     p = strrchr(base_name, '/');
  27728     if (p)
  27729         len = p - base_name;
  27730     else
  27731         len = 0;
  27732 
  27733     cap = len + strlen(name) + 1 + 1;
  27734     filename = js_malloc(ctx, cap);
  27735     if (!filename)
  27736         return NULL;
  27737     memcpy(filename, base_name, len);
  27738     filename[len] = '\0';
  27739 
  27740     /* we only normalize the leading '..' or '.' */
  27741     r = name;
  27742     for(;;) {
  27743         if (r[0] == '.' && r[1] == '/') {
  27744             r += 2;
  27745         } else if (r[0] == '.' && r[1] == '.' && r[2] == '/') {
  27746             /* remove the last path element of filename, except if "."
  27747                or ".." */
  27748             if (filename[0] == '\0')
  27749                 break;
  27750             p = strrchr(filename, '/');
  27751             if (!p)
  27752                 p = filename;
  27753             else
  27754                 p++;
  27755             if (!strcmp(p, ".") || !strcmp(p, ".."))
  27756                 break;
  27757             if (p > filename)
  27758                 p--;
  27759             *p = '\0';
  27760             r += 3;
  27761         } else {
  27762             break;
  27763         }
  27764     }
  27765     if (filename[0] != '\0')
  27766         pstrcat(filename, cap, "/");
  27767     pstrcat(filename, cap, r);
  27768     //    printf("normalize: %s %s -> %s\n", base_name, name, filename);
  27769     return filename;
  27770 }
  27771 
  27772 static JSModuleDef *js_find_loaded_module(JSContext *ctx, JSAtom name)
  27773 {
  27774     struct list_head *el;
  27775     JSModuleDef *m;
  27776 
  27777     /* first look at the loaded modules */
  27778     list_for_each(el, &ctx->loaded_modules) {
  27779         m = list_entry(el, JSModuleDef, link);
  27780         if (m->module_name == name)
  27781             return m;
  27782     }
  27783     return NULL;
  27784 }
  27785 
  27786 /* return NULL in case of exception (e.g. module could not be loaded) */
  27787 static JSModuleDef *js_host_resolve_imported_module(JSContext *ctx,
  27788                                                     const char *base_cname,
  27789                                                     const char *cname1)
  27790 {
  27791     JSRuntime *rt = ctx->rt;
  27792     JSModuleDef *m;
  27793     char *cname;
  27794     JSAtom module_name;
  27795 
  27796     if (!rt->module_normalize_func) {
  27797         cname = js_default_module_normalize_name(ctx, base_cname, cname1);
  27798     } else {
  27799         cname = rt->module_normalize_func(ctx, base_cname, cname1,
  27800                                           rt->module_loader_opaque);
  27801     }
  27802     if (!cname)
  27803         return NULL;
  27804 
  27805     module_name = JS_NewAtom(ctx, cname);
  27806     if (module_name == JS_ATOM_NULL) {
  27807         js_free(ctx, cname);
  27808         return NULL;
  27809     }
  27810 
  27811     /* first look at the loaded modules */
  27812     m = js_find_loaded_module(ctx, module_name);
  27813     if (m) {
  27814         js_free(ctx, cname);
  27815         JS_FreeAtom(ctx, module_name);
  27816         return m;
  27817     }
  27818 
  27819     JS_FreeAtom(ctx, module_name);
  27820 
  27821     /* load the module */
  27822     if (!rt->module_loader_func) {
  27823         /* XXX: use a syntax error ? */
  27824         JS_ThrowReferenceError(ctx, "could not load module '%s'",
  27825                                cname);
  27826         js_free(ctx, cname);
  27827         return NULL;
  27828     }
  27829 
  27830     m = rt->module_loader_func(ctx, cname, rt->module_loader_opaque);
  27831     js_free(ctx, cname);
  27832     return m;
  27833 }
  27834 
  27835 static JSModuleDef *js_host_resolve_imported_module_atom(JSContext *ctx,
  27836                                                     JSAtom base_module_name,
  27837                                                     JSAtom module_name1)
  27838 {
  27839     const char *base_cname, *cname;
  27840     JSModuleDef *m;
  27841 
  27842     base_cname = JS_AtomToCString(ctx, base_module_name);
  27843     if (!base_cname)
  27844         return NULL;
  27845     cname = JS_AtomToCString(ctx, module_name1);
  27846     if (!cname) {
  27847         JS_FreeCString(ctx, base_cname);
  27848         return NULL;
  27849     }
  27850     m = js_host_resolve_imported_module(ctx, base_cname, cname);
  27851     JS_FreeCString(ctx, base_cname);
  27852     JS_FreeCString(ctx, cname);
  27853     return m;
  27854 }
  27855 
  27856 typedef struct JSResolveEntry {
  27857     JSModuleDef *module;
  27858     JSAtom name;
  27859 } JSResolveEntry;
  27860 
  27861 typedef struct JSResolveState {
  27862     JSResolveEntry *array;
  27863     int size;
  27864     int count;
  27865 } JSResolveState;
  27866 
  27867 static int find_resolve_entry(JSResolveState *s,
  27868                               JSModuleDef *m, JSAtom name)
  27869 {
  27870     int i;
  27871     for(i = 0; i < s->count; i++) {
  27872         JSResolveEntry *re = &s->array[i];
  27873         if (re->module == m && re->name == name)
  27874             return i;
  27875     }
  27876     return -1;
  27877 }
  27878 
  27879 static int add_resolve_entry(JSContext *ctx, JSResolveState *s,
  27880                              JSModuleDef *m, JSAtom name)
  27881 {
  27882     JSResolveEntry *re;
  27883 
  27884     if (js_resize_array(ctx, (void **)&s->array,
  27885                         sizeof(JSResolveEntry),
  27886                         &s->size, s->count + 1))
  27887         return -1;
  27888     re = &s->array[s->count++];
  27889     re->module = m;
  27890     re->name = JS_DupAtom(ctx, name);
  27891     return 0;
  27892 }
  27893 
  27894 typedef enum JSResolveResultEnum {
  27895     JS_RESOLVE_RES_EXCEPTION = -1, /* memory alloc error */
  27896     JS_RESOLVE_RES_FOUND = 0,
  27897     JS_RESOLVE_RES_NOT_FOUND,
  27898     JS_RESOLVE_RES_CIRCULAR,
  27899     JS_RESOLVE_RES_AMBIGUOUS,
  27900 } JSResolveResultEnum;
  27901 
  27902 static JSResolveResultEnum js_resolve_export1(JSContext *ctx,
  27903                                               JSModuleDef **pmodule,
  27904                                               JSExportEntry **pme,
  27905                                               JSModuleDef *m,
  27906                                               JSAtom export_name,
  27907                                               JSResolveState *s)
  27908 {
  27909     JSExportEntry *me;
  27910 
  27911     *pmodule = NULL;
  27912     *pme = NULL;
  27913     if (find_resolve_entry(s, m, export_name) >= 0)
  27914         return JS_RESOLVE_RES_CIRCULAR;
  27915     if (add_resolve_entry(ctx, s, m, export_name) < 0)
  27916         return JS_RESOLVE_RES_EXCEPTION;
  27917     me = find_export_entry(ctx, m, export_name);
  27918     if (me) {
  27919         if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
  27920             /* local export */
  27921             *pmodule = m;
  27922             *pme = me;
  27923             return JS_RESOLVE_RES_FOUND;
  27924         } else {
  27925             /* indirect export */
  27926             JSModuleDef *m1;
  27927             m1 = m->req_module_entries[me->u.req_module_idx].module;
  27928             if (me->local_name == JS_ATOM__star_) {
  27929                 /* export ns from */
  27930                 *pmodule = m;
  27931                 *pme = me;
  27932                 return JS_RESOLVE_RES_FOUND;
  27933             } else {
  27934                 return js_resolve_export1(ctx, pmodule, pme, m1,
  27935                                           me->local_name, s);
  27936             }
  27937         }
  27938     } else {
  27939         if (export_name != JS_ATOM_default) {
  27940             /* not found in direct or indirect exports: try star exports */
  27941             int i;
  27942 
  27943             for(i = 0; i < m->star_export_entries_count; i++) {
  27944                 JSStarExportEntry *se = &m->star_export_entries[i];
  27945                 JSModuleDef *m1, *res_m;
  27946                 JSExportEntry *res_me;
  27947                 JSResolveResultEnum ret;
  27948 
  27949                 m1 = m->req_module_entries[se->req_module_idx].module;
  27950                 ret = js_resolve_export1(ctx, &res_m, &res_me, m1,
  27951                                          export_name, s);
  27952                 if (ret == JS_RESOLVE_RES_AMBIGUOUS ||
  27953                     ret == JS_RESOLVE_RES_EXCEPTION) {
  27954                     return ret;
  27955                 } else if (ret == JS_RESOLVE_RES_FOUND) {
  27956                     if (*pme != NULL) {
  27957                         if (*pmodule != res_m ||
  27958                             res_me->local_name != (*pme)->local_name) {
  27959                             *pmodule = NULL;
  27960                             *pme = NULL;
  27961                             return JS_RESOLVE_RES_AMBIGUOUS;
  27962                         }
  27963                     } else {
  27964                         *pmodule = res_m;
  27965                         *pme = res_me;
  27966                     }
  27967                 }
  27968             }
  27969             if (*pme != NULL)
  27970                 return JS_RESOLVE_RES_FOUND;
  27971         }
  27972         return JS_RESOLVE_RES_NOT_FOUND;
  27973     }
  27974 }
  27975 
  27976 /* If the return value is JS_RESOLVE_RES_FOUND, return the module
  27977   (*pmodule) and the corresponding local export entry
  27978   (*pme). Otherwise return (NULL, NULL) */
  27979 static JSResolveResultEnum js_resolve_export(JSContext *ctx,
  27980                                              JSModuleDef **pmodule,
  27981                                              JSExportEntry **pme,
  27982                                              JSModuleDef *m,
  27983                                              JSAtom export_name)
  27984 {
  27985     JSResolveState ss, *s = &ss;
  27986     int i;
  27987     JSResolveResultEnum ret;
  27988 
  27989     s->array = NULL;
  27990     s->size = 0;
  27991     s->count = 0;
  27992 
  27993     ret = js_resolve_export1(ctx, pmodule, pme, m, export_name, s);
  27994 
  27995     for(i = 0; i < s->count; i++)
  27996         JS_FreeAtom(ctx, s->array[i].name);
  27997     js_free(ctx, s->array);
  27998 
  27999     return ret;
  28000 }
  28001 
  28002 static void js_resolve_export_throw_error(JSContext *ctx,
  28003                                           JSResolveResultEnum res,
  28004                                           JSModuleDef *m, JSAtom export_name)
  28005 {
  28006     char buf1[ATOM_GET_STR_BUF_SIZE];
  28007     char buf2[ATOM_GET_STR_BUF_SIZE];
  28008     switch(res) {
  28009     case JS_RESOLVE_RES_EXCEPTION:
  28010         break;
  28011     default:
  28012     case JS_RESOLVE_RES_NOT_FOUND:
  28013         JS_ThrowSyntaxError(ctx, "Could not find export '%s' in module '%s'",
  28014                             JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),
  28015                             JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));
  28016         break;
  28017     case JS_RESOLVE_RES_CIRCULAR:
  28018         JS_ThrowSyntaxError(ctx, "circular reference when looking for export '%s' in module '%s'",
  28019                             JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),
  28020                             JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));
  28021         break;
  28022     case JS_RESOLVE_RES_AMBIGUOUS:
  28023         JS_ThrowSyntaxError(ctx, "export '%s' in module '%s' is ambiguous",
  28024                             JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),
  28025                             JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));
  28026         break;
  28027     }
  28028 }
  28029 
  28030 
  28031 typedef enum {
  28032     EXPORTED_NAME_AMBIGUOUS,
  28033     EXPORTED_NAME_NORMAL,
  28034     EXPORTED_NAME_NS,
  28035 } ExportedNameEntryEnum;
  28036 
  28037 typedef struct ExportedNameEntry {
  28038     JSAtom export_name;
  28039     ExportedNameEntryEnum export_type;
  28040     union {
  28041         JSExportEntry *me; /* using when the list is built */
  28042         JSVarRef *var_ref; /* EXPORTED_NAME_NORMAL */
  28043         JSModuleDef *module; /* for EXPORTED_NAME_NS */
  28044     } u;
  28045 } ExportedNameEntry;
  28046 
  28047 typedef struct GetExportNamesState {
  28048     JSModuleDef **modules;
  28049     int modules_size;
  28050     int modules_count;
  28051 
  28052     ExportedNameEntry *exported_names;
  28053     int exported_names_size;
  28054     int exported_names_count;
  28055 } GetExportNamesState;
  28056 
  28057 static int find_exported_name(GetExportNamesState *s, JSAtom name)
  28058 {
  28059     int i;
  28060     for(i = 0; i < s->exported_names_count; i++) {
  28061         if (s->exported_names[i].export_name == name)
  28062             return i;
  28063     }
  28064     return -1;
  28065 }
  28066 
  28067 static __exception int get_exported_names(JSContext *ctx,
  28068                                           GetExportNamesState *s,
  28069                                           JSModuleDef *m, BOOL from_star)
  28070 {
  28071     ExportedNameEntry *en;
  28072     int i, j;
  28073 
  28074     /* check circular reference */
  28075     for(i = 0; i < s->modules_count; i++) {
  28076         if (s->modules[i] == m)
  28077             return 0;
  28078     }
  28079     if (js_resize_array(ctx, (void **)&s->modules, sizeof(s->modules[0]),
  28080                         &s->modules_size, s->modules_count + 1))
  28081         return -1;
  28082     s->modules[s->modules_count++] = m;
  28083 
  28084     for(i = 0; i < m->export_entries_count; i++) {
  28085         JSExportEntry *me = &m->export_entries[i];
  28086         if (from_star && me->export_name == JS_ATOM_default)
  28087             continue;
  28088         j = find_exported_name(s, me->export_name);
  28089         if (j < 0) {
  28090             if (js_resize_array(ctx, (void **)&s->exported_names, sizeof(s->exported_names[0]),
  28091                                 &s->exported_names_size,
  28092                                 s->exported_names_count + 1))
  28093                 return -1;
  28094             en = &s->exported_names[s->exported_names_count++];
  28095             en->export_name = me->export_name;
  28096             /* avoid a second lookup for simple module exports */
  28097             if (from_star || me->export_type != JS_EXPORT_TYPE_LOCAL)
  28098                 en->u.me = NULL;
  28099             else
  28100                 en->u.me = me;
  28101         } else {
  28102             en = &s->exported_names[j];
  28103             en->u.me = NULL;
  28104         }
  28105     }
  28106     for(i = 0; i < m->star_export_entries_count; i++) {
  28107         JSStarExportEntry *se = &m->star_export_entries[i];
  28108         JSModuleDef *m1;
  28109         m1 = m->req_module_entries[se->req_module_idx].module;
  28110         if (get_exported_names(ctx, s, m1, TRUE))
  28111             return -1;
  28112     }
  28113     return 0;
  28114 }
  28115 
  28116 /* Unfortunately, the spec gives a different behavior from GetOwnProperty ! */
  28117 static int js_module_ns_has(JSContext *ctx, JSValueConst obj, JSAtom atom)
  28118 {
  28119     return (find_own_property1(JS_VALUE_GET_OBJ(obj), atom) != NULL);
  28120 }
  28121 
  28122 static const JSClassExoticMethods js_module_ns_exotic_methods = {
  28123     .has_property = js_module_ns_has,
  28124 };
  28125 
  28126 static int exported_names_cmp(const void *p1, const void *p2, void *opaque)
  28127 {
  28128     JSContext *ctx = opaque;
  28129     const ExportedNameEntry *me1 = p1;
  28130     const ExportedNameEntry *me2 = p2;
  28131     JSValue str1, str2;
  28132     int ret;
  28133 
  28134     /* XXX: should avoid allocation memory in atom comparison */
  28135     str1 = JS_AtomToString(ctx, me1->export_name);
  28136     str2 = JS_AtomToString(ctx, me2->export_name);
  28137     if (JS_IsException(str1) || JS_IsException(str2)) {
  28138         /* XXX: raise an error ? */
  28139         ret = 0;
  28140     } else {
  28141         ret = js_string_compare(ctx, JS_VALUE_GET_STRING(str1),
  28142                                 JS_VALUE_GET_STRING(str2));
  28143     }
  28144     JS_FreeValue(ctx, str1);
  28145     JS_FreeValue(ctx, str2);
  28146     return ret;
  28147 }
  28148 
  28149 static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,
  28150                                      void *opaque)
  28151 {
  28152     JSModuleDef *m = opaque;
  28153     return JS_GetModuleNamespace(ctx, m);
  28154 }
  28155 
  28156 static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m)
  28157 {
  28158     JSValue obj;
  28159     JSObject *p;
  28160     GetExportNamesState s_s, *s = &s_s;
  28161     int i, ret;
  28162     JSProperty *pr;
  28163 
  28164     obj = JS_NewObjectClass(ctx, JS_CLASS_MODULE_NS);
  28165     if (JS_IsException(obj))
  28166         return obj;
  28167     p = JS_VALUE_GET_OBJ(obj);
  28168 
  28169     memset(s, 0, sizeof(*s));
  28170     ret = get_exported_names(ctx, s, m, FALSE);
  28171     js_free(ctx, s->modules);
  28172     if (ret)
  28173         goto fail;
  28174 
  28175     /* Resolve the exported names. The ambiguous exports are removed */
  28176     for(i = 0; i < s->exported_names_count; i++) {
  28177         ExportedNameEntry *en = &s->exported_names[i];
  28178         JSResolveResultEnum res;
  28179         JSExportEntry *res_me;
  28180         JSModuleDef *res_m;
  28181 
  28182         if (en->u.me) {
  28183             res_me = en->u.me; /* fast case: no resolution needed */
  28184             res_m = m;
  28185             res = JS_RESOLVE_RES_FOUND;
  28186         } else {
  28187             res = js_resolve_export(ctx, &res_m, &res_me, m,
  28188                                     en->export_name);
  28189         }
  28190         if (res != JS_RESOLVE_RES_FOUND) {
  28191             if (res != JS_RESOLVE_RES_AMBIGUOUS) {
  28192                 js_resolve_export_throw_error(ctx, res, m, en->export_name);
  28193                 goto fail;
  28194             }
  28195             en->export_type = EXPORTED_NAME_AMBIGUOUS;
  28196         } else {
  28197             if (res_me->local_name == JS_ATOM__star_) {
  28198                 en->export_type = EXPORTED_NAME_NS;
  28199                 en->u.module = res_m->req_module_entries[res_me->u.req_module_idx].module;
  28200             } else {
  28201                 en->export_type = EXPORTED_NAME_NORMAL;
  28202                 if (res_me->u.local.var_ref) {
  28203                     en->u.var_ref = res_me->u.local.var_ref;
  28204                 } else {
  28205                     JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
  28206                     p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
  28207                     en->u.var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];
  28208                 }
  28209             }
  28210         }
  28211     }
  28212 
  28213     /* sort the exported names */
  28214     rqsort(s->exported_names, s->exported_names_count,
  28215            sizeof(s->exported_names[0]), exported_names_cmp, ctx);
  28216 
  28217     for(i = 0; i < s->exported_names_count; i++) {
  28218         ExportedNameEntry *en = &s->exported_names[i];
  28219         switch(en->export_type) {
  28220         case EXPORTED_NAME_NORMAL:
  28221             {
  28222                 JSVarRef *var_ref = en->u.var_ref;
  28223                 pr = add_property(ctx, p, en->export_name,
  28224                                   JS_PROP_ENUMERABLE | JS_PROP_WRITABLE |
  28225                                   JS_PROP_VARREF);
  28226                 if (!pr)
  28227                     goto fail;
  28228                 var_ref->header.ref_count++;
  28229                 pr->u.var_ref = var_ref;
  28230             }
  28231             break;
  28232         case EXPORTED_NAME_NS:
  28233             /* the exported namespace must be created on demand */
  28234             if (JS_DefineAutoInitProperty(ctx, obj,
  28235                                           en->export_name,
  28236                                           JS_AUTOINIT_ID_MODULE_NS,
  28237                                           en->u.module, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0)
  28238                 goto fail;
  28239             break;
  28240         default:
  28241             break;
  28242         }
  28243     }
  28244 
  28245     js_free(ctx, s->exported_names);
  28246 
  28247     JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_toStringTag,
  28248                            JS_AtomToString(ctx, JS_ATOM_Module),
  28249                            0);
  28250 
  28251     p->extensible = FALSE;
  28252     return obj;
  28253  fail:
  28254     js_free(ctx, s->exported_names);
  28255     JS_FreeValue(ctx, obj);
  28256     return JS_EXCEPTION;
  28257 }
  28258 
  28259 JSValue JS_GetModuleNamespace(JSContext *ctx, JSModuleDef *m)
  28260 {
  28261     if (JS_IsUndefined(m->module_ns)) {
  28262         JSValue val;
  28263         val = js_build_module_ns(ctx, m);
  28264         if (JS_IsException(val))
  28265             return JS_EXCEPTION;
  28266         m->module_ns = val;
  28267     }
  28268     return JS_DupValue(ctx, m->module_ns);
  28269 }
  28270 
  28271 /* Load all the required modules for module 'm' */
  28272 static int js_resolve_module(JSContext *ctx, JSModuleDef *m)
  28273 {
  28274     int i;
  28275     JSModuleDef *m1;
  28276 
  28277     if (m->resolved)
  28278         return 0;
  28279 #ifdef DUMP_MODULE_RESOLVE
  28280     {
  28281         char buf1[ATOM_GET_STR_BUF_SIZE];
  28282         printf("resolving module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
  28283     }
  28284 #endif
  28285     m->resolved = TRUE;
  28286     /* resolve each requested module */
  28287     for(i = 0; i < m->req_module_entries_count; i++) {
  28288         JSReqModuleEntry *rme = &m->req_module_entries[i];
  28289         m1 = js_host_resolve_imported_module_atom(ctx, m->module_name,
  28290                                                   rme->module_name);
  28291         if (!m1)
  28292             return -1;
  28293         rme->module = m1;
  28294         /* already done in js_host_resolve_imported_module() except if
  28295            the module was loaded with JS_EvalBinary() */
  28296         if (js_resolve_module(ctx, m1) < 0)
  28297             return -1;
  28298     }
  28299     return 0;
  28300 }
  28301 
  28302 static JSVarRef *js_create_module_var(JSContext *ctx, BOOL is_lexical)
  28303 {
  28304     JSVarRef *var_ref;
  28305     var_ref = js_malloc(ctx, sizeof(JSVarRef));
  28306     if (!var_ref)
  28307         return NULL;
  28308     var_ref->header.ref_count = 1;
  28309     if (is_lexical)
  28310         var_ref->value = JS_UNINITIALIZED;
  28311     else
  28312         var_ref->value = JS_UNDEFINED;
  28313     var_ref->pvalue = &var_ref->value;
  28314     var_ref->is_detached = TRUE;
  28315     add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
  28316     return var_ref;
  28317 }
  28318 
  28319 /* Create the <eval> function associated with the module */
  28320 static int js_create_module_bytecode_function(JSContext *ctx, JSModuleDef *m)
  28321 {
  28322     JSFunctionBytecode *b;
  28323     int i;
  28324     JSVarRef **var_refs;
  28325     JSValue func_obj, bfunc;
  28326     JSObject *p;
  28327 
  28328     bfunc = m->func_obj;
  28329     func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
  28330                                       JS_CLASS_BYTECODE_FUNCTION);
  28331 
  28332     if (JS_IsException(func_obj))
  28333         return -1;
  28334     b = JS_VALUE_GET_PTR(bfunc);
  28335 
  28336     p = JS_VALUE_GET_OBJ(func_obj);
  28337     p->u.func.function_bytecode = b;
  28338     b->header.ref_count++;
  28339     p->u.func.home_object = NULL;
  28340     p->u.func.var_refs = NULL;
  28341     if (b->closure_var_count) {
  28342         var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count);
  28343         if (!var_refs)
  28344             goto fail;
  28345         p->u.func.var_refs = var_refs;
  28346 
  28347         /* create the global variables. The other variables are
  28348            imported from other modules */
  28349         for(i = 0; i < b->closure_var_count; i++) {
  28350             JSClosureVar *cv = &b->closure_var[i];
  28351             JSVarRef *var_ref;
  28352             if (cv->is_local) {
  28353                 var_ref = js_create_module_var(ctx, cv->is_lexical);
  28354                 if (!var_ref)
  28355                     goto fail;
  28356 #ifdef DUMP_MODULE_RESOLVE
  28357                 printf("local %d: %p\n", i, var_ref);
  28358 #endif
  28359                 var_refs[i] = var_ref;
  28360             }
  28361         }
  28362     }
  28363     m->func_obj = func_obj;
  28364     JS_FreeValue(ctx, bfunc);
  28365     return 0;
  28366  fail:
  28367     JS_FreeValue(ctx, func_obj);
  28368     return -1;
  28369 }
  28370 
  28371 /* must be done before js_link_module() because of cyclic references */
  28372 static int js_create_module_function(JSContext *ctx, JSModuleDef *m)
  28373 {
  28374     BOOL is_c_module;
  28375     int i;
  28376     JSVarRef *var_ref;
  28377 
  28378     if (m->func_created)
  28379         return 0;
  28380 
  28381     is_c_module = (m->init_func != NULL);
  28382 
  28383     if (is_c_module) {
  28384         /* initialize the exported variables */
  28385         for(i = 0; i < m->export_entries_count; i++) {
  28386             JSExportEntry *me = &m->export_entries[i];
  28387             if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
  28388                 var_ref = js_create_module_var(ctx, FALSE);
  28389                 if (!var_ref)
  28390                     return -1;
  28391                 me->u.local.var_ref = var_ref;
  28392             }
  28393         }
  28394     } else {
  28395         if (js_create_module_bytecode_function(ctx, m))
  28396             return -1;
  28397     }
  28398     m->func_created = TRUE;
  28399 
  28400     /* do it on the dependencies */
  28401 
  28402     for(i = 0; i < m->req_module_entries_count; i++) {
  28403         JSReqModuleEntry *rme = &m->req_module_entries[i];
  28404         if (js_create_module_function(ctx, rme->module) < 0)
  28405             return -1;
  28406     }
  28407 
  28408     return 0;
  28409 }
  28410 
  28411 
  28412 /* Prepare a module to be executed by resolving all the imported
  28413    variables. */
  28414 static int js_inner_module_linking(JSContext *ctx, JSModuleDef *m,
  28415                                    JSModuleDef **pstack_top, int index)
  28416 {
  28417     int i;
  28418     JSImportEntry *mi;
  28419     JSModuleDef *m1;
  28420     JSVarRef **var_refs, *var_ref;
  28421     JSObject *p;
  28422     BOOL is_c_module;
  28423     JSValue ret_val;
  28424 
  28425     if (js_check_stack_overflow(ctx->rt, 0)) {
  28426         JS_ThrowStackOverflow(ctx);
  28427         return -1;
  28428     }
  28429 
  28430 #ifdef DUMP_MODULE_RESOLVE
  28431     {
  28432         char buf1[ATOM_GET_STR_BUF_SIZE];
  28433         printf("js_inner_module_linking '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
  28434     }
  28435 #endif
  28436 
  28437     if (m->status == JS_MODULE_STATUS_LINKING ||
  28438         m->status == JS_MODULE_STATUS_LINKED ||
  28439         m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
  28440         m->status == JS_MODULE_STATUS_EVALUATED)
  28441         return index;
  28442 
  28443     assert(m->status == JS_MODULE_STATUS_UNLINKED);
  28444     m->status = JS_MODULE_STATUS_LINKING;
  28445     m->dfs_index = index;
  28446     m->dfs_ancestor_index = index;
  28447     index++;
  28448     /* push 'm' on stack */
  28449     m->stack_prev = *pstack_top;
  28450     *pstack_top = m;
  28451 
  28452     for(i = 0; i < m->req_module_entries_count; i++) {
  28453         JSReqModuleEntry *rme = &m->req_module_entries[i];
  28454         m1 = rme->module;
  28455         index = js_inner_module_linking(ctx, m1, pstack_top, index);
  28456         if (index < 0)
  28457             goto fail;
  28458         assert(m1->status == JS_MODULE_STATUS_LINKING ||
  28459                m1->status == JS_MODULE_STATUS_LINKED ||
  28460                m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
  28461                m1->status == JS_MODULE_STATUS_EVALUATED);
  28462         if (m1->status == JS_MODULE_STATUS_LINKING) {
  28463             m->dfs_ancestor_index = min_int(m->dfs_ancestor_index,
  28464                                             m1->dfs_ancestor_index);
  28465         }
  28466     }
  28467 
  28468 #ifdef DUMP_MODULE_RESOLVE
  28469     {
  28470         char buf1[ATOM_GET_STR_BUF_SIZE];
  28471         printf("instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
  28472     }
  28473 #endif
  28474     /* check the indirect exports */
  28475     for(i = 0; i < m->export_entries_count; i++) {
  28476         JSExportEntry *me = &m->export_entries[i];
  28477         if (me->export_type == JS_EXPORT_TYPE_INDIRECT &&
  28478             me->local_name != JS_ATOM__star_) {
  28479             JSResolveResultEnum ret;
  28480             JSExportEntry *res_me;
  28481             JSModuleDef *res_m, *m1;
  28482             m1 = m->req_module_entries[me->u.req_module_idx].module;
  28483             ret = js_resolve_export(ctx, &res_m, &res_me, m1, me->local_name);
  28484             if (ret != JS_RESOLVE_RES_FOUND) {
  28485                 js_resolve_export_throw_error(ctx, ret, m, me->export_name);
  28486                 goto fail;
  28487             }
  28488         }
  28489     }
  28490 
  28491 #ifdef DUMP_MODULE_RESOLVE
  28492     {
  28493         printf("exported bindings:\n");
  28494         for(i = 0; i < m->export_entries_count; i++) {
  28495             JSExportEntry *me = &m->export_entries[i];
  28496             printf(" name="); print_atom(ctx, me->export_name);
  28497             printf(" local="); print_atom(ctx, me->local_name);
  28498             printf(" type=%d idx=%d\n", me->export_type, me->u.local.var_idx);
  28499         }
  28500     }
  28501 #endif
  28502 
  28503     is_c_module = (m->init_func != NULL);
  28504 
  28505     if (!is_c_module) {
  28506         p = JS_VALUE_GET_OBJ(m->func_obj);
  28507         var_refs = p->u.func.var_refs;
  28508 
  28509         for(i = 0; i < m->import_entries_count; i++) {
  28510             mi = &m->import_entries[i];
  28511 #ifdef DUMP_MODULE_RESOLVE
  28512             printf("import var_idx=%d name=", mi->var_idx);
  28513             print_atom(ctx, mi->import_name);
  28514             printf(": ");
  28515 #endif
  28516             m1 = m->req_module_entries[mi->req_module_idx].module;
  28517             if (mi->import_name == JS_ATOM__star_) {
  28518                 JSValue val;
  28519                 /* name space import */
  28520                 val = JS_GetModuleNamespace(ctx, m1);
  28521                 if (JS_IsException(val))
  28522                     goto fail;
  28523                 set_value(ctx, &var_refs[mi->var_idx]->value, val);
  28524 #ifdef DUMP_MODULE_RESOLVE
  28525                 printf("namespace\n");
  28526 #endif
  28527             } else {
  28528                 JSResolveResultEnum ret;
  28529                 JSExportEntry *res_me;
  28530                 JSModuleDef *res_m;
  28531                 JSObject *p1;
  28532 
  28533                 ret = js_resolve_export(ctx, &res_m,
  28534                                         &res_me, m1, mi->import_name);
  28535                 if (ret != JS_RESOLVE_RES_FOUND) {
  28536                     js_resolve_export_throw_error(ctx, ret, m1, mi->import_name);
  28537                     goto fail;
  28538                 }
  28539                 if (res_me->local_name == JS_ATOM__star_) {
  28540                     JSValue val;
  28541                     JSModuleDef *m2;
  28542                     /* name space import from */
  28543                     m2 = res_m->req_module_entries[res_me->u.req_module_idx].module;
  28544                     val = JS_GetModuleNamespace(ctx, m2);
  28545                     if (JS_IsException(val))
  28546                         goto fail;
  28547                     var_ref = js_create_module_var(ctx, TRUE);
  28548                     if (!var_ref) {
  28549                         JS_FreeValue(ctx, val);
  28550                         goto fail;
  28551                     }
  28552                     set_value(ctx, &var_ref->value, val);
  28553                     var_refs[mi->var_idx] = var_ref;
  28554 #ifdef DUMP_MODULE_RESOLVE
  28555                     printf("namespace from\n");
  28556 #endif
  28557                 } else {
  28558                     var_ref = res_me->u.local.var_ref;
  28559                     if (!var_ref) {
  28560                         p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
  28561                         var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];
  28562                     }
  28563                     var_ref->header.ref_count++;
  28564                     var_refs[mi->var_idx] = var_ref;
  28565 #ifdef DUMP_MODULE_RESOLVE
  28566                     printf("local export (var_ref=%p)\n", var_ref);
  28567 #endif
  28568                 }
  28569             }
  28570         }
  28571 
  28572         /* keep the exported variables in the module export entries (they
  28573            are used when the eval function is deleted and cannot be
  28574            initialized before in case imports are exported) */
  28575         for(i = 0; i < m->export_entries_count; i++) {
  28576             JSExportEntry *me = &m->export_entries[i];
  28577             if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
  28578                 var_ref = var_refs[me->u.local.var_idx];
  28579                 var_ref->header.ref_count++;
  28580                 me->u.local.var_ref = var_ref;
  28581             }
  28582         }
  28583 
  28584         /* initialize the global variables */
  28585         ret_val = JS_Call(ctx, m->func_obj, JS_TRUE, 0, NULL);
  28586         if (JS_IsException(ret_val))
  28587             goto fail;
  28588         JS_FreeValue(ctx, ret_val);
  28589     }
  28590 
  28591     assert(m->dfs_ancestor_index <= m->dfs_index);
  28592     if (m->dfs_index == m->dfs_ancestor_index) {
  28593         for(;;) {
  28594             /* pop m1 from stack */
  28595             m1 = *pstack_top;
  28596             *pstack_top = m1->stack_prev;
  28597             m1->status = JS_MODULE_STATUS_LINKED;
  28598             if (m1 == m)
  28599                 break;
  28600         }
  28601     }
  28602 
  28603 #ifdef DUMP_MODULE_RESOLVE
  28604     printf("js_inner_module_linking done\n");
  28605 #endif
  28606     return index;
  28607  fail:
  28608     return -1;
  28609 }
  28610 
  28611 /* Prepare a module to be executed by resolving all the imported
  28612    variables. */
  28613 static int js_link_module(JSContext *ctx, JSModuleDef *m)
  28614 {
  28615     JSModuleDef *stack_top, *m1;
  28616 
  28617 #ifdef DUMP_MODULE_RESOLVE
  28618     {
  28619         char buf1[ATOM_GET_STR_BUF_SIZE];
  28620         printf("js_link_module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
  28621     }
  28622 #endif
  28623     assert(m->status == JS_MODULE_STATUS_UNLINKED ||
  28624            m->status == JS_MODULE_STATUS_LINKED ||
  28625            m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
  28626            m->status == JS_MODULE_STATUS_EVALUATED);
  28627     stack_top = NULL;
  28628     if (js_inner_module_linking(ctx, m, &stack_top, 0) < 0) {
  28629         while (stack_top != NULL) {
  28630             m1 = stack_top;
  28631             assert(m1->status == JS_MODULE_STATUS_LINKING);
  28632             m1->status = JS_MODULE_STATUS_UNLINKED;
  28633             stack_top = m1->stack_prev;
  28634         }
  28635         return -1;
  28636     }
  28637     assert(stack_top == NULL);
  28638     assert(m->status == JS_MODULE_STATUS_LINKED ||
  28639            m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
  28640            m->status == JS_MODULE_STATUS_EVALUATED);
  28641     return 0;
  28642 }
  28643 
  28644 /* return JS_ATOM_NULL if the name cannot be found. Only works with
  28645    not striped bytecode functions. */
  28646 JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels)
  28647 {
  28648     JSStackFrame *sf;
  28649     JSFunctionBytecode *b;
  28650     JSObject *p;
  28651     /* XXX: currently we just use the filename of the englobing
  28652        function from the debug info. May need to add a ScriptOrModule
  28653        info in JSFunctionBytecode. */
  28654     sf = ctx->rt->current_stack_frame;
  28655     if (!sf)
  28656         return JS_ATOM_NULL;
  28657     while (n_stack_levels-- > 0) {
  28658         sf = sf->prev_frame;
  28659         if (!sf)
  28660             return JS_ATOM_NULL;
  28661     }
  28662     for(;;) {
  28663         if (JS_VALUE_GET_TAG(sf->cur_func) != JS_TAG_OBJECT)
  28664             return JS_ATOM_NULL;
  28665         p = JS_VALUE_GET_OBJ(sf->cur_func);
  28666         if (!js_class_has_bytecode(p->class_id))
  28667             return JS_ATOM_NULL;
  28668         b = p->u.func.function_bytecode;
  28669         if (!b->is_direct_or_indirect_eval) {
  28670             if (!b->has_debug)
  28671                 return JS_ATOM_NULL;
  28672             return JS_DupAtom(ctx, b->debug.filename);
  28673         } else {
  28674             sf = sf->prev_frame;
  28675             if (!sf)
  28676                 return JS_ATOM_NULL;
  28677         }
  28678     }
  28679 }
  28680 
  28681 JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m)
  28682 {
  28683     return JS_DupAtom(ctx, m->module_name);
  28684 }
  28685 
  28686 JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m)
  28687 {
  28688     JSValue obj;
  28689     /* allocate meta_obj only if requested to save memory */
  28690     obj = m->meta_obj;
  28691     if (JS_IsUndefined(obj)) {
  28692         obj = JS_NewObjectProto(ctx, JS_NULL);
  28693         if (JS_IsException(obj))
  28694             return JS_EXCEPTION;
  28695         m->meta_obj = obj;
  28696     }
  28697     return JS_DupValue(ctx, obj);
  28698 }
  28699 
  28700 static JSValue js_import_meta(JSContext *ctx)
  28701 {
  28702     JSAtom filename;
  28703     JSModuleDef *m;
  28704 
  28705     filename = JS_GetScriptOrModuleName(ctx, 0);
  28706     if (filename == JS_ATOM_NULL)
  28707         goto fail;
  28708 
  28709     /* XXX: inefficient, need to add a module or script pointer in
  28710        JSFunctionBytecode */
  28711     m = js_find_loaded_module(ctx, filename);
  28712     JS_FreeAtom(ctx, filename);
  28713     if (!m) {
  28714     fail:
  28715         JS_ThrowTypeError(ctx, "import.meta not supported in this context");
  28716         return JS_EXCEPTION;
  28717     }
  28718     return JS_GetImportMeta(ctx, m);
  28719 }
  28720 
  28721 static JSValue JS_NewModuleValue(JSContext *ctx, JSModuleDef *m)
  28722 {
  28723     return JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
  28724 }
  28725 
  28726 static JSValue js_load_module_rejected(JSContext *ctx, JSValueConst this_val,
  28727                                        int argc, JSValueConst *argv, int magic, JSValue *func_data)
  28728 {
  28729     JSValueConst *resolving_funcs = (JSValueConst *)func_data;
  28730     JSValueConst error;
  28731     JSValue ret;
  28732 
  28733     /* XXX: check if the test is necessary */
  28734     if (argc >= 1)
  28735         error = argv[0];
  28736     else
  28737         error = JS_UNDEFINED;
  28738     ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
  28739                   1, &error);
  28740     JS_FreeValue(ctx, ret);
  28741     return JS_UNDEFINED;
  28742 }
  28743 
  28744 static JSValue js_load_module_fulfilled(JSContext *ctx, JSValueConst this_val,
  28745                                         int argc, JSValueConst *argv, int magic, JSValue *func_data)
  28746 {
  28747     JSValueConst *resolving_funcs = (JSValueConst *)func_data;
  28748     JSModuleDef *m = JS_VALUE_GET_PTR(func_data[2]);
  28749     JSValue ret, ns;
  28750 
  28751     /* return the module namespace */
  28752     ns = JS_GetModuleNamespace(ctx, m);
  28753     if (JS_IsException(ns)) {
  28754         JSValue err = JS_GetException(ctx);
  28755         js_load_module_rejected(ctx, JS_UNDEFINED, 1, (JSValueConst *)&err, 0, func_data);
  28756         return JS_UNDEFINED;
  28757     }
  28758     ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED,
  28759                    1, (JSValueConst *)&ns);
  28760     JS_FreeValue(ctx, ret);
  28761     JS_FreeValue(ctx, ns);
  28762     return JS_UNDEFINED;
  28763 }
  28764 
  28765 static void JS_LoadModuleInternal(JSContext *ctx, const char *basename,
  28766                                   const char *filename,
  28767                                   JSValueConst *resolving_funcs)
  28768 {
  28769     JSValue evaluate_promise;
  28770     JSModuleDef *m;
  28771     JSValue ret, err, func_obj, evaluate_resolving_funcs[2];
  28772     JSValueConst func_data[3];
  28773 
  28774     m = js_host_resolve_imported_module(ctx, basename, filename);
  28775     if (!m)
  28776         goto fail;
  28777 
  28778     if (js_resolve_module(ctx, m) < 0) {
  28779         js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED);
  28780         goto fail;
  28781     }
  28782 
  28783     /* Evaluate the module code */
  28784     func_obj = JS_NewModuleValue(ctx, m);
  28785     evaluate_promise = JS_EvalFunction(ctx, func_obj);
  28786     if (JS_IsException(evaluate_promise)) {
  28787     fail:
  28788         err = JS_GetException(ctx);
  28789         ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
  28790                       1, (JSValueConst *)&err);
  28791         JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */
  28792         JS_FreeValue(ctx, err);
  28793         return;
  28794     }
  28795 
  28796     func_obj = JS_NewModuleValue(ctx, m);
  28797     func_data[0] = resolving_funcs[0];
  28798     func_data[1] = resolving_funcs[1];
  28799     func_data[2] = func_obj;
  28800     evaluate_resolving_funcs[0] = JS_NewCFunctionData(ctx, js_load_module_fulfilled, 0, 0, 3, func_data);
  28801     evaluate_resolving_funcs[1] = JS_NewCFunctionData(ctx, js_load_module_rejected, 0, 0, 3, func_data);
  28802     JS_FreeValue(ctx, func_obj);
  28803     ret = js_promise_then(ctx, evaluate_promise, 2, (JSValueConst *)evaluate_resolving_funcs);
  28804     JS_FreeValue(ctx, ret);
  28805     JS_FreeValue(ctx, evaluate_resolving_funcs[0]);
  28806     JS_FreeValue(ctx, evaluate_resolving_funcs[1]);
  28807     JS_FreeValue(ctx, evaluate_promise);
  28808 }
  28809 
  28810 /* Return a promise or an exception in case of memory error. Used by
  28811    os.Worker() */
  28812 JSValue JS_LoadModule(JSContext *ctx, const char *basename,
  28813                       const char *filename)
  28814 {
  28815     JSValue promise, resolving_funcs[2];
  28816 
  28817     promise = JS_NewPromiseCapability(ctx, resolving_funcs);
  28818     if (JS_IsException(promise))
  28819         return JS_EXCEPTION;
  28820     JS_LoadModuleInternal(ctx, basename, filename,
  28821                           (JSValueConst *)resolving_funcs);
  28822     JS_FreeValue(ctx, resolving_funcs[0]);
  28823     JS_FreeValue(ctx, resolving_funcs[1]);
  28824     return promise;
  28825 }
  28826 
  28827 static JSValue js_dynamic_import_job(JSContext *ctx,
  28828                                      int argc, JSValueConst *argv)
  28829 {
  28830     JSValueConst *resolving_funcs = argv;
  28831     JSValueConst basename_val = argv[2];
  28832     JSValueConst specifier = argv[3];
  28833     const char *basename = NULL, *filename;
  28834     JSValue ret, err;
  28835 
  28836     if (!JS_IsString(basename_val)) {
  28837         JS_ThrowTypeError(ctx, "no function filename for import()");
  28838         goto exception;
  28839     }
  28840     basename = JS_ToCString(ctx, basename_val);
  28841     if (!basename)
  28842         goto exception;
  28843 
  28844     filename = JS_ToCString(ctx, specifier);
  28845     if (!filename)
  28846         goto exception;
  28847 
  28848     JS_LoadModuleInternal(ctx, basename, filename,
  28849                           resolving_funcs);
  28850     JS_FreeCString(ctx, filename);
  28851     JS_FreeCString(ctx, basename);
  28852     return JS_UNDEFINED;
  28853  exception:
  28854     err = JS_GetException(ctx);
  28855     ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
  28856                    1, (JSValueConst *)&err);
  28857     JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */
  28858     JS_FreeValue(ctx, err);
  28859     JS_FreeCString(ctx, basename);
  28860     return JS_UNDEFINED;
  28861 }
  28862 
  28863 static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier)
  28864 {
  28865     JSAtom basename;
  28866     JSValue promise, resolving_funcs[2], basename_val;
  28867     JSValueConst args[4];
  28868 
  28869     basename = JS_GetScriptOrModuleName(ctx, 0);
  28870     if (basename == JS_ATOM_NULL)
  28871         basename_val = JS_NULL;
  28872     else
  28873         basename_val = JS_AtomToValue(ctx, basename);
  28874     JS_FreeAtom(ctx, basename);
  28875     if (JS_IsException(basename_val))
  28876         return basename_val;
  28877 
  28878     promise = JS_NewPromiseCapability(ctx, resolving_funcs);
  28879     if (JS_IsException(promise)) {
  28880         JS_FreeValue(ctx, basename_val);
  28881         return promise;
  28882     }
  28883 
  28884     args[0] = resolving_funcs[0];
  28885     args[1] = resolving_funcs[1];
  28886     args[2] = basename_val;
  28887     args[3] = specifier;
  28888 
  28889     /* cannot run JS_LoadModuleInternal synchronously because it would
  28890        cause an unexpected recursion in js_evaluate_module() */
  28891     JS_EnqueueJob(ctx, js_dynamic_import_job, 4, args);
  28892 
  28893     JS_FreeValue(ctx, basename_val);
  28894     JS_FreeValue(ctx, resolving_funcs[0]);
  28895     JS_FreeValue(ctx, resolving_funcs[1]);
  28896     return promise;
  28897 }
  28898 
  28899 static void js_set_module_evaluated(JSContext *ctx, JSModuleDef *m)
  28900 {
  28901     m->status = JS_MODULE_STATUS_EVALUATED;
  28902     if (!JS_IsUndefined(m->promise)) {
  28903         JSValue value, ret_val;
  28904         assert(m->cycle_root == m);
  28905         value = JS_UNDEFINED;
  28906         ret_val = JS_Call(ctx, m->resolving_funcs[0], JS_UNDEFINED,
  28907                           1, (JSValueConst *)&value);
  28908         JS_FreeValue(ctx, ret_val);
  28909     }
  28910 }
  28911 
  28912 typedef struct {
  28913     JSModuleDef **tab;
  28914     int count;
  28915     int size;
  28916 } ExecModuleList;
  28917 
  28918 /* XXX: slow. Could use a linked list instead of ExecModuleList */
  28919 static BOOL find_in_exec_module_list(ExecModuleList *exec_list, JSModuleDef *m)
  28920 {
  28921     int i;
  28922     for(i = 0; i < exec_list->count; i++) {
  28923         if (exec_list->tab[i] == m)
  28924             return TRUE;
  28925     }
  28926     return FALSE;
  28927 }
  28928 
  28929 static int gather_available_ancestors(JSContext *ctx, JSModuleDef *module,
  28930                                       ExecModuleList *exec_list)
  28931 {
  28932     int i;
  28933 
  28934     if (js_check_stack_overflow(ctx->rt, 0)) {
  28935         JS_ThrowStackOverflow(ctx);
  28936         return -1;
  28937     }
  28938     for(i = 0; i < module->async_parent_modules_count; i++) {
  28939         JSModuleDef *m = module->async_parent_modules[i];
  28940         if (!find_in_exec_module_list(exec_list, m) &&
  28941             !m->cycle_root->eval_has_exception) {
  28942             assert(m->status == JS_MODULE_STATUS_EVALUATING_ASYNC);
  28943             assert(!m->eval_has_exception);
  28944             assert(m->async_evaluation);
  28945             assert(m->pending_async_dependencies > 0);
  28946             m->pending_async_dependencies--;
  28947             if (m->pending_async_dependencies == 0) {
  28948                 if (js_resize_array(ctx, (void **)&exec_list->tab, sizeof(exec_list->tab[0]), &exec_list->size, exec_list->count + 1)) {
  28949                     return -1;
  28950                 }
  28951                 exec_list->tab[exec_list->count++] = m;
  28952                 if (!m->has_tla) {
  28953                     if (gather_available_ancestors(ctx, m, exec_list))
  28954                         return -1;
  28955                 }
  28956             }
  28957         }
  28958     }
  28959     return 0;
  28960 }
  28961 
  28962 static int exec_module_list_cmp(const void *p1, const void *p2, void *opaque)
  28963 {
  28964     JSModuleDef *m1 = *(JSModuleDef **)p1;
  28965     JSModuleDef *m2 = *(JSModuleDef **)p2;
  28966     return (m1->async_evaluation_timestamp > m2->async_evaluation_timestamp) -
  28967         (m1->async_evaluation_timestamp < m2->async_evaluation_timestamp);
  28968 }
  28969 
  28970 static int js_execute_async_module(JSContext *ctx, JSModuleDef *m);
  28971 static int js_execute_sync_module(JSContext *ctx, JSModuleDef *m,
  28972                                   JSValue *pvalue);
  28973 
  28974 static JSValue js_async_module_execution_rejected(JSContext *ctx, JSValueConst this_val,
  28975                                                   int argc, JSValueConst *argv, int magic, JSValue *func_data)
  28976 {
  28977     JSModuleDef *module = JS_VALUE_GET_PTR(func_data[0]);
  28978     JSValueConst error = argv[0];
  28979     int i;
  28980 
  28981     if (js_check_stack_overflow(ctx->rt, 0))
  28982         return JS_ThrowStackOverflow(ctx);
  28983 
  28984     if (module->status == JS_MODULE_STATUS_EVALUATED) {
  28985         assert(module->eval_has_exception);
  28986         return JS_UNDEFINED;
  28987     }
  28988 
  28989     assert(module->status == JS_MODULE_STATUS_EVALUATING_ASYNC);
  28990     assert(!module->eval_has_exception);
  28991     assert(module->async_evaluation);
  28992 
  28993     module->eval_has_exception = TRUE;
  28994     module->eval_exception = JS_DupValue(ctx, error);
  28995     module->status = JS_MODULE_STATUS_EVALUATED;
  28996 
  28997     for(i = 0; i < module->async_parent_modules_count; i++) {
  28998         JSModuleDef *m = module->async_parent_modules[i];
  28999         JSValue m_obj = JS_NewModuleValue(ctx, m);
  29000         js_async_module_execution_rejected(ctx, JS_UNDEFINED, 1, &error, 0,
  29001                                            &m_obj);
  29002         JS_FreeValue(ctx, m_obj);
  29003     }
  29004 
  29005     if (!JS_IsUndefined(module->promise)) {
  29006         JSValue ret_val;
  29007         assert(module->cycle_root == module);
  29008         ret_val = JS_Call(ctx, module->resolving_funcs[1], JS_UNDEFINED,
  29009                           1, &error);
  29010         JS_FreeValue(ctx, ret_val);
  29011     }
  29012     return JS_UNDEFINED;
  29013 }
  29014 
  29015 static JSValue js_async_module_execution_fulfilled(JSContext *ctx, JSValueConst this_val,
  29016                                                    int argc, JSValueConst *argv, int magic, JSValue *func_data)
  29017 {
  29018     JSModuleDef *module = JS_VALUE_GET_PTR(func_data[0]);
  29019     ExecModuleList exec_list_s, *exec_list = &exec_list_s;
  29020     int i;
  29021 
  29022     if (module->status == JS_MODULE_STATUS_EVALUATED) {
  29023         assert(module->eval_has_exception);
  29024         return JS_UNDEFINED;
  29025     }
  29026     assert(module->status == JS_MODULE_STATUS_EVALUATING_ASYNC);
  29027     assert(!module->eval_has_exception);
  29028     assert(module->async_evaluation);
  29029     module->async_evaluation = FALSE;
  29030     js_set_module_evaluated(ctx, module);
  29031 
  29032     exec_list->tab = NULL;
  29033     exec_list->count = 0;
  29034     exec_list->size = 0;
  29035 
  29036     if (gather_available_ancestors(ctx, module, exec_list) < 0) {
  29037         js_free(ctx, exec_list->tab);
  29038         return JS_EXCEPTION;
  29039     }
  29040 
  29041     /* sort by increasing async_evaluation timestamp */
  29042     rqsort(exec_list->tab, exec_list->count, sizeof(exec_list->tab[0]),
  29043            exec_module_list_cmp, NULL);
  29044 
  29045     for(i = 0; i < exec_list->count; i++) {
  29046         JSModuleDef *m = exec_list->tab[i];
  29047         if (m->status == JS_MODULE_STATUS_EVALUATED) {
  29048             assert(m->eval_has_exception);
  29049         } else if (m->has_tla) {
  29050             js_execute_async_module(ctx, m);
  29051         } else {
  29052             JSValue error;
  29053             if (js_execute_sync_module(ctx, m, &error) < 0) {
  29054                 JSValue m_obj = JS_NewModuleValue(ctx, m);
  29055                 js_async_module_execution_rejected(ctx, JS_UNDEFINED,
  29056                                                    1, (JSValueConst *)&error, 0,
  29057                                                    &m_obj);
  29058                 JS_FreeValue(ctx, m_obj);
  29059                 JS_FreeValue(ctx, error);
  29060             } else {
  29061                 js_set_module_evaluated(ctx, m);
  29062             }
  29063         }
  29064     }
  29065     js_free(ctx, exec_list->tab);
  29066     return JS_UNDEFINED;
  29067 }
  29068 
  29069 static int js_execute_async_module(JSContext *ctx, JSModuleDef *m)
  29070 {
  29071     JSValue promise, m_obj;
  29072     JSValue resolve_funcs[2], ret_val;
  29073     promise = js_async_function_call(ctx, m->func_obj, JS_UNDEFINED, 0, NULL, 0);
  29074     if (JS_IsException(promise))
  29075         return -1;
  29076     m_obj = JS_NewModuleValue(ctx, m);
  29077     resolve_funcs[0] = JS_NewCFunctionData(ctx, js_async_module_execution_fulfilled, 0, 0, 1, (JSValueConst *)&m_obj);
  29078     resolve_funcs[1] = JS_NewCFunctionData(ctx, js_async_module_execution_rejected, 0, 0, 1, (JSValueConst *)&m_obj);
  29079     ret_val = js_promise_then(ctx, promise, 2, (JSValueConst *)resolve_funcs);
  29080     JS_FreeValue(ctx, ret_val);
  29081     JS_FreeValue(ctx, m_obj);
  29082     JS_FreeValue(ctx, resolve_funcs[0]);
  29083     JS_FreeValue(ctx, resolve_funcs[1]);
  29084     JS_FreeValue(ctx, promise);
  29085     return 0;
  29086 }
  29087 
  29088 /* return < 0 in case of exception. *pvalue contains the exception. */
  29089 static int js_execute_sync_module(JSContext *ctx, JSModuleDef *m,
  29090                                   JSValue *pvalue)
  29091 {
  29092     if (m->init_func) {
  29093         /* C module init : no asynchronous execution */
  29094         if (m->init_func(ctx, m) < 0)
  29095             goto fail;
  29096     } else {
  29097         JSValue promise;
  29098         JSPromiseStateEnum state;
  29099 
  29100         promise = js_async_function_call(ctx, m->func_obj, JS_UNDEFINED, 0, NULL, 0);
  29101         if (JS_IsException(promise))
  29102             goto fail;
  29103         state = JS_PromiseState(ctx, promise);
  29104         if (state == JS_PROMISE_FULFILLED) {
  29105             JS_FreeValue(ctx, promise);
  29106         } else if (state == JS_PROMISE_REJECTED) {
  29107             *pvalue = JS_PromiseResult(ctx, promise);
  29108             JS_FreeValue(ctx, promise);
  29109             return -1;
  29110         } else {
  29111             JS_FreeValue(ctx, promise);
  29112             JS_ThrowTypeError(ctx, "promise is pending");
  29113         fail:
  29114             *pvalue = JS_GetException(ctx);
  29115             return -1;
  29116         }
  29117     }
  29118     *pvalue = JS_UNDEFINED;
  29119     return 0;
  29120 }
  29121 
  29122 /* spec: InnerModuleEvaluation. Return (index, JS_UNDEFINED) or (-1,
  29123    exception) */
  29124 static int js_inner_module_evaluation(JSContext *ctx, JSModuleDef *m,
  29125                                       int index, JSModuleDef **pstack_top,
  29126                                       JSValue *pvalue)
  29127 {
  29128     JSModuleDef *m1;
  29129     int i;
  29130 
  29131     if (js_check_stack_overflow(ctx->rt, 0)) {
  29132         JS_ThrowStackOverflow(ctx);
  29133         *pvalue = JS_GetException(ctx);
  29134         return -1;
  29135     }
  29136 
  29137 #ifdef DUMP_MODULE_RESOLVE
  29138     {
  29139         char buf1[ATOM_GET_STR_BUF_SIZE];
  29140         printf("js_inner_module_evaluation '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
  29141     }
  29142 #endif
  29143 
  29144     if (m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
  29145         m->status == JS_MODULE_STATUS_EVALUATED) {
  29146         if (m->eval_has_exception) {
  29147             *pvalue = JS_DupValue(ctx, m->eval_exception);
  29148             return -1;
  29149         } else {
  29150             *pvalue = JS_UNDEFINED;
  29151             return index;
  29152         }
  29153     }
  29154     if (m->status == JS_MODULE_STATUS_EVALUATING) {
  29155         *pvalue = JS_UNDEFINED;
  29156         return index;
  29157     }
  29158     assert(m->status == JS_MODULE_STATUS_LINKED);
  29159 
  29160     m->status = JS_MODULE_STATUS_EVALUATING;
  29161     m->dfs_index = index;
  29162     m->dfs_ancestor_index = index;
  29163     m->pending_async_dependencies = 0;
  29164     index++;
  29165     /* push 'm' on stack */
  29166     m->stack_prev = *pstack_top;
  29167     *pstack_top = m;
  29168 
  29169     for(i = 0; i < m->req_module_entries_count; i++) {
  29170         JSReqModuleEntry *rme = &m->req_module_entries[i];
  29171         m1 = rme->module;
  29172         index = js_inner_module_evaluation(ctx, m1, index, pstack_top, pvalue);
  29173         if (index < 0)
  29174             return -1;
  29175         assert(m1->status == JS_MODULE_STATUS_EVALUATING ||
  29176                m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
  29177                m1->status == JS_MODULE_STATUS_EVALUATED);
  29178         if (m1->status == JS_MODULE_STATUS_EVALUATING) {
  29179             m->dfs_ancestor_index = min_int(m->dfs_ancestor_index,
  29180                                             m1->dfs_ancestor_index);
  29181         } else {
  29182             m1 = m1->cycle_root;
  29183             assert(m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
  29184                    m1->status == JS_MODULE_STATUS_EVALUATED);
  29185             if (m1->eval_has_exception) {
  29186                 *pvalue = JS_DupValue(ctx, m1->eval_exception);
  29187                 return -1;
  29188             }
  29189         }
  29190         if (m1->async_evaluation) {
  29191             m->pending_async_dependencies++;
  29192             if (js_resize_array(ctx, (void **)&m1->async_parent_modules, sizeof(m1->async_parent_modules[0]), &m1->async_parent_modules_size, m1->async_parent_modules_count + 1)) {
  29193                 *pvalue = JS_GetException(ctx);
  29194                 return -1;
  29195             }
  29196             m1->async_parent_modules[m1->async_parent_modules_count++] = m;
  29197         }
  29198     }
  29199 
  29200     if (m->pending_async_dependencies > 0) {
  29201         assert(!m->async_evaluation);
  29202         m->async_evaluation = TRUE;
  29203         m->async_evaluation_timestamp =
  29204             ctx->rt->module_async_evaluation_next_timestamp++;
  29205     } else if (m->has_tla) {
  29206         assert(!m->async_evaluation);
  29207         m->async_evaluation = TRUE;
  29208         m->async_evaluation_timestamp =
  29209             ctx->rt->module_async_evaluation_next_timestamp++;
  29210         js_execute_async_module(ctx, m);
  29211     } else {
  29212         if (js_execute_sync_module(ctx, m, pvalue) < 0)
  29213             return -1;
  29214     }
  29215 
  29216     assert(m->dfs_ancestor_index <= m->dfs_index);
  29217     if (m->dfs_index == m->dfs_ancestor_index) {
  29218         for(;;) {
  29219             /* pop m1 from stack */
  29220             m1 = *pstack_top;
  29221             *pstack_top = m1->stack_prev;
  29222             if (!m1->async_evaluation) {
  29223                 m1->status = JS_MODULE_STATUS_EVALUATED;
  29224             } else {
  29225                 m1->status = JS_MODULE_STATUS_EVALUATING_ASYNC;
  29226             }
  29227             /* spec bug: cycle_root must be assigned before the test */
  29228             m1->cycle_root = m;
  29229             if (m1 == m)
  29230                 break;
  29231         }
  29232     }
  29233     *pvalue = JS_UNDEFINED;
  29234     return index;
  29235 }
  29236 
  29237 /* Run the <eval> function of the module and of all its requested
  29238    modules. Return a promise or an exception. */
  29239 static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m)
  29240 {
  29241     JSModuleDef *m1, *stack_top;
  29242     JSValue ret_val, result;
  29243 
  29244     assert(m->status == JS_MODULE_STATUS_LINKED ||
  29245            m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
  29246            m->status == JS_MODULE_STATUS_EVALUATED);
  29247     if (m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
  29248         m->status == JS_MODULE_STATUS_EVALUATED) {
  29249         m = m->cycle_root;
  29250     }
  29251     /* a promise may be created only on the cycle_root of a cycle */
  29252     if (!JS_IsUndefined(m->promise))
  29253         return JS_DupValue(ctx, m->promise);
  29254     m->promise = JS_NewPromiseCapability(ctx, m->resolving_funcs);
  29255     if (JS_IsException(m->promise))
  29256         return JS_EXCEPTION;
  29257 
  29258     stack_top = NULL;
  29259     if (js_inner_module_evaluation(ctx, m, 0, &stack_top, &result) < 0) {
  29260         while (stack_top != NULL) {
  29261             m1 = stack_top;
  29262             assert(m1->status == JS_MODULE_STATUS_EVALUATING);
  29263             m1->status = JS_MODULE_STATUS_EVALUATED;
  29264             m1->eval_has_exception = TRUE;
  29265             m1->eval_exception = JS_DupValue(ctx, result);
  29266             m1->cycle_root = m; /* spec bug: should be present */
  29267             stack_top = m1->stack_prev;
  29268         }
  29269         JS_FreeValue(ctx, result);
  29270         assert(m->status == JS_MODULE_STATUS_EVALUATED);
  29271         assert(m->eval_has_exception);
  29272         ret_val = JS_Call(ctx, m->resolving_funcs[1], JS_UNDEFINED,
  29273                           1, (JSValueConst *)&m->eval_exception);
  29274         JS_FreeValue(ctx, ret_val);
  29275     } else {
  29276         assert(m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
  29277                m->status == JS_MODULE_STATUS_EVALUATED);
  29278         assert(!m->eval_has_exception);
  29279         if (!m->async_evaluation) {
  29280             JSValue value;
  29281             assert(m->status == JS_MODULE_STATUS_EVALUATED);
  29282             value = JS_UNDEFINED;
  29283             ret_val = JS_Call(ctx, m->resolving_funcs[0], JS_UNDEFINED,
  29284                               1, (JSValueConst *)&value);
  29285             JS_FreeValue(ctx, ret_val);
  29286         }
  29287         assert(stack_top == NULL);
  29288     }
  29289     return JS_DupValue(ctx, m->promise);
  29290 }
  29291 
  29292 static __exception JSAtom js_parse_from_clause(JSParseState *s)
  29293 {
  29294     JSAtom module_name;
  29295     if (!token_is_pseudo_keyword(s, JS_ATOM_from)) {
  29296         js_parse_error(s, "from clause expected");
  29297         return JS_ATOM_NULL;
  29298     }
  29299     if (next_token(s))
  29300         return JS_ATOM_NULL;
  29301     if (s->token.val != TOK_STRING) {
  29302         js_parse_error(s, "string expected");
  29303         return JS_ATOM_NULL;
  29304     }
  29305     module_name = JS_ValueToAtom(s->ctx, s->token.u.str.str);
  29306     if (module_name == JS_ATOM_NULL)
  29307         return JS_ATOM_NULL;
  29308     if (next_token(s)) {
  29309         JS_FreeAtom(s->ctx, module_name);
  29310         return JS_ATOM_NULL;
  29311     }
  29312     return module_name;
  29313 }
  29314 
  29315 static __exception int js_parse_export(JSParseState *s)
  29316 {
  29317     JSContext *ctx = s->ctx;
  29318     JSModuleDef *m = s->cur_func->module;
  29319     JSAtom local_name, export_name;
  29320     int first_export, idx, i, tok;
  29321     JSAtom module_name;
  29322     JSExportEntry *me;
  29323 
  29324     if (next_token(s))
  29325         return -1;
  29326 
  29327     tok = s->token.val;
  29328     if (tok == TOK_CLASS) {
  29329         return js_parse_class(s, FALSE, JS_PARSE_EXPORT_NAMED);
  29330     } else if (tok == TOK_FUNCTION ||
  29331                (token_is_pseudo_keyword(s, JS_ATOM_async) &&
  29332                 peek_token(s, TRUE) == TOK_FUNCTION)) {
  29333         return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT,
  29334                                        JS_FUNC_NORMAL, JS_ATOM_NULL,
  29335                                        s->token.ptr, s->token.line_num,
  29336                                        JS_PARSE_EXPORT_NAMED, NULL);
  29337     }
  29338 
  29339     if (next_token(s))
  29340         return -1;
  29341 
  29342     switch(tok) {
  29343     case '{':
  29344         first_export = m->export_entries_count;
  29345         while (s->token.val != '}') {
  29346             if (!token_is_ident(s->token.val)) {
  29347                 js_parse_error(s, "identifier expected");
  29348                 return -1;
  29349             }
  29350             local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
  29351             export_name = JS_ATOM_NULL;
  29352             if (next_token(s))
  29353                 goto fail;
  29354             if (token_is_pseudo_keyword(s, JS_ATOM_as)) {
  29355                 if (next_token(s))
  29356                     goto fail;
  29357                 if (!token_is_ident(s->token.val)) {
  29358                     js_parse_error(s, "identifier expected");
  29359                     goto fail;
  29360                 }
  29361                 export_name = JS_DupAtom(ctx, s->token.u.ident.atom);
  29362                 if (next_token(s)) {
  29363                 fail:
  29364                     JS_FreeAtom(ctx, local_name);
  29365                 fail1:
  29366                     JS_FreeAtom(ctx, export_name);
  29367                     return -1;
  29368                 }
  29369             } else {
  29370                 export_name = JS_DupAtom(ctx, local_name);
  29371             }
  29372             me = add_export_entry(s, m, local_name, export_name,
  29373                                   JS_EXPORT_TYPE_LOCAL);
  29374             JS_FreeAtom(ctx, local_name);
  29375             JS_FreeAtom(ctx, export_name);
  29376             if (!me)
  29377                 return -1;
  29378             if (s->token.val != ',')
  29379                 break;
  29380             if (next_token(s))
  29381                 return -1;
  29382         }
  29383         if (js_parse_expect(s, '}'))
  29384             return -1;
  29385         if (token_is_pseudo_keyword(s, JS_ATOM_from)) {
  29386             module_name = js_parse_from_clause(s);
  29387             if (module_name == JS_ATOM_NULL)
  29388                 return -1;
  29389             idx = add_req_module_entry(ctx, m, module_name);
  29390             JS_FreeAtom(ctx, module_name);
  29391             if (idx < 0)
  29392                 return -1;
  29393             for(i = first_export; i < m->export_entries_count; i++) {
  29394                 me = &m->export_entries[i];
  29395                 me->export_type = JS_EXPORT_TYPE_INDIRECT;
  29396                 me->u.req_module_idx = idx;
  29397             }
  29398         }
  29399         break;
  29400     case '*':
  29401         if (token_is_pseudo_keyword(s, JS_ATOM_as)) {
  29402             /* export ns from */
  29403             if (next_token(s))
  29404                 return -1;
  29405             if (!token_is_ident(s->token.val)) {
  29406                 js_parse_error(s, "identifier expected");
  29407                 return -1;
  29408             }
  29409             export_name = JS_DupAtom(ctx, s->token.u.ident.atom);
  29410             if (next_token(s))
  29411                 goto fail1;
  29412             module_name = js_parse_from_clause(s);
  29413             if (module_name == JS_ATOM_NULL)
  29414                 goto fail1;
  29415             idx = add_req_module_entry(ctx, m, module_name);
  29416             JS_FreeAtom(ctx, module_name);
  29417             if (idx < 0)
  29418                 goto fail1;
  29419             me = add_export_entry(s, m, JS_ATOM__star_, export_name,
  29420                                   JS_EXPORT_TYPE_INDIRECT);
  29421             JS_FreeAtom(ctx, export_name);
  29422             if (!me)
  29423                 return -1;
  29424             me->u.req_module_idx = idx;
  29425         } else {
  29426             module_name = js_parse_from_clause(s);
  29427             if (module_name == JS_ATOM_NULL)
  29428                 return -1;
  29429             idx = add_req_module_entry(ctx, m, module_name);
  29430             JS_FreeAtom(ctx, module_name);
  29431             if (idx < 0)
  29432                 return -1;
  29433             if (add_star_export_entry(ctx, m, idx) < 0)
  29434                 return -1;
  29435         }
  29436         break;
  29437     case TOK_DEFAULT:
  29438         if (s->token.val == TOK_CLASS) {
  29439             return js_parse_class(s, FALSE, JS_PARSE_EXPORT_DEFAULT);
  29440         } else if (s->token.val == TOK_FUNCTION ||
  29441                    (token_is_pseudo_keyword(s, JS_ATOM_async) &&
  29442                     peek_token(s, TRUE) == TOK_FUNCTION)) {
  29443             return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT,
  29444                                            JS_FUNC_NORMAL, JS_ATOM_NULL,
  29445                                            s->token.ptr, s->token.line_num,
  29446                                            JS_PARSE_EXPORT_DEFAULT, NULL);
  29447         } else {
  29448             if (js_parse_assign_expr(s))
  29449                 return -1;
  29450         }
  29451         /* set the name of anonymous functions */
  29452         set_object_name(s, JS_ATOM_default);
  29453 
  29454         /* store the value in the _default_ global variable and export
  29455            it */
  29456         local_name = JS_ATOM__default_;
  29457         if (define_var(s, s->cur_func, local_name, JS_VAR_DEF_LET) < 0)
  29458             return -1;
  29459         emit_op(s, OP_scope_put_var_init);
  29460         emit_atom(s, local_name);
  29461         emit_u16(s, 0);
  29462 
  29463         if (!add_export_entry(s, m, local_name, JS_ATOM_default,
  29464                               JS_EXPORT_TYPE_LOCAL))
  29465             return -1;
  29466         break;
  29467     case TOK_VAR:
  29468     case TOK_LET:
  29469     case TOK_CONST:
  29470         return js_parse_var(s, TRUE, tok, TRUE);
  29471     default:
  29472         return js_parse_error(s, "invalid export syntax");
  29473     }
  29474     return js_parse_expect_semi(s);
  29475 }
  29476 
  29477 static int add_closure_var(JSContext *ctx, JSFunctionDef *s,
  29478                            BOOL is_local, BOOL is_arg,
  29479                            int var_idx, JSAtom var_name,
  29480                            BOOL is_const, BOOL is_lexical,
  29481                            JSVarKindEnum var_kind);
  29482 
  29483 static int add_import(JSParseState *s, JSModuleDef *m,
  29484                       JSAtom local_name, JSAtom import_name)
  29485 {
  29486     JSContext *ctx = s->ctx;
  29487     int i, var_idx;
  29488     JSImportEntry *mi;
  29489     BOOL is_local;
  29490 
  29491     if (local_name == JS_ATOM_arguments || local_name == JS_ATOM_eval)
  29492         return js_parse_error(s, "invalid import binding");
  29493 
  29494     if (local_name != JS_ATOM_default) {
  29495         for (i = 0; i < s->cur_func->closure_var_count; i++) {
  29496             if (s->cur_func->closure_var[i].var_name == local_name)
  29497                 return js_parse_error(s, "duplicate import binding");
  29498         }
  29499     }
  29500 
  29501     is_local = (import_name == JS_ATOM__star_);
  29502     var_idx = add_closure_var(ctx, s->cur_func, is_local, FALSE,
  29503                               m->import_entries_count,
  29504                               local_name, TRUE, TRUE, FALSE);
  29505     if (var_idx < 0)
  29506         return -1;
  29507     if (js_resize_array(ctx, (void **)&m->import_entries,
  29508                         sizeof(JSImportEntry),
  29509                         &m->import_entries_size,
  29510                         m->import_entries_count + 1))
  29511         return -1;
  29512     mi = &m->import_entries[m->import_entries_count++];
  29513     mi->import_name = JS_DupAtom(ctx, import_name);
  29514     mi->var_idx = var_idx;
  29515     return 0;
  29516 }
  29517 
  29518 static __exception int js_parse_import(JSParseState *s)
  29519 {
  29520     JSContext *ctx = s->ctx;
  29521     JSModuleDef *m = s->cur_func->module;
  29522     JSAtom local_name, import_name, module_name;
  29523     int first_import, i, idx;
  29524 
  29525     if (next_token(s))
  29526         return -1;
  29527 
  29528     first_import = m->import_entries_count;
  29529     if (s->token.val == TOK_STRING) {
  29530         module_name = JS_ValueToAtom(ctx, s->token.u.str.str);
  29531         if (module_name == JS_ATOM_NULL)
  29532             return -1;
  29533         if (next_token(s)) {
  29534             JS_FreeAtom(ctx, module_name);
  29535             return -1;
  29536         }
  29537     } else {
  29538         if (s->token.val == TOK_IDENT) {
  29539             if (s->token.u.ident.is_reserved) {
  29540                 return js_parse_error_reserved_identifier(s);
  29541             }
  29542             /* "default" import */
  29543             local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
  29544             import_name = JS_ATOM_default;
  29545             if (next_token(s))
  29546                 goto fail;
  29547             if (add_import(s, m, local_name, import_name))
  29548                 goto fail;
  29549             JS_FreeAtom(ctx, local_name);
  29550 
  29551             if (s->token.val != ',')
  29552                 goto end_import_clause;
  29553             if (next_token(s))
  29554                 return -1;
  29555         }
  29556 
  29557         if (s->token.val == '*') {
  29558             /* name space import */
  29559             if (next_token(s))
  29560                 return -1;
  29561             if (!token_is_pseudo_keyword(s, JS_ATOM_as))
  29562                 return js_parse_error(s, "expecting 'as'");
  29563             if (next_token(s))
  29564                 return -1;
  29565             if (!token_is_ident(s->token.val)) {
  29566                 js_parse_error(s, "identifier expected");
  29567                 return -1;
  29568             }
  29569             local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
  29570             import_name = JS_ATOM__star_;
  29571             if (next_token(s))
  29572                 goto fail;
  29573             if (add_import(s, m, local_name, import_name))
  29574                 goto fail;
  29575             JS_FreeAtom(ctx, local_name);
  29576         } else if (s->token.val == '{') {
  29577             if (next_token(s))
  29578                 return -1;
  29579 
  29580             while (s->token.val != '}') {
  29581                 if (!token_is_ident(s->token.val)) {
  29582                     js_parse_error(s, "identifier expected");
  29583                     return -1;
  29584                 }
  29585                 import_name = JS_DupAtom(ctx, s->token.u.ident.atom);
  29586                 local_name = JS_ATOM_NULL;
  29587                 if (next_token(s))
  29588                     goto fail;
  29589                 if (token_is_pseudo_keyword(s, JS_ATOM_as)) {
  29590                     if (next_token(s))
  29591                         goto fail;
  29592                     if (!token_is_ident(s->token.val)) {
  29593                         js_parse_error(s, "identifier expected");
  29594                         goto fail;
  29595                     }
  29596                     local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
  29597                     if (next_token(s)) {
  29598                     fail:
  29599                         JS_FreeAtom(ctx, local_name);
  29600                         JS_FreeAtom(ctx, import_name);
  29601                         return -1;
  29602                     }
  29603                 } else {
  29604                     local_name = JS_DupAtom(ctx, import_name);
  29605                 }
  29606                 if (add_import(s, m, local_name, import_name))
  29607                     goto fail;
  29608                 JS_FreeAtom(ctx, local_name);
  29609                 JS_FreeAtom(ctx, import_name);
  29610                 if (s->token.val != ',')
  29611                     break;
  29612                 if (next_token(s))
  29613                     return -1;
  29614             }
  29615             if (js_parse_expect(s, '}'))
  29616                 return -1;
  29617         }
  29618     end_import_clause:
  29619         module_name = js_parse_from_clause(s);
  29620         if (module_name == JS_ATOM_NULL)
  29621             return -1;
  29622     }
  29623     idx = add_req_module_entry(ctx, m, module_name);
  29624     JS_FreeAtom(ctx, module_name);
  29625     if (idx < 0)
  29626         return -1;
  29627     for(i = first_import; i < m->import_entries_count; i++)
  29628         m->import_entries[i].req_module_idx = idx;
  29629 
  29630     return js_parse_expect_semi(s);
  29631 }
  29632 
  29633 static __exception int js_parse_source_element(JSParseState *s)
  29634 {
  29635     JSFunctionDef *fd = s->cur_func;
  29636     int tok;
  29637 
  29638     if (s->token.val == TOK_FUNCTION ||
  29639         (token_is_pseudo_keyword(s, JS_ATOM_async) &&
  29640          peek_token(s, TRUE) == TOK_FUNCTION)) {
  29641         if (js_parse_function_decl(s, JS_PARSE_FUNC_STATEMENT,
  29642                                    JS_FUNC_NORMAL, JS_ATOM_NULL,
  29643                                    s->token.ptr, s->token.line_num))
  29644             return -1;
  29645     } else if (s->token.val == TOK_EXPORT && fd->module) {
  29646         if (js_parse_export(s))
  29647             return -1;
  29648     } else if (s->token.val == TOK_IMPORT && fd->module &&
  29649                ((tok = peek_token(s, FALSE)) != '(' && tok != '.'))  {
  29650         /* the peek_token is needed to avoid confusion with ImportCall
  29651            (dynamic import) or import.meta */
  29652         if (js_parse_import(s))
  29653             return -1;
  29654     } else {
  29655         if (js_parse_statement_or_decl(s, DECL_MASK_ALL))
  29656             return -1;
  29657     }
  29658     return 0;
  29659 }
  29660 
  29661 static JSFunctionDef *js_new_function_def(JSContext *ctx,
  29662                                           JSFunctionDef *parent,
  29663                                           BOOL is_eval,
  29664                                           BOOL is_func_expr,
  29665                                           const char *filename, int line_num)
  29666 {
  29667     JSFunctionDef *fd;
  29668 
  29669     fd = js_mallocz(ctx, sizeof(*fd));
  29670     if (!fd)
  29671         return NULL;
  29672 
  29673     fd->ctx = ctx;
  29674     init_list_head(&fd->child_list);
  29675 
  29676     /* insert in parent list */
  29677     fd->parent = parent;
  29678     fd->parent_cpool_idx = -1;
  29679     if (parent) {
  29680         list_add_tail(&fd->link, &parent->child_list);
  29681         fd->js_mode = parent->js_mode;
  29682         fd->parent_scope_level = parent->scope_level;
  29683     }
  29684 
  29685     fd->is_eval = is_eval;
  29686     fd->is_func_expr = is_func_expr;
  29687     js_dbuf_init(ctx, &fd->byte_code);
  29688     fd->last_opcode_pos = -1;
  29689     fd->func_name = JS_ATOM_NULL;
  29690     fd->var_object_idx = -1;
  29691     fd->arg_var_object_idx = -1;
  29692     fd->arguments_var_idx = -1;
  29693     fd->arguments_arg_idx = -1;
  29694     fd->func_var_idx = -1;
  29695     fd->eval_ret_idx = -1;
  29696     fd->this_var_idx = -1;
  29697     fd->new_target_var_idx = -1;
  29698     fd->this_active_func_var_idx = -1;
  29699     fd->home_object_var_idx = -1;
  29700 
  29701     /* XXX: should distinguish arg, var and var object and body scopes */
  29702     fd->scopes = fd->def_scope_array;
  29703     fd->scope_size = countof(fd->def_scope_array);
  29704     fd->scope_count = 1;
  29705     fd->scopes[0].first = -1;
  29706     fd->scopes[0].parent = -1;
  29707     fd->scope_level = 0;  /* 0: var/arg scope */
  29708     fd->scope_first = -1;
  29709     fd->body_scope = -1;
  29710 
  29711     fd->filename = JS_NewAtom(ctx, filename);
  29712     fd->line_num = line_num;
  29713 
  29714     js_dbuf_init(ctx, &fd->pc2line);
  29715     //fd->pc2line_last_line_num = line_num;
  29716     //fd->pc2line_last_pc = 0;
  29717     fd->last_opcode_line_num = line_num;
  29718 
  29719     return fd;
  29720 }
  29721 
  29722 static void free_bytecode_atoms(JSRuntime *rt,
  29723                                 const uint8_t *bc_buf, int bc_len,
  29724                                 BOOL use_short_opcodes)
  29725 {
  29726     int pos, len, op;
  29727     JSAtom atom;
  29728     const JSOpCode *oi;
  29729 
  29730     pos = 0;
  29731     while (pos < bc_len) {
  29732         op = bc_buf[pos];
  29733         if (use_short_opcodes)
  29734             oi = &short_opcode_info(op);
  29735         else
  29736             oi = &opcode_info[op];
  29737 
  29738         len = oi->size;
  29739         switch(oi->fmt) {
  29740         case OP_FMT_atom:
  29741         case OP_FMT_atom_u8:
  29742         case OP_FMT_atom_u16:
  29743         case OP_FMT_atom_label_u8:
  29744         case OP_FMT_atom_label_u16:
  29745             atom = get_u32(bc_buf + pos + 1);
  29746             JS_FreeAtomRT(rt, atom);
  29747             break;
  29748         default:
  29749             break;
  29750         }
  29751         pos += len;
  29752     }
  29753 }
  29754 
  29755 static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd)
  29756 {
  29757     int i;
  29758     struct list_head *el, *el1;
  29759 
  29760     /* free the child functions */
  29761     list_for_each_safe(el, el1, &fd->child_list) {
  29762         JSFunctionDef *fd1;
  29763         fd1 = list_entry(el, JSFunctionDef, link);
  29764         js_free_function_def(ctx, fd1);
  29765     }
  29766 
  29767     free_bytecode_atoms(ctx->rt, fd->byte_code.buf, fd->byte_code.size,
  29768                         fd->use_short_opcodes);
  29769     dbuf_free(&fd->byte_code);
  29770     js_free(ctx, fd->jump_slots);
  29771     js_free(ctx, fd->label_slots);
  29772     js_free(ctx, fd->line_number_slots);
  29773 
  29774     for(i = 0; i < fd->cpool_count; i++) {
  29775         JS_FreeValue(ctx, fd->cpool[i]);
  29776     }
  29777     js_free(ctx, fd->cpool);
  29778 
  29779     JS_FreeAtom(ctx, fd->func_name);
  29780 
  29781     for(i = 0; i < fd->var_count; i++) {
  29782         JS_FreeAtom(ctx, fd->vars[i].var_name);
  29783     }
  29784     js_free(ctx, fd->vars);
  29785     for(i = 0; i < fd->arg_count; i++) {
  29786         JS_FreeAtom(ctx, fd->args[i].var_name);
  29787     }
  29788     js_free(ctx, fd->args);
  29789 
  29790     for(i = 0; i < fd->global_var_count; i++) {
  29791         JS_FreeAtom(ctx, fd->global_vars[i].var_name);
  29792     }
  29793     js_free(ctx, fd->global_vars);
  29794 
  29795     for(i = 0; i < fd->closure_var_count; i++) {
  29796         JSClosureVar *cv = &fd->closure_var[i];
  29797         JS_FreeAtom(ctx, cv->var_name);
  29798     }
  29799     js_free(ctx, fd->closure_var);
  29800 
  29801     if (fd->scopes != fd->def_scope_array)
  29802         js_free(ctx, fd->scopes);
  29803 
  29804     JS_FreeAtom(ctx, fd->filename);
  29805     dbuf_free(&fd->pc2line);
  29806 
  29807     js_free(ctx, fd->source);
  29808 
  29809     if (fd->parent) {
  29810         /* remove in parent list */
  29811         list_del(&fd->link);
  29812     }
  29813     js_free(ctx, fd);
  29814 }
  29815 
  29816 #ifdef DUMP_BYTECODE
  29817 static const char *skip_lines(const char *p, int n) {
  29818     while (n-- > 0 && *p) {
  29819         while (*p && *p++ != '\n')
  29820             continue;
  29821     }
  29822     return p;
  29823 }
  29824 
  29825 static void print_lines(const char *source, int line, int line1) {
  29826     const char *s = source;
  29827     const char *p = skip_lines(s, line);
  29828     if (*p) {
  29829         while (line++ < line1) {
  29830             p = skip_lines(s = p, 1);
  29831             printf(";; %.*s", (int)(p - s), s);
  29832             if (!*p) {
  29833                 if (p[-1] != '\n')
  29834                     printf("\n");
  29835                 break;
  29836             }
  29837         }
  29838     }
  29839 }
  29840 
  29841 static void dump_byte_code(JSContext *ctx, int pass,
  29842                            const uint8_t *tab, int len,
  29843                            const JSVarDef *args, int arg_count,
  29844                            const JSVarDef *vars, int var_count,
  29845                            const JSClosureVar *closure_var, int closure_var_count,
  29846                            const JSValue *cpool, uint32_t cpool_count,
  29847                            const char *source, int line_num,
  29848                            const LabelSlot *label_slots, JSFunctionBytecode *b)
  29849 {
  29850     const JSOpCode *oi;
  29851     int pos, pos_next, op, size, idx, addr, line, line1, in_source;
  29852     uint8_t *bits = js_mallocz(ctx, len * sizeof(*bits));
  29853     BOOL use_short_opcodes = (b != NULL);
  29854 
  29855     /* scan for jump targets */
  29856     for (pos = 0; pos < len; pos = pos_next) {
  29857         op = tab[pos];
  29858         if (use_short_opcodes)
  29859             oi = &short_opcode_info(op);
  29860         else
  29861             oi = &opcode_info[op];
  29862         pos_next = pos + oi->size;
  29863         if (op < OP_COUNT) {
  29864             switch (oi->fmt) {
  29865 #if SHORT_OPCODES
  29866             case OP_FMT_label8:
  29867                 pos++;
  29868                 addr = (int8_t)tab[pos];
  29869                 goto has_addr;
  29870             case OP_FMT_label16:
  29871                 pos++;
  29872                 addr = (int16_t)get_u16(tab + pos);
  29873                 goto has_addr;
  29874 #endif
  29875             case OP_FMT_atom_label_u8:
  29876             case OP_FMT_atom_label_u16:
  29877                 pos += 4;
  29878                 /* fall thru */
  29879             case OP_FMT_label:
  29880             case OP_FMT_label_u16:
  29881                 pos++;
  29882                 addr = get_u32(tab + pos);
  29883                 goto has_addr;
  29884             has_addr:
  29885                 if (pass == 1)
  29886                     addr = label_slots[addr].pos;
  29887                 if (pass == 2)
  29888                     addr = label_slots[addr].pos2;
  29889                 if (pass == 3)
  29890                     addr += pos;
  29891                 if (addr >= 0 && addr < len)
  29892                     bits[addr] |= 1;
  29893                 break;
  29894             }
  29895         }
  29896     }
  29897     in_source = 0;
  29898     if (source) {
  29899         /* Always print first line: needed if single line */
  29900         print_lines(source, 0, 1);
  29901         in_source = 1;
  29902     }
  29903     line1 = line = 1;
  29904     pos = 0;
  29905     while (pos < len) {
  29906         op = tab[pos];
  29907         if (source) {
  29908             if (b) {
  29909                 line1 = find_line_num(ctx, b, pos) - line_num + 1;
  29910             } else if (op == OP_line_num) {
  29911                 line1 = get_u32(tab + pos + 1) - line_num + 1;
  29912             }
  29913             if (line1 > line) {
  29914                 if (!in_source)
  29915                     printf("\n");
  29916                 in_source = 1;
  29917                 print_lines(source, line, line1);
  29918                 line = line1;
  29919                 //bits[pos] |= 2;
  29920             }
  29921         }
  29922         if (in_source)
  29923             printf("\n");
  29924         in_source = 0;
  29925         if (op >= OP_COUNT) {
  29926             printf("invalid opcode (0x%02x)\n", op);
  29927             pos++;
  29928             continue;
  29929         }
  29930         if (use_short_opcodes)
  29931             oi = &short_opcode_info(op);
  29932         else
  29933             oi = &opcode_info[op];
  29934         size = oi->size;
  29935         if (pos + size > len) {
  29936             printf("truncated opcode (0x%02x)\n", op);
  29937             break;
  29938         }
  29939 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 16)
  29940         {
  29941             int i, x, x0;
  29942             x = x0 = printf("%5d ", pos);
  29943             for (i = 0; i < size; i++) {
  29944                 if (i == 6) {
  29945                     printf("\n%*s", x = x0, "");
  29946                 }
  29947                 x += printf(" %02X", tab[pos + i]);
  29948             }
  29949             printf("%*s", x0 + 20 - x, "");
  29950         }
  29951 #endif
  29952         if (bits[pos]) {
  29953             printf("%5d:  ", pos);
  29954         } else {
  29955             printf("        ");
  29956         }
  29957         printf("%s", oi->name);
  29958         pos++;
  29959         switch(oi->fmt) {
  29960         case OP_FMT_none_int:
  29961             printf(" %d", op - OP_push_0);
  29962             break;
  29963         case OP_FMT_npopx:
  29964             printf(" %d", op - OP_call0);
  29965             break;
  29966         case OP_FMT_u8:
  29967             printf(" %u", get_u8(tab + pos));
  29968             break;
  29969         case OP_FMT_i8:
  29970             printf(" %d", get_i8(tab + pos));
  29971             break;
  29972         case OP_FMT_u16:
  29973         case OP_FMT_npop:
  29974             printf(" %u", get_u16(tab + pos));
  29975             break;
  29976         case OP_FMT_npop_u16:
  29977             printf(" %u,%u", get_u16(tab + pos), get_u16(tab + pos + 2));
  29978             break;
  29979         case OP_FMT_i16:
  29980             printf(" %d", get_i16(tab + pos));
  29981             break;
  29982         case OP_FMT_i32:
  29983             printf(" %d", get_i32(tab + pos));
  29984             break;
  29985         case OP_FMT_u32:
  29986             printf(" %u", get_u32(tab + pos));
  29987             break;
  29988 #if SHORT_OPCODES
  29989         case OP_FMT_label8:
  29990             addr = get_i8(tab + pos);
  29991             goto has_addr1;
  29992         case OP_FMT_label16:
  29993             addr = get_i16(tab + pos);
  29994             goto has_addr1;
  29995 #endif
  29996         case OP_FMT_label:
  29997             addr = get_u32(tab + pos);
  29998             goto has_addr1;
  29999         has_addr1:
  30000             if (pass == 1)
  30001                 printf(" %u:%u", addr, label_slots[addr].pos);
  30002             if (pass == 2)
  30003                 printf(" %u:%u", addr, label_slots[addr].pos2);
  30004             if (pass == 3)
  30005                 printf(" %u", addr + pos);
  30006             break;
  30007         case OP_FMT_label_u16:
  30008             addr = get_u32(tab + pos);
  30009             if (pass == 1)
  30010                 printf(" %u:%u", addr, label_slots[addr].pos);
  30011             if (pass == 2)
  30012                 printf(" %u:%u", addr, label_slots[addr].pos2);
  30013             if (pass == 3)
  30014                 printf(" %u", addr + pos);
  30015             printf(",%u", get_u16(tab + pos + 4));
  30016             break;
  30017 #if SHORT_OPCODES
  30018         case OP_FMT_const8:
  30019             idx = get_u8(tab + pos);
  30020             goto has_pool_idx;
  30021 #endif
  30022         case OP_FMT_const:
  30023             idx = get_u32(tab + pos);
  30024             goto has_pool_idx;
  30025         has_pool_idx:
  30026             printf(" %u: ", idx);
  30027             if (idx < cpool_count) {
  30028                 JS_DumpValue(ctx, cpool[idx]);
  30029             }
  30030             break;
  30031         case OP_FMT_atom:
  30032             printf(" ");
  30033             print_atom(ctx, get_u32(tab + pos));
  30034             break;
  30035         case OP_FMT_atom_u8:
  30036             printf(" ");
  30037             print_atom(ctx, get_u32(tab + pos));
  30038             printf(",%d", get_u8(tab + pos + 4));
  30039             break;
  30040         case OP_FMT_atom_u16:
  30041             printf(" ");
  30042             print_atom(ctx, get_u32(tab + pos));
  30043             printf(",%d", get_u16(tab + pos + 4));
  30044             break;
  30045         case OP_FMT_atom_label_u8:
  30046         case OP_FMT_atom_label_u16:
  30047             printf(" ");
  30048             print_atom(ctx, get_u32(tab + pos));
  30049             addr = get_u32(tab + pos + 4);
  30050             if (pass == 1)
  30051                 printf(",%u:%u", addr, label_slots[addr].pos);
  30052             if (pass == 2)
  30053                 printf(",%u:%u", addr, label_slots[addr].pos2);
  30054             if (pass == 3)
  30055                 printf(",%u", addr + pos + 4);
  30056             if (oi->fmt == OP_FMT_atom_label_u8)
  30057                 printf(",%u", get_u8(tab + pos + 8));
  30058             else
  30059                 printf(",%u", get_u16(tab + pos + 8));
  30060             break;
  30061         case OP_FMT_none_loc:
  30062             idx = (op - OP_get_loc0) % 4;
  30063             goto has_loc;
  30064         case OP_FMT_loc8:
  30065             idx = get_u8(tab + pos);
  30066             goto has_loc;
  30067         case OP_FMT_loc:
  30068             idx = get_u16(tab + pos);
  30069         has_loc:
  30070             printf(" %d: ", idx);
  30071             if (idx < var_count) {
  30072                 print_atom(ctx, vars[idx].var_name);
  30073             }
  30074             break;
  30075         case OP_FMT_none_arg:
  30076             idx = (op - OP_get_arg0) % 4;
  30077             goto has_arg;
  30078         case OP_FMT_arg:
  30079             idx = get_u16(tab + pos);
  30080         has_arg:
  30081             printf(" %d: ", idx);
  30082             if (idx < arg_count) {
  30083                 print_atom(ctx, args[idx].var_name);
  30084             }
  30085             break;
  30086         case OP_FMT_none_var_ref:
  30087             idx = (op - OP_get_var_ref0) % 4;
  30088             goto has_var_ref;
  30089         case OP_FMT_var_ref:
  30090             idx = get_u16(tab + pos);
  30091         has_var_ref:
  30092             printf(" %d: ", idx);
  30093             if (idx < closure_var_count) {
  30094                 print_atom(ctx, closure_var[idx].var_name);
  30095             }
  30096             break;
  30097         default:
  30098             break;
  30099         }
  30100         printf("\n");
  30101         pos += oi->size - 1;
  30102     }
  30103     if (source) {
  30104         if (!in_source)
  30105             printf("\n");
  30106         print_lines(source, line, INT32_MAX);
  30107     }
  30108     js_free(ctx, bits);
  30109 }
  30110 
  30111 static __maybe_unused void dump_pc2line(JSContext *ctx, const uint8_t *buf, int len,
  30112                                                  int line_num)
  30113 {
  30114     const uint8_t *p_end, *p_next, *p;
  30115     int pc, v;
  30116     unsigned int op;
  30117 
  30118     if (len <= 0)
  30119         return;
  30120 
  30121     printf("%5s %5s\n", "PC", "LINE");
  30122 
  30123     p = buf;
  30124     p_end = buf + len;
  30125     pc = 0;
  30126     while (p < p_end) {
  30127         op = *p++;
  30128         if (op == 0) {
  30129             v = unicode_from_utf8(p, p_end - p, &p_next);
  30130             if (v < 0)
  30131                 goto fail;
  30132             pc += v;
  30133             p = p_next;
  30134             v = unicode_from_utf8(p, p_end - p, &p_next);
  30135             if (v < 0) {
  30136             fail:
  30137                 printf("invalid pc2line encode pos=%d\n", (int)(p - buf));
  30138                 return;
  30139             }
  30140             if (!(v & 1)) {
  30141                 v = v >> 1;
  30142             } else {
  30143                 v = -(v >> 1) - 1;
  30144             }
  30145             line_num += v;
  30146             p = p_next;
  30147         } else {
  30148             op -= PC2LINE_OP_FIRST;
  30149             pc += (op / PC2LINE_RANGE);
  30150             line_num += (op % PC2LINE_RANGE) + PC2LINE_BASE;
  30151         }
  30152         printf("%5d %5d\n", pc, line_num);
  30153     }
  30154 }
  30155 
  30156 static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionBytecode *b)
  30157 {
  30158     int i;
  30159     char atom_buf[ATOM_GET_STR_BUF_SIZE];
  30160     const char *str;
  30161 
  30162     if (b->has_debug && b->debug.filename != JS_ATOM_NULL) {
  30163         str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->debug.filename);
  30164         printf("%s:%d: ", str, b->debug.line_num);
  30165     }
  30166 
  30167     str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->func_name);
  30168     printf("function: %s%s\n", &"*"[b->func_kind != JS_FUNC_GENERATOR], str);
  30169     if (b->js_mode) {
  30170         printf("  mode:");
  30171         if (b->js_mode & JS_MODE_STRICT)
  30172             printf(" strict");
  30173 #ifdef CONFIG_BIGNUM
  30174         if (b->js_mode & JS_MODE_MATH)
  30175             printf(" math");
  30176 #endif
  30177         printf("\n");
  30178     }
  30179     if (b->arg_count && b->vardefs) {
  30180         printf("  args:");
  30181         for(i = 0; i < b->arg_count; i++) {
  30182             printf(" %s", JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf),
  30183                                         b->vardefs[i].var_name));
  30184         }
  30185         printf("\n");
  30186     }
  30187     if (b->var_count && b->vardefs) {
  30188         printf("  locals:\n");
  30189         for(i = 0; i < b->var_count; i++) {
  30190             JSVarDef *vd = &b->vardefs[b->arg_count + i];
  30191             printf("%5d: %s %s", i,
  30192                    vd->var_kind == JS_VAR_CATCH ? "catch" :
  30193                    (vd->var_kind == JS_VAR_FUNCTION_DECL ||
  30194                     vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) ? "function" :
  30195                    vd->is_const ? "const" :
  30196                    vd->is_lexical ? "let" : "var",
  30197                    JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), vd->var_name));
  30198             if (vd->scope_level)
  30199                 printf(" [level:%d next:%d]", vd->scope_level, vd->scope_next);
  30200             printf("\n");
  30201         }
  30202     }
  30203     if (b->closure_var_count) {
  30204         printf("  closure vars:\n");
  30205         for(i = 0; i < b->closure_var_count; i++) {
  30206             JSClosureVar *cv = &b->closure_var[i];
  30207             printf("%5d: %s %s:%s%d %s\n", i,
  30208                    JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), cv->var_name),
  30209                    cv->is_local ? "local" : "parent",
  30210                    cv->is_arg ? "arg" : "loc", cv->var_idx,
  30211                    cv->is_const ? "const" :
  30212                    cv->is_lexical ? "let" : "var");
  30213         }
  30214     }
  30215     printf("  stack_size: %d\n", b->stack_size);
  30216     printf("  opcodes:\n");
  30217     dump_byte_code(ctx, 3, b->byte_code_buf, b->byte_code_len,
  30218                    b->vardefs, b->arg_count,
  30219                    b->vardefs ? b->vardefs + b->arg_count : NULL, b->var_count,
  30220                    b->closure_var, b->closure_var_count,
  30221                    b->cpool, b->cpool_count,
  30222                    b->has_debug ? b->debug.source : NULL,
  30223                    b->has_debug ? b->debug.line_num : -1, NULL, b);
  30224 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 32)
  30225     if (b->has_debug)
  30226         dump_pc2line(ctx, b->debug.pc2line_buf, b->debug.pc2line_len, b->debug.line_num);
  30227 #endif
  30228     printf("\n");
  30229 }
  30230 #endif
  30231 
  30232 static int add_closure_var(JSContext *ctx, JSFunctionDef *s,
  30233                            BOOL is_local, BOOL is_arg,
  30234                            int var_idx, JSAtom var_name,
  30235                            BOOL is_const, BOOL is_lexical,
  30236                            JSVarKindEnum var_kind)
  30237 {
  30238     JSClosureVar *cv;
  30239 
  30240     /* the closure variable indexes are currently stored on 16 bits */
  30241     if (s->closure_var_count >= JS_MAX_LOCAL_VARS) {
  30242         JS_ThrowInternalError(ctx, "too many closure variables");
  30243         return -1;
  30244     }
  30245 
  30246     if (js_resize_array(ctx, (void **)&s->closure_var,
  30247                         sizeof(s->closure_var[0]),
  30248                         &s->closure_var_size, s->closure_var_count + 1))
  30249         return -1;
  30250     cv = &s->closure_var[s->closure_var_count++];
  30251     cv->is_local = is_local;
  30252     cv->is_arg = is_arg;
  30253     cv->is_const = is_const;
  30254     cv->is_lexical = is_lexical;
  30255     cv->var_kind = var_kind;
  30256     cv->var_idx = var_idx;
  30257     cv->var_name = JS_DupAtom(ctx, var_name);
  30258     return s->closure_var_count - 1;
  30259 }
  30260 
  30261 static int find_closure_var(JSContext *ctx, JSFunctionDef *s,
  30262                             JSAtom var_name)
  30263 {
  30264     int i;
  30265     for(i = 0; i < s->closure_var_count; i++) {
  30266         JSClosureVar *cv = &s->closure_var[i];
  30267         if (cv->var_name == var_name)
  30268             return i;
  30269     }
  30270     return -1;
  30271 }
  30272 
  30273 /* 'fd' must be a parent of 's'. Create in 's' a closure referencing a
  30274    local variable (is_local = TRUE) or a closure (is_local = FALSE) in
  30275    'fd' */
  30276 static int get_closure_var2(JSContext *ctx, JSFunctionDef *s,
  30277                             JSFunctionDef *fd, BOOL is_local,
  30278                             BOOL is_arg, int var_idx, JSAtom var_name,
  30279                             BOOL is_const, BOOL is_lexical,
  30280                             JSVarKindEnum var_kind)
  30281 {
  30282     int i;
  30283 
  30284     if (fd != s->parent) {
  30285         var_idx = get_closure_var2(ctx, s->parent, fd, is_local,
  30286                                    is_arg, var_idx, var_name,
  30287                                    is_const, is_lexical, var_kind);
  30288         if (var_idx < 0)
  30289             return -1;
  30290         is_local = FALSE;
  30291     }
  30292     for(i = 0; i < s->closure_var_count; i++) {
  30293         JSClosureVar *cv = &s->closure_var[i];
  30294         if (cv->var_idx == var_idx && cv->is_arg == is_arg &&
  30295             cv->is_local == is_local)
  30296             return i;
  30297     }
  30298     return add_closure_var(ctx, s, is_local, is_arg, var_idx, var_name,
  30299                            is_const, is_lexical, var_kind);
  30300 }
  30301 
  30302 static int get_closure_var(JSContext *ctx, JSFunctionDef *s,
  30303                            JSFunctionDef *fd, BOOL is_arg,
  30304                            int var_idx, JSAtom var_name,
  30305                            BOOL is_const, BOOL is_lexical,
  30306                            JSVarKindEnum var_kind)
  30307 {
  30308     return get_closure_var2(ctx, s, fd, TRUE, is_arg,
  30309                             var_idx, var_name, is_const, is_lexical,
  30310                             var_kind);
  30311 }
  30312 
  30313 static int get_with_scope_opcode(int op)
  30314 {
  30315     if (op == OP_scope_get_var_undef)
  30316         return OP_with_get_var;
  30317     else
  30318         return OP_with_get_var + (op - OP_scope_get_var);
  30319 }
  30320 
  30321 static BOOL can_opt_put_ref_value(const uint8_t *bc_buf, int pos)
  30322 {
  30323     int opcode = bc_buf[pos];
  30324     return (bc_buf[pos + 1] == OP_put_ref_value &&
  30325             (opcode == OP_insert3 ||
  30326              opcode == OP_perm4 ||
  30327              opcode == OP_nop ||
  30328              opcode == OP_rot3l));
  30329 }
  30330 
  30331 static BOOL can_opt_put_global_ref_value(const uint8_t *bc_buf, int pos)
  30332 {
  30333     int opcode = bc_buf[pos];
  30334     return (bc_buf[pos + 1] == OP_put_ref_value &&
  30335             (opcode == OP_insert3 ||
  30336              opcode == OP_perm4 ||
  30337              opcode == OP_nop ||
  30338              opcode == OP_rot3l));
  30339 }
  30340 
  30341 static int optimize_scope_make_ref(JSContext *ctx, JSFunctionDef *s,
  30342                                    DynBuf *bc, uint8_t *bc_buf,
  30343                                    LabelSlot *ls, int pos_next,
  30344                                    int get_op, int var_idx)
  30345 {
  30346     int label_pos, end_pos, pos;
  30347 
  30348     /* XXX: should optimize `loc(a) += expr` as `expr add_loc(a)`
  30349        but only if expr does not modify `a`.
  30350        should scan the code between pos_next and label_pos
  30351        for operations that can potentially change `a`:
  30352        OP_scope_make_ref(a), function calls, jumps and gosub.
  30353      */
  30354     /* replace the reference get/put with normal variable
  30355        accesses */
  30356     if (bc_buf[pos_next] == OP_get_ref_value) {
  30357         dbuf_putc(bc, get_op);
  30358         dbuf_put_u16(bc, var_idx);
  30359         pos_next++;
  30360     }
  30361     /* remove the OP_label to make room for replacement */
  30362     /* label should have a refcount of 0 anyway */
  30363     /* XXX: should avoid this patch by inserting nops in phase 1 */
  30364     label_pos = ls->pos;
  30365     pos = label_pos - 5;
  30366     assert(bc_buf[pos] == OP_label);
  30367     /* label points to an instruction pair:
  30368        - insert3 / put_ref_value
  30369        - perm4 / put_ref_value
  30370        - rot3l / put_ref_value
  30371        - nop / put_ref_value
  30372      */
  30373     end_pos = label_pos + 2;
  30374     if (bc_buf[label_pos] == OP_insert3)
  30375         bc_buf[pos++] = OP_dup;
  30376     bc_buf[pos] = get_op + 1;
  30377     put_u16(bc_buf + pos + 1, var_idx);
  30378     pos += 3;
  30379     /* pad with OP_nop */
  30380     while (pos < end_pos)
  30381         bc_buf[pos++] = OP_nop;
  30382     return pos_next;
  30383 }
  30384 
  30385 static int optimize_scope_make_global_ref(JSContext *ctx, JSFunctionDef *s,
  30386                                           DynBuf *bc, uint8_t *bc_buf,
  30387                                           LabelSlot *ls, int pos_next,
  30388                                           JSAtom var_name)
  30389 {
  30390     int label_pos, end_pos, pos, op;
  30391     BOOL is_strict;
  30392     is_strict = ((s->js_mode & JS_MODE_STRICT) != 0);
  30393 
  30394     /* replace the reference get/put with normal variable
  30395        accesses */
  30396     if (is_strict) {
  30397         /* need to check if the variable exists before evaluating the right
  30398            expression */
  30399         /* XXX: need an extra OP_true if destructuring an array */
  30400         dbuf_putc(bc, OP_check_var);
  30401         dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
  30402     } else {
  30403         /* XXX: need 2 extra OP_true if destructuring an array */
  30404     }
  30405     if (bc_buf[pos_next] == OP_get_ref_value) {
  30406         dbuf_putc(bc, OP_get_var);
  30407         dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
  30408         pos_next++;
  30409     }
  30410     /* remove the OP_label to make room for replacement */
  30411     /* label should have a refcount of 0 anyway */
  30412     /* XXX: should have emitted several OP_nop to avoid this kludge */
  30413     label_pos = ls->pos;
  30414     pos = label_pos - 5;
  30415     assert(bc_buf[pos] == OP_label);
  30416     end_pos = label_pos + 2;
  30417     op = bc_buf[label_pos];
  30418     if (is_strict) {
  30419         if (op != OP_nop) {
  30420             switch(op) {
  30421             case OP_insert3:
  30422                 op = OP_insert2;
  30423                 break;
  30424             case OP_perm4:
  30425                 op = OP_perm3;
  30426                 break;
  30427             case OP_rot3l:
  30428                 op = OP_swap;
  30429                 break;
  30430             default:
  30431                 abort();
  30432             }
  30433             bc_buf[pos++] = op;
  30434         }
  30435     } else {
  30436         if (op == OP_insert3)
  30437             bc_buf[pos++] = OP_dup;
  30438     }
  30439     if (is_strict) {
  30440         bc_buf[pos] = OP_put_var_strict;
  30441         /* XXX: need 1 extra OP_drop if destructuring an array */
  30442     } else {
  30443         bc_buf[pos] = OP_put_var;
  30444         /* XXX: need 2 extra OP_drop if destructuring an array */
  30445     }
  30446     put_u32(bc_buf + pos + 1, JS_DupAtom(ctx, var_name));
  30447     pos += 5;
  30448     /* pad with OP_nop */
  30449     while (pos < end_pos)
  30450         bc_buf[pos++] = OP_nop;
  30451     return pos_next;
  30452 }
  30453 
  30454 static int add_var_this(JSContext *ctx, JSFunctionDef *fd)
  30455 {
  30456     int idx;
  30457     idx = add_var(ctx, fd, JS_ATOM_this);
  30458     if (idx >= 0 && fd->is_derived_class_constructor) {
  30459         JSVarDef *vd = &fd->vars[idx];
  30460         /* XXX: should have is_this flag or var type */
  30461         vd->is_lexical = 1; /* used to trigger 'uninitialized' checks
  30462                                in a derived class constructor */
  30463     }
  30464     return idx;
  30465 }
  30466 
  30467 static int resolve_pseudo_var(JSContext *ctx, JSFunctionDef *s,
  30468                                JSAtom var_name)
  30469 {
  30470     int var_idx;
  30471 
  30472     if (!s->has_this_binding)
  30473         return -1;
  30474     switch(var_name) {
  30475     case JS_ATOM_home_object:
  30476         /* 'home_object' pseudo variable */
  30477         if (s->home_object_var_idx < 0)
  30478             s->home_object_var_idx = add_var(ctx, s, var_name);
  30479         var_idx = s->home_object_var_idx;
  30480         break;
  30481     case JS_ATOM_this_active_func:
  30482         /* 'this.active_func' pseudo variable */
  30483         if (s->this_active_func_var_idx < 0)
  30484             s->this_active_func_var_idx = add_var(ctx, s, var_name);
  30485         var_idx = s->this_active_func_var_idx;
  30486         break;
  30487     case JS_ATOM_new_target:
  30488         /* 'new.target' pseudo variable */
  30489         if (s->new_target_var_idx < 0)
  30490             s->new_target_var_idx = add_var(ctx, s, var_name);
  30491         var_idx = s->new_target_var_idx;
  30492         break;
  30493     case JS_ATOM_this:
  30494         /* 'this' pseudo variable */
  30495         if (s->this_var_idx < 0)
  30496             s->this_var_idx = add_var_this(ctx, s);
  30497         var_idx = s->this_var_idx;
  30498         break;
  30499     default:
  30500         var_idx = -1;
  30501         break;
  30502     }
  30503     return var_idx;
  30504 }
  30505 
  30506 /* test if 'var_name' is in the variable object on the stack. If is it
  30507    the case, handle it and jump to 'label_done' */
  30508 static void var_object_test(JSContext *ctx, JSFunctionDef *s,
  30509                             JSAtom var_name, int op, DynBuf *bc,
  30510                             int *plabel_done, BOOL is_with)
  30511 {
  30512     dbuf_putc(bc, get_with_scope_opcode(op));
  30513     dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
  30514     *plabel_done = new_label_fd(s, *plabel_done);
  30515     dbuf_put_u32(bc, *plabel_done);
  30516     dbuf_putc(bc, is_with);
  30517     update_label(s, *plabel_done, 1);
  30518     s->jump_size++;
  30519 }
  30520 
  30521 /* return the position of the next opcode */
  30522 static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s,
  30523                              JSAtom var_name, int scope_level, int op,
  30524                              DynBuf *bc, uint8_t *bc_buf,
  30525                              LabelSlot *ls, int pos_next)
  30526 {
  30527     int idx, var_idx, is_put;
  30528     int label_done;
  30529     JSFunctionDef *fd;
  30530     JSVarDef *vd;
  30531     BOOL is_pseudo_var, is_arg_scope;
  30532 
  30533     label_done = -1;
  30534 
  30535     /* XXX: could be simpler to use a specific function to
  30536        resolve the pseudo variables */
  30537     is_pseudo_var = (var_name == JS_ATOM_home_object ||
  30538                      var_name == JS_ATOM_this_active_func ||
  30539                      var_name == JS_ATOM_new_target ||
  30540                      var_name == JS_ATOM_this);
  30541 
  30542     /* resolve local scoped variables */
  30543     var_idx = -1;
  30544     for (idx = s->scopes[scope_level].first; idx >= 0;) {
  30545         vd = &s->vars[idx];
  30546         if (vd->var_name == var_name) {
  30547             if (op == OP_scope_put_var || op == OP_scope_make_ref) {
  30548                 if (vd->is_const) {
  30549                     dbuf_putc(bc, OP_throw_error);
  30550                     dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
  30551                     dbuf_putc(bc, JS_THROW_VAR_RO);
  30552                     goto done;
  30553                 }
  30554             }
  30555             var_idx = idx;
  30556             break;
  30557         } else
  30558         if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) {
  30559             dbuf_putc(bc, OP_get_loc);
  30560             dbuf_put_u16(bc, idx);
  30561             var_object_test(ctx, s, var_name, op, bc, &label_done, 1);
  30562         }
  30563         idx = vd->scope_next;
  30564     }
  30565     is_arg_scope = (idx == ARG_SCOPE_END);
  30566     if (var_idx < 0) {
  30567         /* argument scope: variables are not visible but pseudo
  30568            variables are visible */
  30569         if (!is_arg_scope) {
  30570             var_idx = find_var(ctx, s, var_name);
  30571         }
  30572 
  30573         if (var_idx < 0 && is_pseudo_var)
  30574             var_idx = resolve_pseudo_var(ctx, s, var_name);
  30575 
  30576         if (var_idx < 0 && var_name == JS_ATOM_arguments &&
  30577             s->has_arguments_binding) {
  30578             /* 'arguments' pseudo variable */
  30579             var_idx = add_arguments_var(ctx, s);
  30580         }
  30581         if (var_idx < 0 && s->is_func_expr && var_name == s->func_name) {
  30582             /* add a new variable with the function name */
  30583             var_idx = add_func_var(ctx, s, var_name);
  30584         }
  30585     }
  30586     if (var_idx >= 0) {
  30587         if ((op == OP_scope_put_var || op == OP_scope_make_ref) &&
  30588             !(var_idx & ARGUMENT_VAR_OFFSET) &&
  30589             s->vars[var_idx].is_const) {
  30590             /* only happens when assigning a function expression name
  30591                in strict mode */
  30592             dbuf_putc(bc, OP_throw_error);
  30593             dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
  30594             dbuf_putc(bc, JS_THROW_VAR_RO);
  30595             goto done;
  30596         }
  30597         /* OP_scope_put_var_init is only used to initialize a
  30598            lexical variable, so it is never used in a with or var object. It
  30599            can be used with a closure (module global variable case). */
  30600         switch (op) {
  30601         case OP_scope_make_ref:
  30602             if (!(var_idx & ARGUMENT_VAR_OFFSET) &&
  30603                 s->vars[var_idx].var_kind == JS_VAR_FUNCTION_NAME) {
  30604                 /* Create a dummy object reference for the func_var */
  30605                 dbuf_putc(bc, OP_object);
  30606                 dbuf_putc(bc, OP_get_loc);
  30607                 dbuf_put_u16(bc, var_idx);
  30608                 dbuf_putc(bc, OP_define_field);
  30609                 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
  30610                 dbuf_putc(bc, OP_push_atom_value);
  30611                 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
  30612             } else
  30613             if (label_done == -1 && can_opt_put_ref_value(bc_buf, ls->pos)) {
  30614                 int get_op;
  30615                 if (var_idx & ARGUMENT_VAR_OFFSET) {
  30616                     get_op = OP_get_arg;
  30617                     var_idx -= ARGUMENT_VAR_OFFSET;
  30618                 } else {
  30619                     if (s->vars[var_idx].is_lexical)
  30620                         get_op = OP_get_loc_check;
  30621                     else
  30622                         get_op = OP_get_loc;
  30623                 }
  30624                 pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls,
  30625                                                    pos_next, get_op, var_idx);
  30626             } else {
  30627                 /* Create a dummy object with a named slot that is
  30628                    a reference to the local variable */
  30629                 if (var_idx & ARGUMENT_VAR_OFFSET) {
  30630                     dbuf_putc(bc, OP_make_arg_ref);
  30631                     dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
  30632                     dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET);
  30633                 } else {
  30634                     dbuf_putc(bc, OP_make_loc_ref);
  30635                     dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
  30636                     dbuf_put_u16(bc, var_idx);
  30637                 }
  30638             }
  30639             break;
  30640         case OP_scope_get_ref:
  30641             dbuf_putc(bc, OP_undefined);
  30642             /* fall thru */
  30643         case OP_scope_get_var_checkthis:
  30644         case OP_scope_get_var_undef:
  30645         case OP_scope_get_var:
  30646         case OP_scope_put_var:
  30647         case OP_scope_put_var_init:
  30648             is_put = (op == OP_scope_put_var || op == OP_scope_put_var_init);
  30649             if (var_idx & ARGUMENT_VAR_OFFSET) {
  30650                 dbuf_putc(bc, OP_get_arg + is_put);
  30651                 dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET);
  30652             } else {
  30653                 if (is_put) {
  30654                     if (s->vars[var_idx].is_lexical) {
  30655                         if (op == OP_scope_put_var_init) {
  30656                             /* 'this' can only be initialized once */
  30657                             if (var_name == JS_ATOM_this)
  30658                                 dbuf_putc(bc, OP_put_loc_check_init);
  30659                             else
  30660                                 dbuf_putc(bc, OP_put_loc);
  30661                         } else {
  30662                             dbuf_putc(bc, OP_put_loc_check);
  30663                         }
  30664                     } else {
  30665                         dbuf_putc(bc, OP_put_loc);
  30666                     }
  30667                 } else {
  30668                     if (s->vars[var_idx].is_lexical) {
  30669                         if (op == OP_scope_get_var_checkthis) {
  30670                             /* only used for 'this' return in derived class constructors */
  30671                             dbuf_putc(bc, OP_get_loc_checkthis);
  30672                         } else {
  30673                             dbuf_putc(bc, OP_get_loc_check);
  30674                         }
  30675                     } else {
  30676                         dbuf_putc(bc, OP_get_loc);
  30677                     }
  30678                 }
  30679                 dbuf_put_u16(bc, var_idx);
  30680             }
  30681             break;
  30682         case OP_scope_delete_var:
  30683             dbuf_putc(bc, OP_push_false);
  30684             break;
  30685         }
  30686         goto done;
  30687     }
  30688     /* check eval object */
  30689     if (!is_arg_scope && s->var_object_idx >= 0 && !is_pseudo_var) {
  30690         dbuf_putc(bc, OP_get_loc);
  30691         dbuf_put_u16(bc, s->var_object_idx);
  30692         var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
  30693     }
  30694     /* check eval object in argument scope */
  30695     if (s->arg_var_object_idx >= 0 && !is_pseudo_var) {
  30696         dbuf_putc(bc, OP_get_loc);
  30697         dbuf_put_u16(bc, s->arg_var_object_idx);
  30698         var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
  30699     }
  30700 
  30701     /* check parent scopes */
  30702     for (fd = s; fd->parent;) {
  30703         scope_level = fd->parent_scope_level;
  30704         fd = fd->parent;
  30705         for (idx = fd->scopes[scope_level].first; idx >= 0;) {
  30706             vd = &fd->vars[idx];
  30707             if (vd->var_name == var_name) {
  30708                 if (op == OP_scope_put_var || op == OP_scope_make_ref) {
  30709                     if (vd->is_const) {
  30710                         dbuf_putc(bc, OP_throw_error);
  30711                         dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
  30712                         dbuf_putc(bc, JS_THROW_VAR_RO);
  30713                         goto done;
  30714                     }
  30715                 }
  30716                 var_idx = idx;
  30717                 break;
  30718             } else if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) {
  30719                 vd->is_captured = 1;
  30720                 idx = get_closure_var(ctx, s, fd, FALSE, idx, vd->var_name, FALSE, FALSE, JS_VAR_NORMAL);
  30721                 if (idx >= 0) {
  30722                     dbuf_putc(bc, OP_get_var_ref);
  30723                     dbuf_put_u16(bc, idx);
  30724                     var_object_test(ctx, s, var_name, op, bc, &label_done, 1);
  30725                 }
  30726             }
  30727             idx = vd->scope_next;
  30728         }
  30729         is_arg_scope = (idx == ARG_SCOPE_END);
  30730         if (var_idx >= 0)
  30731             break;
  30732 
  30733         if (!is_arg_scope) {
  30734             var_idx = find_var(ctx, fd, var_name);
  30735             if (var_idx >= 0)
  30736                 break;
  30737         }
  30738         if (is_pseudo_var) {
  30739             var_idx = resolve_pseudo_var(ctx, fd, var_name);
  30740             if (var_idx >= 0)
  30741                 break;
  30742         }
  30743         if (var_name == JS_ATOM_arguments && fd->has_arguments_binding) {
  30744             var_idx = add_arguments_var(ctx, fd);
  30745             break;
  30746         }
  30747         if (fd->is_func_expr && fd->func_name == var_name) {
  30748             /* add a new variable with the function name */
  30749             var_idx = add_func_var(ctx, fd, var_name);
  30750             break;
  30751         }
  30752 
  30753         /* check eval object */
  30754         if (!is_arg_scope && fd->var_object_idx >= 0 && !is_pseudo_var) {
  30755             vd = &fd->vars[fd->var_object_idx];
  30756             vd->is_captured = 1;
  30757             idx = get_closure_var(ctx, s, fd, FALSE,
  30758                                   fd->var_object_idx, vd->var_name,
  30759                                   FALSE, FALSE, JS_VAR_NORMAL);
  30760             dbuf_putc(bc, OP_get_var_ref);
  30761             dbuf_put_u16(bc, idx);
  30762             var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
  30763         }
  30764 
  30765         /* check eval object in argument scope */
  30766         if (fd->arg_var_object_idx >= 0 && !is_pseudo_var) {
  30767             vd = &fd->vars[fd->arg_var_object_idx];
  30768             vd->is_captured = 1;
  30769             idx = get_closure_var(ctx, s, fd, FALSE,
  30770                                   fd->arg_var_object_idx, vd->var_name,
  30771                                   FALSE, FALSE, JS_VAR_NORMAL);
  30772             dbuf_putc(bc, OP_get_var_ref);
  30773             dbuf_put_u16(bc, idx);
  30774             var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
  30775         }
  30776 
  30777         if (fd->is_eval)
  30778             break; /* it it necessarily the top level function */
  30779     }
  30780 
  30781     /* check direct eval scope (in the closure of the eval function
  30782        which is necessarily at the top level) */
  30783     if (!fd)
  30784         fd = s;
  30785     if (var_idx < 0 && fd->is_eval) {
  30786         int idx1;
  30787         for (idx1 = 0; idx1 < fd->closure_var_count; idx1++) {
  30788             JSClosureVar *cv = &fd->closure_var[idx1];
  30789             if (var_name == cv->var_name) {
  30790                 if (fd != s) {
  30791                     idx = get_closure_var2(ctx, s, fd,
  30792                                            FALSE,
  30793                                            cv->is_arg, idx1,
  30794                                            cv->var_name, cv->is_const,
  30795                                            cv->is_lexical, cv->var_kind);
  30796                 } else {
  30797                     idx = idx1;
  30798                 }
  30799                 goto has_idx;
  30800             } else if ((cv->var_name == JS_ATOM__var_ ||
  30801                         cv->var_name == JS_ATOM__arg_var_ ||
  30802                         cv->var_name == JS_ATOM__with_) && !is_pseudo_var) {
  30803                 int is_with = (cv->var_name == JS_ATOM__with_);
  30804                 if (fd != s) {
  30805                     idx = get_closure_var2(ctx, s, fd,
  30806                                            FALSE,
  30807                                            cv->is_arg, idx1,
  30808                                            cv->var_name, FALSE, FALSE,
  30809                                            JS_VAR_NORMAL);
  30810                 } else {
  30811                     idx = idx1;
  30812                 }
  30813                 dbuf_putc(bc, OP_get_var_ref);
  30814                 dbuf_put_u16(bc, idx);
  30815                 var_object_test(ctx, s, var_name, op, bc, &label_done, is_with);
  30816             }
  30817         }
  30818     }
  30819 
  30820     if (var_idx >= 0) {
  30821         /* find the corresponding closure variable */
  30822         if (var_idx & ARGUMENT_VAR_OFFSET) {
  30823             fd->args[var_idx - ARGUMENT_VAR_OFFSET].is_captured = 1;
  30824             idx = get_closure_var(ctx, s, fd,
  30825                                   TRUE, var_idx - ARGUMENT_VAR_OFFSET,
  30826                                   var_name, FALSE, FALSE, JS_VAR_NORMAL);
  30827         } else {
  30828             fd->vars[var_idx].is_captured = 1;
  30829             idx = get_closure_var(ctx, s, fd,
  30830                                   FALSE, var_idx,
  30831                                   var_name,
  30832                                   fd->vars[var_idx].is_const,
  30833                                   fd->vars[var_idx].is_lexical,
  30834                                   fd->vars[var_idx].var_kind);
  30835         }
  30836         if (idx >= 0) {
  30837         has_idx:
  30838             if ((op == OP_scope_put_var || op == OP_scope_make_ref) &&
  30839                 s->closure_var[idx].is_const) {
  30840                 dbuf_putc(bc, OP_throw_error);
  30841                 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
  30842                 dbuf_putc(bc, JS_THROW_VAR_RO);
  30843                 goto done;
  30844             }
  30845             switch (op) {
  30846             case OP_scope_make_ref:
  30847                 if (s->closure_var[idx].var_kind == JS_VAR_FUNCTION_NAME) {
  30848                     /* Create a dummy object reference for the func_var */
  30849                     dbuf_putc(bc, OP_object);
  30850                     dbuf_putc(bc, OP_get_var_ref);
  30851                     dbuf_put_u16(bc, idx);
  30852                     dbuf_putc(bc, OP_define_field);
  30853                     dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
  30854                     dbuf_putc(bc, OP_push_atom_value);
  30855                     dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
  30856                 } else
  30857                 if (label_done == -1 &&
  30858                     can_opt_put_ref_value(bc_buf, ls->pos)) {
  30859                     int get_op;
  30860                     if (s->closure_var[idx].is_lexical)
  30861                         get_op = OP_get_var_ref_check;
  30862                     else
  30863                         get_op = OP_get_var_ref;
  30864                     pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls,
  30865                                                        pos_next,
  30866                                                        get_op, idx);
  30867                 } else {
  30868                     /* Create a dummy object with a named slot that is
  30869                        a reference to the closure variable */
  30870                     dbuf_putc(bc, OP_make_var_ref_ref);
  30871                     dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
  30872                     dbuf_put_u16(bc, idx);
  30873                 }
  30874                 break;
  30875             case OP_scope_get_ref:
  30876                 /* XXX: should create a dummy object with a named slot that is
  30877                    a reference to the closure variable */
  30878                 dbuf_putc(bc, OP_undefined);
  30879                 /* fall thru */
  30880             case OP_scope_get_var_undef:
  30881             case OP_scope_get_var:
  30882             case OP_scope_put_var:
  30883             case OP_scope_put_var_init:
  30884                 is_put = (op == OP_scope_put_var ||
  30885                           op == OP_scope_put_var_init);
  30886                 if (is_put) {
  30887                     if (s->closure_var[idx].is_lexical) {
  30888                         if (op == OP_scope_put_var_init) {
  30889                             /* 'this' can only be initialized once */
  30890                             if (var_name == JS_ATOM_this)
  30891                                 dbuf_putc(bc, OP_put_var_ref_check_init);
  30892                             else
  30893                                 dbuf_putc(bc, OP_put_var_ref);
  30894                         } else {
  30895                             dbuf_putc(bc, OP_put_var_ref_check);
  30896                         }
  30897                     } else {
  30898                         dbuf_putc(bc, OP_put_var_ref);
  30899                     }
  30900                 } else {
  30901                     if (s->closure_var[idx].is_lexical) {
  30902                         dbuf_putc(bc, OP_get_var_ref_check);
  30903                     } else {
  30904                         dbuf_putc(bc, OP_get_var_ref);
  30905                     }
  30906                 }
  30907                 dbuf_put_u16(bc, idx);
  30908                 break;
  30909             case OP_scope_delete_var:
  30910                 dbuf_putc(bc, OP_push_false);
  30911                 break;
  30912             }
  30913             goto done;
  30914         }
  30915     }
  30916 
  30917     /* global variable access */
  30918 
  30919     switch (op) {
  30920     case OP_scope_make_ref:
  30921         if (label_done == -1 && can_opt_put_global_ref_value(bc_buf, ls->pos)) {
  30922             pos_next = optimize_scope_make_global_ref(ctx, s, bc, bc_buf, ls,
  30923                                                       pos_next, var_name);
  30924         } else {
  30925             dbuf_putc(bc, OP_make_var_ref);
  30926             dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
  30927         }
  30928         break;
  30929     case OP_scope_get_ref:
  30930         /* XXX: should create a dummy object with a named slot that is
  30931            a reference to the global variable */
  30932         dbuf_putc(bc, OP_undefined);
  30933         dbuf_putc(bc, OP_get_var);
  30934         dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
  30935         break;
  30936     case OP_scope_get_var_undef:
  30937     case OP_scope_get_var:
  30938     case OP_scope_put_var:
  30939         dbuf_putc(bc, OP_get_var_undef + (op - OP_scope_get_var_undef));
  30940         dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
  30941         break;
  30942     case OP_scope_put_var_init:
  30943         dbuf_putc(bc, OP_put_var_init);
  30944         dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
  30945         break;
  30946     case OP_scope_delete_var:
  30947         dbuf_putc(bc, OP_delete_var);
  30948         dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
  30949         break;
  30950     }
  30951 done:
  30952     if (label_done >= 0) {
  30953         dbuf_putc(bc, OP_label);
  30954         dbuf_put_u32(bc, label_done);
  30955         s->label_slots[label_done].pos2 = bc->size;
  30956     }
  30957     return pos_next;
  30958 }
  30959 
  30960 /* search in all scopes */
  30961 static int find_private_class_field_all(JSContext *ctx, JSFunctionDef *fd,
  30962                                         JSAtom name, int scope_level)
  30963 {
  30964     int idx;
  30965 
  30966     idx = fd->scopes[scope_level].first;
  30967     while (idx >= 0) {
  30968         if (fd->vars[idx].var_name == name)
  30969             return idx;
  30970         idx = fd->vars[idx].scope_next;
  30971     }
  30972     return -1;
  30973 }
  30974 
  30975 static void get_loc_or_ref(DynBuf *bc, BOOL is_ref, int idx)
  30976 {
  30977     /* if the field is not initialized, the error is catched when
  30978        accessing it */
  30979     if (is_ref)
  30980         dbuf_putc(bc, OP_get_var_ref);
  30981     else
  30982         dbuf_putc(bc, OP_get_loc);
  30983     dbuf_put_u16(bc, idx);
  30984 }
  30985 
  30986 static int resolve_scope_private_field1(JSContext *ctx,
  30987                                         BOOL *pis_ref, int *pvar_kind,
  30988                                         JSFunctionDef *s,
  30989                                         JSAtom var_name, int scope_level)
  30990 {
  30991     int idx, var_kind;
  30992     JSFunctionDef *fd;
  30993     BOOL is_ref;
  30994 
  30995     fd = s;
  30996     is_ref = FALSE;
  30997     for(;;) {
  30998         idx = find_private_class_field_all(ctx, fd, var_name, scope_level);
  30999         if (idx >= 0) {
  31000             var_kind = fd->vars[idx].var_kind;
  31001             if (is_ref) {
  31002                 idx = get_closure_var(ctx, s, fd, FALSE, idx, var_name,
  31003                                       TRUE, TRUE, JS_VAR_NORMAL);
  31004                 if (idx < 0)
  31005                     return -1;
  31006             }
  31007             break;
  31008         }
  31009         scope_level = fd->parent_scope_level;
  31010         if (!fd->parent) {
  31011             if (fd->is_eval) {
  31012                 /* closure of the eval function (top level) */
  31013                 for (idx = 0; idx < fd->closure_var_count; idx++) {
  31014                     JSClosureVar *cv = &fd->closure_var[idx];
  31015                     if (cv->var_name == var_name) {
  31016                         var_kind = cv->var_kind;
  31017                         is_ref = TRUE;
  31018                         if (fd != s) {
  31019                             idx = get_closure_var2(ctx, s, fd,
  31020                                                    FALSE,
  31021                                                    cv->is_arg, idx,
  31022                                                    cv->var_name, cv->is_const,
  31023                                                    cv->is_lexical,
  31024                                                    cv->var_kind);
  31025                             if (idx < 0)
  31026                                 return -1;
  31027                         }
  31028                         goto done;
  31029                     }
  31030                 }
  31031             }
  31032             /* XXX: no line number info */
  31033             JS_ThrowSyntaxErrorAtom(ctx, "undefined private field '%s'",
  31034                                     var_name);
  31035             return -1;
  31036         } else {
  31037             fd = fd->parent;
  31038         }
  31039         is_ref = TRUE;
  31040     }
  31041  done:
  31042     *pis_ref = is_ref;
  31043     *pvar_kind = var_kind;
  31044     return idx;
  31045 }
  31046 
  31047 /* return 0 if OK or -1 if the private field could not be resolved */
  31048 static int resolve_scope_private_field(JSContext *ctx, JSFunctionDef *s,
  31049                                        JSAtom var_name, int scope_level, int op,
  31050                                        DynBuf *bc)
  31051 {
  31052     int idx, var_kind;
  31053     BOOL is_ref;
  31054 
  31055     idx = resolve_scope_private_field1(ctx, &is_ref, &var_kind, s,
  31056                                        var_name, scope_level);
  31057     if (idx < 0)
  31058         return -1;
  31059     assert(var_kind != JS_VAR_NORMAL);
  31060     switch (op) {
  31061     case OP_scope_get_private_field:
  31062     case OP_scope_get_private_field2:
  31063         switch(var_kind) {
  31064         case JS_VAR_PRIVATE_FIELD:
  31065             if (op == OP_scope_get_private_field2)
  31066                 dbuf_putc(bc, OP_dup);
  31067             get_loc_or_ref(bc, is_ref, idx);
  31068             dbuf_putc(bc, OP_get_private_field);
  31069             break;
  31070         case JS_VAR_PRIVATE_METHOD:
  31071             get_loc_or_ref(bc, is_ref, idx);
  31072             dbuf_putc(bc, OP_check_brand);
  31073             if (op != OP_scope_get_private_field2)
  31074                 dbuf_putc(bc, OP_nip);
  31075             break;
  31076         case JS_VAR_PRIVATE_GETTER:
  31077         case JS_VAR_PRIVATE_GETTER_SETTER:
  31078             if (op == OP_scope_get_private_field2)
  31079                 dbuf_putc(bc, OP_dup);
  31080             get_loc_or_ref(bc, is_ref, idx);
  31081             dbuf_putc(bc, OP_check_brand);
  31082             dbuf_putc(bc, OP_call_method);
  31083             dbuf_put_u16(bc, 0);
  31084             break;
  31085         case JS_VAR_PRIVATE_SETTER:
  31086             /* XXX: add clearer error message */
  31087             dbuf_putc(bc, OP_throw_error);
  31088             dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
  31089             dbuf_putc(bc, JS_THROW_VAR_RO);
  31090             break;
  31091         default:
  31092             abort();
  31093         }
  31094         break;
  31095     case OP_scope_put_private_field:
  31096         switch(var_kind) {
  31097         case JS_VAR_PRIVATE_FIELD:
  31098             get_loc_or_ref(bc, is_ref, idx);
  31099             dbuf_putc(bc, OP_put_private_field);
  31100             break;
  31101         case JS_VAR_PRIVATE_METHOD:
  31102         case JS_VAR_PRIVATE_GETTER:
  31103             /* XXX: add clearer error message */
  31104             dbuf_putc(bc, OP_throw_error);
  31105             dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
  31106             dbuf_putc(bc, JS_THROW_VAR_RO);
  31107             break;
  31108         case JS_VAR_PRIVATE_SETTER:
  31109         case JS_VAR_PRIVATE_GETTER_SETTER:
  31110             {
  31111                 JSAtom setter_name = get_private_setter_name(ctx, var_name);
  31112                 if (setter_name == JS_ATOM_NULL)
  31113                     return -1;
  31114                 idx = resolve_scope_private_field1(ctx, &is_ref,
  31115                                                    &var_kind, s,
  31116                                                    setter_name, scope_level);
  31117                 JS_FreeAtom(ctx, setter_name);
  31118                 if (idx < 0)
  31119                     return -1;
  31120                 assert(var_kind == JS_VAR_PRIVATE_SETTER);
  31121                 get_loc_or_ref(bc, is_ref, idx);
  31122                 dbuf_putc(bc, OP_swap);
  31123                 /* obj func value */
  31124                 dbuf_putc(bc, OP_rot3r);
  31125                 /* value obj func */
  31126                 dbuf_putc(bc, OP_check_brand);
  31127                 dbuf_putc(bc, OP_rot3l);
  31128                 /* obj func value */
  31129                 dbuf_putc(bc, OP_call_method);
  31130                 dbuf_put_u16(bc, 1);
  31131                 dbuf_putc(bc, OP_drop);
  31132             }
  31133             break;
  31134         default:
  31135             abort();
  31136         }
  31137         break;
  31138     case OP_scope_in_private_field:
  31139         get_loc_or_ref(bc, is_ref, idx);
  31140         dbuf_putc(bc, OP_private_in);
  31141         break;
  31142     default:
  31143         abort();
  31144     }
  31145     return 0;
  31146 }
  31147 
  31148 static void mark_eval_captured_variables(JSContext *ctx, JSFunctionDef *s,
  31149                                          int scope_level)
  31150 {
  31151     int idx;
  31152     JSVarDef *vd;
  31153 
  31154     for (idx = s->scopes[scope_level].first; idx >= 0;) {
  31155         vd = &s->vars[idx];
  31156         vd->is_captured = 1;
  31157         idx = vd->scope_next;
  31158     }
  31159 }
  31160 
  31161 /* XXX: should handle the argument scope generically */
  31162 static BOOL is_var_in_arg_scope(const JSVarDef *vd)
  31163 {
  31164     return (vd->var_name == JS_ATOM_home_object ||
  31165             vd->var_name == JS_ATOM_this_active_func ||
  31166             vd->var_name == JS_ATOM_new_target ||
  31167             vd->var_name == JS_ATOM_this ||
  31168             vd->var_name == JS_ATOM__arg_var_ ||
  31169             vd->var_kind == JS_VAR_FUNCTION_NAME);
  31170 }
  31171 
  31172 static void add_eval_variables(JSContext *ctx, JSFunctionDef *s)
  31173 {
  31174     JSFunctionDef *fd;
  31175     JSVarDef *vd;
  31176     int i, scope_level, scope_idx;
  31177     BOOL has_arguments_binding, has_this_binding, is_arg_scope;
  31178 
  31179     /* in non strict mode, variables are created in the caller's
  31180        environment object */
  31181     if (!s->is_eval && !(s->js_mode & JS_MODE_STRICT)) {
  31182         s->var_object_idx = add_var(ctx, s, JS_ATOM__var_);
  31183         if (s->has_parameter_expressions) {
  31184             /* an additional variable object is needed for the
  31185                argument scope */
  31186             s->arg_var_object_idx = add_var(ctx, s, JS_ATOM__arg_var_);
  31187         }
  31188     }
  31189 
  31190     /* eval can potentially use 'arguments' so we must define it */
  31191     has_this_binding = s->has_this_binding;
  31192     if (has_this_binding) {
  31193         if (s->this_var_idx < 0)
  31194             s->this_var_idx = add_var_this(ctx, s);
  31195         if (s->new_target_var_idx < 0)
  31196             s->new_target_var_idx = add_var(ctx, s, JS_ATOM_new_target);
  31197         if (s->is_derived_class_constructor && s->this_active_func_var_idx < 0)
  31198             s->this_active_func_var_idx = add_var(ctx, s, JS_ATOM_this_active_func);
  31199         if (s->has_home_object && s->home_object_var_idx < 0)
  31200             s->home_object_var_idx = add_var(ctx, s, JS_ATOM_home_object);
  31201     }
  31202     has_arguments_binding = s->has_arguments_binding;
  31203     if (has_arguments_binding) {
  31204         add_arguments_var(ctx, s);
  31205         /* also add an arguments binding in the argument scope to
  31206            raise an error if a direct eval in the argument scope tries
  31207            to redefine it */
  31208         if (s->has_parameter_expressions && !(s->js_mode & JS_MODE_STRICT))
  31209             add_arguments_arg(ctx, s);
  31210     }
  31211     if (s->is_func_expr && s->func_name != JS_ATOM_NULL)
  31212         add_func_var(ctx, s, s->func_name);
  31213 
  31214     /* eval can use all the variables of the enclosing functions, so
  31215        they must be all put in the closure. The closure variables are
  31216        ordered by scope. It works only because no closure are created
  31217        before. */
  31218     assert(s->is_eval || s->closure_var_count == 0);
  31219 
  31220     /* XXX: inefficient, but eval performance is less critical */
  31221     fd = s;
  31222     for(;;) {
  31223         scope_level = fd->parent_scope_level;
  31224         fd = fd->parent;
  31225         if (!fd)
  31226             break;
  31227         /* add 'this' if it was not previously added */
  31228         if (!has_this_binding && fd->has_this_binding) {
  31229             if (fd->this_var_idx < 0)
  31230                 fd->this_var_idx = add_var_this(ctx, fd);
  31231             if (fd->new_target_var_idx < 0)
  31232                 fd->new_target_var_idx = add_var(ctx, fd, JS_ATOM_new_target);
  31233             if (fd->is_derived_class_constructor && fd->this_active_func_var_idx < 0)
  31234                 fd->this_active_func_var_idx = add_var(ctx, fd, JS_ATOM_this_active_func);
  31235             if (fd->has_home_object && fd->home_object_var_idx < 0)
  31236                 fd->home_object_var_idx = add_var(ctx, fd, JS_ATOM_home_object);
  31237             has_this_binding = TRUE;
  31238         }
  31239         /* add 'arguments' if it was not previously added */
  31240         if (!has_arguments_binding && fd->has_arguments_binding) {
  31241             add_arguments_var(ctx, fd);
  31242             has_arguments_binding = TRUE;
  31243         }
  31244         /* add function name */
  31245         if (fd->is_func_expr && fd->func_name != JS_ATOM_NULL)
  31246             add_func_var(ctx, fd, fd->func_name);
  31247 
  31248         /* add lexical variables */
  31249         scope_idx = fd->scopes[scope_level].first;
  31250         while (scope_idx >= 0) {
  31251             vd = &fd->vars[scope_idx];
  31252             vd->is_captured = 1;
  31253             get_closure_var(ctx, s, fd, FALSE, scope_idx,
  31254                             vd->var_name, vd->is_const, vd->is_lexical, vd->var_kind);
  31255             scope_idx = vd->scope_next;
  31256         }
  31257         is_arg_scope = (scope_idx == ARG_SCOPE_END);
  31258         if (!is_arg_scope) {
  31259             /* add unscoped variables */
  31260             /* XXX: propagate is_const and var_kind too ? */
  31261             for(i = 0; i < fd->arg_count; i++) {
  31262                 vd = &fd->args[i];
  31263                 if (vd->var_name != JS_ATOM_NULL) {
  31264                     get_closure_var(ctx, s, fd,
  31265                                     TRUE, i, vd->var_name, FALSE,
  31266                                     vd->is_lexical, JS_VAR_NORMAL);
  31267                 }
  31268             }
  31269             for(i = 0; i < fd->var_count; i++) {
  31270                 vd = &fd->vars[i];
  31271                 /* do not close top level last result */
  31272                 if (vd->scope_level == 0 &&
  31273                     vd->var_name != JS_ATOM__ret_ &&
  31274                     vd->var_name != JS_ATOM_NULL) {
  31275                     get_closure_var(ctx, s, fd,
  31276                                     FALSE, i, vd->var_name, FALSE,
  31277                                     vd->is_lexical, JS_VAR_NORMAL);
  31278                 }
  31279             }
  31280         } else {
  31281             for(i = 0; i < fd->var_count; i++) {
  31282                 vd = &fd->vars[i];
  31283                 /* do not close top level last result */
  31284                 if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) {
  31285                     get_closure_var(ctx, s, fd,
  31286                                     FALSE, i, vd->var_name, FALSE,
  31287                                     vd->is_lexical, JS_VAR_NORMAL);
  31288                 }
  31289             }
  31290         }
  31291         if (fd->is_eval) {
  31292             int idx;
  31293             /* add direct eval variables (we are necessarily at the
  31294                top level) */
  31295             for (idx = 0; idx < fd->closure_var_count; idx++) {
  31296                 JSClosureVar *cv = &fd->closure_var[idx];
  31297                 get_closure_var2(ctx, s, fd,
  31298                                  FALSE, cv->is_arg,
  31299                                  idx, cv->var_name, cv->is_const,
  31300                                  cv->is_lexical, cv->var_kind);
  31301             }
  31302         }
  31303     }
  31304 }
  31305 
  31306 static void set_closure_from_var(JSContext *ctx, JSClosureVar *cv,
  31307                                  JSVarDef *vd, int var_idx)
  31308 {
  31309     cv->is_local = TRUE;
  31310     cv->is_arg = FALSE;
  31311     cv->is_const = vd->is_const;
  31312     cv->is_lexical = vd->is_lexical;
  31313     cv->var_kind = vd->var_kind;
  31314     cv->var_idx = var_idx;
  31315     cv->var_name = JS_DupAtom(ctx, vd->var_name);
  31316 }
  31317 
  31318 /* for direct eval compilation: add references to the variables of the
  31319    calling function */
  31320 static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s,
  31321                                              JSFunctionBytecode *b, int scope_idx)
  31322 {
  31323     int i, count;
  31324     JSVarDef *vd;
  31325     BOOL is_arg_scope;
  31326 
  31327     count = b->arg_count + b->var_count + b->closure_var_count;
  31328     s->closure_var = NULL;
  31329     s->closure_var_count = 0;
  31330     s->closure_var_size = count;
  31331     if (count == 0)
  31332         return 0;
  31333     s->closure_var = js_malloc(ctx, sizeof(s->closure_var[0]) * count);
  31334     if (!s->closure_var)
  31335         return -1;
  31336     /* Add lexical variables in scope at the point of evaluation */
  31337     for (i = scope_idx; i >= 0;) {
  31338         vd = &b->vardefs[b->arg_count + i];
  31339         if (vd->scope_level > 0) {
  31340             JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
  31341             set_closure_from_var(ctx, cv, vd, i);
  31342         }
  31343         i = vd->scope_next;
  31344     }
  31345     is_arg_scope = (i == ARG_SCOPE_END);
  31346     if (!is_arg_scope) {
  31347         /* Add argument variables */
  31348         for(i = 0; i < b->arg_count; i++) {
  31349             JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
  31350             vd = &b->vardefs[i];
  31351             cv->is_local = TRUE;
  31352             cv->is_arg = TRUE;
  31353             cv->is_const = FALSE;
  31354             cv->is_lexical = FALSE;
  31355             cv->var_kind = JS_VAR_NORMAL;
  31356             cv->var_idx = i;
  31357             cv->var_name = JS_DupAtom(ctx, vd->var_name);
  31358         }
  31359         /* Add local non lexical variables */
  31360         for(i = 0; i < b->var_count; i++) {
  31361             vd = &b->vardefs[b->arg_count + i];
  31362             if (vd->scope_level == 0 && vd->var_name != JS_ATOM__ret_) {
  31363                 JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
  31364                 set_closure_from_var(ctx, cv, vd, i);
  31365             }
  31366         }
  31367     } else {
  31368         /* only add pseudo variables */
  31369         for(i = 0; i < b->var_count; i++) {
  31370             vd = &b->vardefs[b->arg_count + i];
  31371             if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) {
  31372                 JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
  31373                 set_closure_from_var(ctx, cv, vd, i);
  31374             }
  31375         }
  31376     }
  31377     for(i = 0; i < b->closure_var_count; i++) {
  31378         JSClosureVar *cv0 = &b->closure_var[i];
  31379         JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
  31380         cv->is_local = FALSE;
  31381         cv->is_arg = cv0->is_arg;
  31382         cv->is_const = cv0->is_const;
  31383         cv->is_lexical = cv0->is_lexical;
  31384         cv->var_kind = cv0->var_kind;
  31385         cv->var_idx = i;
  31386         cv->var_name = JS_DupAtom(ctx, cv0->var_name);
  31387     }
  31388     return 0;
  31389 }
  31390 
  31391 typedef struct CodeContext {
  31392     const uint8_t *bc_buf; /* code buffer */
  31393     int bc_len;   /* length of the code buffer */
  31394     int pos;      /* position past the matched code pattern */
  31395     int line_num; /* last visited OP_line_num parameter or -1 */
  31396     int op;
  31397     int idx;
  31398     int label;
  31399     int val;
  31400     JSAtom atom;
  31401 } CodeContext;
  31402 
  31403 #define M2(op1, op2)            ((op1) | ((op2) << 8))
  31404 #define M3(op1, op2, op3)       ((op1) | ((op2) << 8) | ((op3) << 16))
  31405 #define M4(op1, op2, op3, op4)  ((op1) | ((op2) << 8) | ((op3) << 16) | ((op4) << 24))
  31406 
  31407 static BOOL code_match(CodeContext *s, int pos, ...)
  31408 {
  31409     const uint8_t *tab = s->bc_buf;
  31410     int op, len, op1, line_num, pos_next;
  31411     va_list ap;
  31412     BOOL ret = FALSE;
  31413 
  31414     line_num = -1;
  31415     va_start(ap, pos);
  31416 
  31417     for(;;) {
  31418         op1 = va_arg(ap, int);
  31419         if (op1 == -1) {
  31420             s->pos = pos;
  31421             s->line_num = line_num;
  31422             ret = TRUE;
  31423             break;
  31424         }
  31425         for (;;) {
  31426             if (pos >= s->bc_len)
  31427                 goto done;
  31428             op = tab[pos];
  31429             len = opcode_info[op].size;
  31430             pos_next = pos + len;
  31431             if (pos_next > s->bc_len)
  31432                 goto done;
  31433             if (op == OP_line_num) {
  31434                 line_num = get_u32(tab + pos + 1);
  31435                 pos = pos_next;
  31436             } else {
  31437                 break;
  31438             }
  31439         }
  31440         if (op != op1) {
  31441             if (op1 == (uint8_t)op1 || !op)
  31442                 break;
  31443             if (op != (uint8_t)op1
  31444             &&  op != (uint8_t)(op1 >> 8)
  31445             &&  op != (uint8_t)(op1 >> 16)
  31446             &&  op != (uint8_t)(op1 >> 24)) {
  31447                 break;
  31448             }
  31449             s->op = op;
  31450         }
  31451 
  31452         pos++;
  31453         switch(opcode_info[op].fmt) {
  31454         case OP_FMT_loc8:
  31455         case OP_FMT_u8:
  31456             {
  31457                 int idx = tab[pos];
  31458                 int arg = va_arg(ap, int);
  31459                 if (arg == -1) {
  31460                     s->idx = idx;
  31461                 } else {
  31462                     if (arg != idx)
  31463                         goto done;
  31464                 }
  31465                 break;
  31466             }
  31467         case OP_FMT_u16:
  31468         case OP_FMT_npop:
  31469         case OP_FMT_loc:
  31470         case OP_FMT_arg:
  31471         case OP_FMT_var_ref:
  31472             {
  31473                 int idx = get_u16(tab + pos);
  31474                 int arg = va_arg(ap, int);
  31475                 if (arg == -1) {
  31476                     s->idx = idx;
  31477                 } else {
  31478                     if (arg != idx)
  31479                         goto done;
  31480                 }
  31481                 break;
  31482             }
  31483         case OP_FMT_i32:
  31484         case OP_FMT_u32:
  31485         case OP_FMT_label:
  31486         case OP_FMT_const:
  31487             {
  31488                 s->label = get_u32(tab + pos);
  31489                 break;
  31490             }
  31491         case OP_FMT_label_u16:
  31492             {
  31493                 s->label = get_u32(tab + pos);
  31494                 s->val = get_u16(tab + pos + 4);
  31495                 break;
  31496             }
  31497         case OP_FMT_atom:
  31498             {
  31499                 s->atom = get_u32(tab + pos);
  31500                 break;
  31501             }
  31502         case OP_FMT_atom_u8:
  31503             {
  31504                 s->atom = get_u32(tab + pos);
  31505                 s->val = get_u8(tab + pos + 4);
  31506                 break;
  31507             }
  31508         case OP_FMT_atom_u16:
  31509             {
  31510                 s->atom = get_u32(tab + pos);
  31511                 s->val = get_u16(tab + pos + 4);
  31512                 break;
  31513             }
  31514         case OP_FMT_atom_label_u8:
  31515             {
  31516                 s->atom = get_u32(tab + pos);
  31517                 s->label = get_u32(tab + pos + 4);
  31518                 s->val = get_u8(tab + pos + 8);
  31519                 break;
  31520             }
  31521         default:
  31522             break;
  31523         }
  31524         pos = pos_next;
  31525     }
  31526  done:
  31527     va_end(ap);
  31528     return ret;
  31529 }
  31530 
  31531 static void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, DynBuf *bc)
  31532 {
  31533     int i, idx, label_next = -1;
  31534 
  31535     /* add the hoisted functions in arguments and local variables */
  31536     for(i = 0; i < s->arg_count; i++) {
  31537         JSVarDef *vd = &s->args[i];
  31538         if (vd->func_pool_idx >= 0) {
  31539             dbuf_putc(bc, OP_fclosure);
  31540             dbuf_put_u32(bc, vd->func_pool_idx);
  31541             dbuf_putc(bc, OP_put_arg);
  31542             dbuf_put_u16(bc, i);
  31543         }
  31544     }
  31545     for(i = 0; i < s->var_count; i++) {
  31546         JSVarDef *vd = &s->vars[i];
  31547         if (vd->scope_level == 0 && vd->func_pool_idx >= 0) {
  31548             dbuf_putc(bc, OP_fclosure);
  31549             dbuf_put_u32(bc, vd->func_pool_idx);
  31550             dbuf_putc(bc, OP_put_loc);
  31551             dbuf_put_u16(bc, i);
  31552         }
  31553     }
  31554 
  31555     /* the module global variables must be initialized before
  31556        evaluating the module so that the exported functions are
  31557        visible if there are cyclic module references */
  31558     if (s->module) {
  31559         label_next = new_label_fd(s, -1);
  31560 
  31561         /* if 'this' is true, initialize the global variables and return */
  31562         dbuf_putc(bc, OP_push_this);
  31563         dbuf_putc(bc, OP_if_false);
  31564         dbuf_put_u32(bc, label_next);
  31565         update_label(s, label_next, 1);
  31566         s->jump_size++;
  31567     }
  31568 
  31569     /* add the global variables (only happens if s->is_global_var is
  31570        true) */
  31571     for(i = 0; i < s->global_var_count; i++) {
  31572         JSGlobalVar *hf = &s->global_vars[i];
  31573         int has_closure = 0;
  31574         BOOL force_init = hf->force_init;
  31575         /* we are in an eval, so the closure contains all the
  31576            enclosing variables */
  31577         /* If the outer function has a variable environment,
  31578            create a property for the variable there */
  31579         for(idx = 0; idx < s->closure_var_count; idx++) {
  31580             JSClosureVar *cv = &s->closure_var[idx];
  31581             if (cv->var_name == hf->var_name) {
  31582                 has_closure = 2;
  31583                 force_init = FALSE;
  31584                 break;
  31585             }
  31586             if (cv->var_name == JS_ATOM__var_ ||
  31587                 cv->var_name == JS_ATOM__arg_var_) {
  31588                 dbuf_putc(bc, OP_get_var_ref);
  31589                 dbuf_put_u16(bc, idx);
  31590                 has_closure = 1;
  31591                 force_init = TRUE;
  31592                 break;
  31593             }
  31594         }
  31595         if (!has_closure) {
  31596             int flags;
  31597 
  31598             flags = 0;
  31599             if (s->eval_type != JS_EVAL_TYPE_GLOBAL)
  31600                 flags |= JS_PROP_CONFIGURABLE;
  31601             if (hf->cpool_idx >= 0 && !hf->is_lexical) {
  31602                 /* global function definitions need a specific handling */
  31603                 dbuf_putc(bc, OP_fclosure);
  31604                 dbuf_put_u32(bc, hf->cpool_idx);
  31605 
  31606                 dbuf_putc(bc, OP_define_func);
  31607                 dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
  31608                 dbuf_putc(bc, flags);
  31609 
  31610                 goto done_global_var;
  31611             } else {
  31612                 if (hf->is_lexical) {
  31613                     flags |= DEFINE_GLOBAL_LEX_VAR;
  31614                     if (!hf->is_const)
  31615                         flags |= JS_PROP_WRITABLE;
  31616                 }
  31617                 dbuf_putc(bc, OP_define_var);
  31618                 dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
  31619                 dbuf_putc(bc, flags);
  31620             }
  31621         }
  31622         if (hf->cpool_idx >= 0 || force_init) {
  31623             if (hf->cpool_idx >= 0) {
  31624                 dbuf_putc(bc, OP_fclosure);
  31625                 dbuf_put_u32(bc, hf->cpool_idx);
  31626                 if (hf->var_name == JS_ATOM__default_) {
  31627                     /* set default export function name */
  31628                     dbuf_putc(bc, OP_set_name);
  31629                     dbuf_put_u32(bc, JS_DupAtom(ctx, JS_ATOM_default));
  31630                 }
  31631             } else {
  31632                 dbuf_putc(bc, OP_undefined);
  31633             }
  31634             if (has_closure == 2) {
  31635                 dbuf_putc(bc, OP_put_var_ref);
  31636                 dbuf_put_u16(bc, idx);
  31637             } else if (has_closure == 1) {
  31638                 dbuf_putc(bc, OP_define_field);
  31639                 dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
  31640                 dbuf_putc(bc, OP_drop);
  31641             } else {
  31642                 /* XXX: Check if variable is writable and enumerable */
  31643                 dbuf_putc(bc, OP_put_var);
  31644                 dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
  31645             }
  31646         }
  31647     done_global_var:
  31648         JS_FreeAtom(ctx, hf->var_name);
  31649     }
  31650 
  31651     if (s->module) {
  31652         dbuf_putc(bc, OP_return_undef);
  31653 
  31654         dbuf_putc(bc, OP_label);
  31655         dbuf_put_u32(bc, label_next);
  31656         s->label_slots[label_next].pos2 = bc->size;
  31657     }
  31658 
  31659     js_free(ctx, s->global_vars);
  31660     s->global_vars = NULL;
  31661     s->global_var_count = 0;
  31662     s->global_var_size = 0;
  31663 }
  31664 
  31665 static int skip_dead_code(JSFunctionDef *s, const uint8_t *bc_buf, int bc_len,
  31666                           int pos, int *linep)
  31667 {
  31668     int op, len, label;
  31669 
  31670     for (; pos < bc_len; pos += len) {
  31671         op = bc_buf[pos];
  31672         len = opcode_info[op].size;
  31673         if (op == OP_line_num) {
  31674             *linep = get_u32(bc_buf + pos + 1);
  31675         } else
  31676         if (op == OP_label) {
  31677             label = get_u32(bc_buf + pos + 1);
  31678             if (update_label(s, label, 0) > 0)
  31679                 break;
  31680 #if 0
  31681             if (s->label_slots[label].first_reloc) {
  31682                 printf("line %d: unreferenced label %d:%d has relocations\n",
  31683                        *linep, label, s->label_slots[label].pos2);
  31684             }
  31685 #endif
  31686             assert(s->label_slots[label].first_reloc == NULL);
  31687         } else {
  31688             /* XXX: output a warning for unreachable code? */
  31689             JSAtom atom;
  31690             switch(opcode_info[op].fmt) {
  31691             case OP_FMT_label:
  31692             case OP_FMT_label_u16:
  31693                 label = get_u32(bc_buf + pos + 1);
  31694                 update_label(s, label, -1);
  31695                 break;
  31696             case OP_FMT_atom_label_u8:
  31697             case OP_FMT_atom_label_u16:
  31698                 label = get_u32(bc_buf + pos + 5);
  31699                 update_label(s, label, -1);
  31700                 /* fall thru */
  31701             case OP_FMT_atom:
  31702             case OP_FMT_atom_u8:
  31703             case OP_FMT_atom_u16:
  31704                 atom = get_u32(bc_buf + pos + 1);
  31705                 JS_FreeAtom(s->ctx, atom);
  31706                 break;
  31707             default:
  31708                 break;
  31709             }
  31710         }
  31711     }
  31712     return pos;
  31713 }
  31714 
  31715 static int get_label_pos(JSFunctionDef *s, int label)
  31716 {
  31717     int i, pos;
  31718     for (i = 0; i < 20; i++) {
  31719         pos = s->label_slots[label].pos;
  31720         for (;;) {
  31721             switch (s->byte_code.buf[pos]) {
  31722             case OP_line_num:
  31723             case OP_label:
  31724                 pos += 5;
  31725                 continue;
  31726             case OP_goto:
  31727                 label = get_u32(s->byte_code.buf + pos + 1);
  31728                 break;
  31729             default:
  31730                 return pos;
  31731             }
  31732             break;
  31733         }
  31734     }
  31735     return pos;
  31736 }
  31737 
  31738 /* convert global variable accesses to local variables or closure
  31739    variables when necessary */
  31740 static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s)
  31741 {
  31742     int pos, pos_next, bc_len, op, len, i, idx, line_num;
  31743     uint8_t *bc_buf;
  31744     JSAtom var_name;
  31745     DynBuf bc_out;
  31746     CodeContext cc;
  31747     int scope;
  31748 
  31749     cc.bc_buf = bc_buf = s->byte_code.buf;
  31750     cc.bc_len = bc_len = s->byte_code.size;
  31751     js_dbuf_init(ctx, &bc_out);
  31752 
  31753     /* first pass for runtime checks (must be done before the
  31754        variables are created) */
  31755     for(i = 0; i < s->global_var_count; i++) {
  31756         JSGlobalVar *hf = &s->global_vars[i];
  31757         int flags;
  31758 
  31759         /* check if global variable (XXX: simplify) */
  31760         for(idx = 0; idx < s->closure_var_count; idx++) {
  31761             JSClosureVar *cv = &s->closure_var[idx];
  31762             if (cv->var_name == hf->var_name) {
  31763                 if (s->eval_type == JS_EVAL_TYPE_DIRECT &&
  31764                     cv->is_lexical) {
  31765                     /* Check if a lexical variable is
  31766                        redefined as 'var'. XXX: Could abort
  31767                        compilation here, but for consistency
  31768                        with the other checks, we delay the
  31769                        error generation. */
  31770                     dbuf_putc(&bc_out, OP_throw_error);
  31771                     dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name));
  31772                     dbuf_putc(&bc_out, JS_THROW_VAR_REDECL);
  31773                 }
  31774                 goto next;
  31775             }
  31776             if (cv->var_name == JS_ATOM__var_ ||
  31777                 cv->var_name == JS_ATOM__arg_var_)
  31778                 goto next;
  31779         }
  31780 
  31781         dbuf_putc(&bc_out, OP_check_define_var);
  31782         dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name));
  31783         flags = 0;
  31784         if (hf->is_lexical)
  31785             flags |= DEFINE_GLOBAL_LEX_VAR;
  31786         if (hf->cpool_idx >= 0)
  31787             flags |= DEFINE_GLOBAL_FUNC_VAR;
  31788         dbuf_putc(&bc_out, flags);
  31789     next: ;
  31790     }
  31791 
  31792     line_num = 0; /* avoid warning */
  31793     for (pos = 0; pos < bc_len; pos = pos_next) {
  31794         op = bc_buf[pos];
  31795         len = opcode_info[op].size;
  31796         pos_next = pos + len;
  31797         switch(op) {
  31798         case OP_line_num:
  31799             line_num = get_u32(bc_buf + pos + 1);
  31800             s->line_number_size++;
  31801             goto no_change;
  31802 
  31803         case OP_eval: /* convert scope index to adjusted variable index */
  31804             {
  31805                 int call_argc = get_u16(bc_buf + pos + 1);
  31806                 scope = get_u16(bc_buf + pos + 1 + 2);
  31807                 mark_eval_captured_variables(ctx, s, scope);
  31808                 dbuf_putc(&bc_out, op);
  31809                 dbuf_put_u16(&bc_out, call_argc);
  31810                 dbuf_put_u16(&bc_out, s->scopes[scope].first + 1);
  31811             }
  31812             break;
  31813         case OP_apply_eval: /* convert scope index to adjusted variable index */
  31814             scope = get_u16(bc_buf + pos + 1);
  31815             mark_eval_captured_variables(ctx, s, scope);
  31816             dbuf_putc(&bc_out, op);
  31817             dbuf_put_u16(&bc_out, s->scopes[scope].first + 1);
  31818             break;
  31819         case OP_scope_get_var_checkthis:
  31820         case OP_scope_get_var_undef:
  31821         case OP_scope_get_var:
  31822         case OP_scope_put_var:
  31823         case OP_scope_delete_var:
  31824         case OP_scope_get_ref:
  31825         case OP_scope_put_var_init:
  31826             var_name = get_u32(bc_buf + pos + 1);
  31827             scope = get_u16(bc_buf + pos + 5);
  31828             pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out,
  31829                                          NULL, NULL, pos_next);
  31830             JS_FreeAtom(ctx, var_name);
  31831             break;
  31832         case OP_scope_make_ref:
  31833             {
  31834                 int label;
  31835                 LabelSlot *ls;
  31836                 var_name = get_u32(bc_buf + pos + 1);
  31837                 label = get_u32(bc_buf + pos + 5);
  31838                 scope = get_u16(bc_buf + pos + 9);
  31839                 ls = &s->label_slots[label];
  31840                 ls->ref_count--;  /* always remove label reference */
  31841                 pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out,
  31842                                              bc_buf, ls, pos_next);
  31843                 JS_FreeAtom(ctx, var_name);
  31844             }
  31845             break;
  31846         case OP_scope_get_private_field:
  31847         case OP_scope_get_private_field2:
  31848         case OP_scope_put_private_field:
  31849         case OP_scope_in_private_field:
  31850             {
  31851                 int ret;
  31852                 var_name = get_u32(bc_buf + pos + 1);
  31853                 scope = get_u16(bc_buf + pos + 5);
  31854                 ret = resolve_scope_private_field(ctx, s, var_name, scope, op, &bc_out);
  31855                 if (ret < 0)
  31856                     goto fail;
  31857                 JS_FreeAtom(ctx, var_name);
  31858             }
  31859             break;
  31860         case OP_gosub:
  31861             s->jump_size++;
  31862             if (OPTIMIZE) {
  31863                 /* remove calls to empty finalizers  */
  31864                 int label;
  31865                 LabelSlot *ls;
  31866 
  31867                 label = get_u32(bc_buf + pos + 1);
  31868                 assert(label >= 0 && label < s->label_count);
  31869                 ls = &s->label_slots[label];
  31870                 if (code_match(&cc, ls->pos, OP_ret, -1)) {
  31871                     ls->ref_count--;
  31872                     break;
  31873                 }
  31874             }
  31875             goto no_change;
  31876         case OP_drop:
  31877             if (0) {
  31878                 /* remove drops before return_undef */
  31879                 /* do not perform this optimization in pass2 because
  31880                    it breaks patterns recognised in resolve_labels */
  31881                 int pos1 = pos_next;
  31882                 int line1 = line_num;
  31883                 while (code_match(&cc, pos1, OP_drop, -1)) {
  31884                     if (cc.line_num >= 0) line1 = cc.line_num;
  31885                     pos1 = cc.pos;
  31886                 }
  31887                 if (code_match(&cc, pos1, OP_return_undef, -1)) {
  31888                     pos_next = pos1;
  31889                     if (line1 != -1 && line1 != line_num) {
  31890                         line_num = line1;
  31891                         s->line_number_size++;
  31892                         dbuf_putc(&bc_out, OP_line_num);
  31893                         dbuf_put_u32(&bc_out, line_num);
  31894                     }
  31895                     break;
  31896                 }
  31897             }
  31898             goto no_change;
  31899         case OP_insert3:
  31900             if (OPTIMIZE) {
  31901                 /* Transformation: insert3 put_array_el|put_ref_value drop -> put_array_el|put_ref_value */
  31902                 if (code_match(&cc, pos_next, M2(OP_put_array_el, OP_put_ref_value), OP_drop, -1)) {
  31903                     dbuf_putc(&bc_out, cc.op);
  31904                     pos_next = cc.pos;
  31905                     if (cc.line_num != -1 && cc.line_num != line_num) {
  31906                         line_num = cc.line_num;
  31907                         s->line_number_size++;
  31908                         dbuf_putc(&bc_out, OP_line_num);
  31909                         dbuf_put_u32(&bc_out, line_num);
  31910                     }
  31911                     break;
  31912                 }
  31913             }
  31914             goto no_change;
  31915 
  31916         case OP_goto:
  31917             s->jump_size++;
  31918             /* fall thru */
  31919         case OP_tail_call:
  31920         case OP_tail_call_method:
  31921         case OP_return:
  31922         case OP_return_undef:
  31923         case OP_throw:
  31924         case OP_throw_error:
  31925         case OP_ret:
  31926             if (OPTIMIZE) {
  31927                 /* remove dead code */
  31928                 int line = -1;
  31929                 dbuf_put(&bc_out, bc_buf + pos, len);
  31930                 pos = skip_dead_code(s, bc_buf, bc_len, pos + len, &line);
  31931                 pos_next = pos;
  31932                 if (pos < bc_len && line >= 0 && line_num != line) {
  31933                     line_num = line;
  31934                     s->line_number_size++;
  31935                     dbuf_putc(&bc_out, OP_line_num);
  31936                     dbuf_put_u32(&bc_out, line_num);
  31937                 }
  31938                 break;
  31939             }
  31940             goto no_change;
  31941 
  31942         case OP_label:
  31943             {
  31944                 int label;
  31945                 LabelSlot *ls;
  31946 
  31947                 label = get_u32(bc_buf + pos + 1);
  31948                 assert(label >= 0 && label < s->label_count);
  31949                 ls = &s->label_slots[label];
  31950                 ls->pos2 = bc_out.size + opcode_info[op].size;
  31951             }
  31952             goto no_change;
  31953 
  31954         case OP_enter_scope:
  31955             {
  31956                 int scope_idx, scope = get_u16(bc_buf + pos + 1);
  31957 
  31958                 if (scope == s->body_scope) {
  31959                     instantiate_hoisted_definitions(ctx, s, &bc_out);
  31960                 }
  31961 
  31962                 for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) {
  31963                     JSVarDef *vd = &s->vars[scope_idx];
  31964                     if (vd->scope_level == scope) {
  31965                         if (scope_idx != s->arguments_arg_idx) {
  31966                             if (vd->var_kind == JS_VAR_FUNCTION_DECL ||
  31967                                 vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) {
  31968                                 /* Initialize lexical variable upon entering scope */
  31969                                 dbuf_putc(&bc_out, OP_fclosure);
  31970                                 dbuf_put_u32(&bc_out, vd->func_pool_idx);
  31971                                 dbuf_putc(&bc_out, OP_put_loc);
  31972                                 dbuf_put_u16(&bc_out, scope_idx);
  31973                             } else {
  31974                                 /* XXX: should check if variable can be used
  31975                                    before initialization */
  31976                                 dbuf_putc(&bc_out, OP_set_loc_uninitialized);
  31977                                 dbuf_put_u16(&bc_out, scope_idx);
  31978                             }
  31979                         }
  31980                         scope_idx = vd->scope_next;
  31981                     } else {
  31982                         break;
  31983                     }
  31984                 }
  31985             }
  31986             break;
  31987 
  31988         case OP_leave_scope:
  31989             {
  31990                 int scope_idx, scope = get_u16(bc_buf + pos + 1);
  31991 
  31992                 for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) {
  31993                     JSVarDef *vd = &s->vars[scope_idx];
  31994                     if (vd->scope_level == scope) {
  31995                         if (vd->is_captured) {
  31996                             dbuf_putc(&bc_out, OP_close_loc);
  31997                             dbuf_put_u16(&bc_out, scope_idx);
  31998                         }
  31999                         scope_idx = vd->scope_next;
  32000                     } else {
  32001                         break;
  32002                     }
  32003                 }
  32004             }
  32005             break;
  32006 
  32007         case OP_set_name:
  32008             {
  32009                 /* remove dummy set_name opcodes */
  32010                 JSAtom name = get_u32(bc_buf + pos + 1);
  32011                 if (name == JS_ATOM_NULL)
  32012                     break;
  32013             }
  32014             goto no_change;
  32015 
  32016         case OP_if_false:
  32017         case OP_if_true:
  32018         case OP_catch:
  32019             s->jump_size++;
  32020             goto no_change;
  32021 
  32022         case OP_dup:
  32023             if (OPTIMIZE) {
  32024                 /* Transformation: dup if_false(l1) drop, l1: if_false(l2) -> if_false(l2) */
  32025                 /* Transformation: dup if_true(l1) drop, l1: if_true(l2) -> if_true(l2) */
  32026                 if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), OP_drop, -1)) {
  32027                     int lab0, lab1, op1, pos1, line1, pos2;
  32028                     lab0 = lab1 = cc.label;
  32029                     assert(lab1 >= 0 && lab1 < s->label_count);
  32030                     op1 = cc.op;
  32031                     pos1 = cc.pos;
  32032                     line1 = cc.line_num;
  32033                     while (code_match(&cc, (pos2 = get_label_pos(s, lab1)), OP_dup, op1, OP_drop, -1)) {
  32034                         lab1 = cc.label;
  32035                     }
  32036                     if (code_match(&cc, pos2, op1, -1)) {
  32037                         s->jump_size++;
  32038                         update_label(s, lab0, -1);
  32039                         update_label(s, cc.label, +1);
  32040                         dbuf_putc(&bc_out, op1);
  32041                         dbuf_put_u32(&bc_out, cc.label);
  32042                         pos_next = pos1;
  32043                         if (line1 != -1 && line1 != line_num) {
  32044                             line_num = line1;
  32045                             s->line_number_size++;
  32046                             dbuf_putc(&bc_out, OP_line_num);
  32047                             dbuf_put_u32(&bc_out, line_num);
  32048                         }
  32049                         break;
  32050                     }
  32051                 }
  32052             }
  32053             goto no_change;
  32054 
  32055         case OP_nop:
  32056             /* remove erased code */
  32057             break;
  32058         case OP_set_class_name:
  32059             /* only used during parsing */
  32060             break;
  32061 
  32062         case OP_get_field_opt_chain: /* equivalent to OP_get_field */
  32063             {
  32064                 JSAtom name = get_u32(bc_buf + pos + 1);
  32065                 dbuf_putc(&bc_out, OP_get_field);
  32066                 dbuf_put_u32(&bc_out, name);
  32067             }
  32068             break;
  32069         case OP_get_array_el_opt_chain: /* equivalent to OP_get_array_el */
  32070             dbuf_putc(&bc_out, OP_get_array_el);
  32071             break;
  32072 
  32073         default:
  32074         no_change:
  32075             dbuf_put(&bc_out, bc_buf + pos, len);
  32076             break;
  32077         }
  32078     }
  32079 
  32080     /* set the new byte code */
  32081     dbuf_free(&s->byte_code);
  32082     s->byte_code = bc_out;
  32083     if (dbuf_error(&s->byte_code)) {
  32084         JS_ThrowOutOfMemory(ctx);
  32085         return -1;
  32086     }
  32087     return 0;
  32088  fail:
  32089     /* continue the copy to keep the atom refcounts consistent */
  32090     /* XXX: find a better solution ? */
  32091     for (; pos < bc_len; pos = pos_next) {
  32092         op = bc_buf[pos];
  32093         len = opcode_info[op].size;
  32094         pos_next = pos + len;
  32095         dbuf_put(&bc_out, bc_buf + pos, len);
  32096     }
  32097     dbuf_free(&s->byte_code);
  32098     s->byte_code = bc_out;
  32099     return -1;
  32100 }
  32101 
  32102 /* the pc2line table gives a line number for each PC value */
  32103 static void add_pc2line_info(JSFunctionDef *s, uint32_t pc, int line_num)
  32104 {
  32105     if (s->line_number_slots != NULL
  32106     &&  s->line_number_count < s->line_number_size
  32107     &&  pc >= s->line_number_last_pc
  32108     &&  line_num != s->line_number_last) {
  32109         s->line_number_slots[s->line_number_count].pc = pc;
  32110         s->line_number_slots[s->line_number_count].line_num = line_num;
  32111         s->line_number_count++;
  32112         s->line_number_last_pc = pc;
  32113         s->line_number_last = line_num;
  32114     }
  32115 }
  32116 
  32117 static void compute_pc2line_info(JSFunctionDef *s)
  32118 {
  32119     if (!(s->js_mode & JS_MODE_STRIP) && s->line_number_slots) {
  32120         int last_line_num = s->line_num;
  32121         uint32_t last_pc = 0;
  32122         int i;
  32123 
  32124         js_dbuf_init(s->ctx, &s->pc2line);
  32125         for (i = 0; i < s->line_number_count; i++) {
  32126             uint32_t pc = s->line_number_slots[i].pc;
  32127             int line_num = s->line_number_slots[i].line_num;
  32128             int diff_pc, diff_line;
  32129 
  32130             if (line_num < 0)
  32131                 continue;
  32132 
  32133             diff_pc = pc - last_pc;
  32134             diff_line = line_num - last_line_num;
  32135             if (diff_line == 0 || diff_pc < 0)
  32136                 continue;
  32137 
  32138             if (diff_line >= PC2LINE_BASE &&
  32139                 diff_line < PC2LINE_BASE + PC2LINE_RANGE &&
  32140                 diff_pc <= PC2LINE_DIFF_PC_MAX) {
  32141                 dbuf_putc(&s->pc2line, (diff_line - PC2LINE_BASE) +
  32142                           diff_pc * PC2LINE_RANGE + PC2LINE_OP_FIRST);
  32143             } else {
  32144                 /* longer encoding */
  32145                 dbuf_putc(&s->pc2line, 0);
  32146                 dbuf_put_leb128(&s->pc2line, diff_pc);
  32147                 dbuf_put_sleb128(&s->pc2line, diff_line);
  32148             }
  32149             last_pc = pc;
  32150             last_line_num = line_num;
  32151         }
  32152     }
  32153 }
  32154 
  32155 static RelocEntry *add_reloc(JSContext *ctx, LabelSlot *ls, uint32_t addr, int size)
  32156 {
  32157     RelocEntry *re;
  32158     re = js_malloc(ctx, sizeof(*re));
  32159     if (!re)
  32160         return NULL;
  32161     re->addr = addr;
  32162     re->size = size;
  32163     re->next = ls->first_reloc;
  32164     ls->first_reloc = re;
  32165     return re;
  32166 }
  32167 
  32168 static BOOL code_has_label(CodeContext *s, int pos, int label)
  32169 {
  32170     while (pos < s->bc_len) {
  32171         int op = s->bc_buf[pos];
  32172         if (op == OP_line_num) {
  32173             pos += 5;
  32174             continue;
  32175         }
  32176         if (op == OP_label) {
  32177             int lab = get_u32(s->bc_buf + pos + 1);
  32178             if (lab == label)
  32179                 return TRUE;
  32180             pos += 5;
  32181             continue;
  32182         }
  32183         if (op == OP_goto) {
  32184             int lab = get_u32(s->bc_buf + pos + 1);
  32185             if (lab == label)
  32186                 return TRUE;
  32187         }
  32188         break;
  32189     }
  32190     return FALSE;
  32191 }
  32192 
  32193 /* return the target label, following the OP_goto jumps
  32194    the first opcode at destination is stored in *pop
  32195  */
  32196 static int find_jump_target(JSFunctionDef *s, int label, int *pop, int *pline)
  32197 {
  32198     int i, pos, op;
  32199 
  32200     update_label(s, label, -1);
  32201     for (i = 0; i < 10; i++) {
  32202         assert(label >= 0 && label < s->label_count);
  32203         pos = s->label_slots[label].pos2;
  32204         for (;;) {
  32205             switch(op = s->byte_code.buf[pos]) {
  32206             case OP_line_num:
  32207                 if (pline)
  32208                     *pline = get_u32(s->byte_code.buf + pos + 1);
  32209                 /* fall thru */
  32210             case OP_label:
  32211                 pos += opcode_info[op].size;
  32212                 continue;
  32213             case OP_goto:
  32214                 label = get_u32(s->byte_code.buf + pos + 1);
  32215                 break;
  32216             case OP_drop:
  32217                 /* ignore drop opcodes if followed by OP_return_undef */
  32218                 while (s->byte_code.buf[++pos] == OP_drop)
  32219                     continue;
  32220                 if (s->byte_code.buf[pos] == OP_return_undef)
  32221                     op = OP_return_undef;
  32222                 /* fall thru */
  32223             default:
  32224                 goto done;
  32225             }
  32226             break;
  32227         }
  32228     }
  32229     /* cycle detected, could issue a warning */
  32230  done:
  32231     *pop = op;
  32232     update_label(s, label, +1);
  32233     return label;
  32234 }
  32235 
  32236 static void push_short_int(DynBuf *bc_out, int val)
  32237 {
  32238 #if SHORT_OPCODES
  32239     if (val >= -1 && val <= 7) {
  32240         dbuf_putc(bc_out, OP_push_0 + val);
  32241         return;
  32242     }
  32243     if (val == (int8_t)val) {
  32244         dbuf_putc(bc_out, OP_push_i8);
  32245         dbuf_putc(bc_out, val);
  32246         return;
  32247     }
  32248     if (val == (int16_t)val) {
  32249         dbuf_putc(bc_out, OP_push_i16);
  32250         dbuf_put_u16(bc_out, val);
  32251         return;
  32252     }
  32253 #endif
  32254     dbuf_putc(bc_out, OP_push_i32);
  32255     dbuf_put_u32(bc_out, val);
  32256 }
  32257 
  32258 static void put_short_code(DynBuf *bc_out, int op, int idx)
  32259 {
  32260 #if SHORT_OPCODES
  32261     if (idx < 4) {
  32262         switch (op) {
  32263         case OP_get_loc:
  32264             dbuf_putc(bc_out, OP_get_loc0 + idx);
  32265             return;
  32266         case OP_put_loc:
  32267             dbuf_putc(bc_out, OP_put_loc0 + idx);
  32268             return;
  32269         case OP_set_loc:
  32270             dbuf_putc(bc_out, OP_set_loc0 + idx);
  32271             return;
  32272         case OP_get_arg:
  32273             dbuf_putc(bc_out, OP_get_arg0 + idx);
  32274             return;
  32275         case OP_put_arg:
  32276             dbuf_putc(bc_out, OP_put_arg0 + idx);
  32277             return;
  32278         case OP_set_arg:
  32279             dbuf_putc(bc_out, OP_set_arg0 + idx);
  32280             return;
  32281         case OP_get_var_ref:
  32282             dbuf_putc(bc_out, OP_get_var_ref0 + idx);
  32283             return;
  32284         case OP_put_var_ref:
  32285             dbuf_putc(bc_out, OP_put_var_ref0 + idx);
  32286             return;
  32287         case OP_set_var_ref:
  32288             dbuf_putc(bc_out, OP_set_var_ref0 + idx);
  32289             return;
  32290         case OP_call:
  32291             dbuf_putc(bc_out, OP_call0 + idx);
  32292             return;
  32293         }
  32294     }
  32295     if (idx < 256) {
  32296         switch (op) {
  32297         case OP_get_loc:
  32298             dbuf_putc(bc_out, OP_get_loc8);
  32299             dbuf_putc(bc_out, idx);
  32300             return;
  32301         case OP_put_loc:
  32302             dbuf_putc(bc_out, OP_put_loc8);
  32303             dbuf_putc(bc_out, idx);
  32304             return;
  32305         case OP_set_loc:
  32306             dbuf_putc(bc_out, OP_set_loc8);
  32307             dbuf_putc(bc_out, idx);
  32308             return;
  32309         }
  32310     }
  32311 #endif
  32312     dbuf_putc(bc_out, op);
  32313     dbuf_put_u16(bc_out, idx);
  32314 }
  32315 
  32316 /* peephole optimizations and resolve goto/labels */
  32317 static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
  32318 {
  32319     int pos, pos_next, bc_len, op, op1, len, i, line_num;
  32320     const uint8_t *bc_buf;
  32321     DynBuf bc_out;
  32322     LabelSlot *label_slots, *ls;
  32323     RelocEntry *re, *re_next;
  32324     CodeContext cc;
  32325     int label;
  32326 #if SHORT_OPCODES
  32327     JumpSlot *jp;
  32328 #endif
  32329 
  32330     label_slots = s->label_slots;
  32331 
  32332     line_num = s->line_num;
  32333 
  32334     cc.bc_buf = bc_buf = s->byte_code.buf;
  32335     cc.bc_len = bc_len = s->byte_code.size;
  32336     js_dbuf_init(ctx, &bc_out);
  32337 
  32338 #if SHORT_OPCODES
  32339     if (s->jump_size) {
  32340         s->jump_slots = js_mallocz(s->ctx, sizeof(*s->jump_slots) * s->jump_size);
  32341         if (s->jump_slots == NULL)
  32342             return -1;
  32343     }
  32344 #endif
  32345     /* XXX: Should skip this phase if not generating SHORT_OPCODES */
  32346     if (s->line_number_size && !(s->js_mode & JS_MODE_STRIP)) {
  32347         s->line_number_slots = js_mallocz(s->ctx, sizeof(*s->line_number_slots) * s->line_number_size);
  32348         if (s->line_number_slots == NULL)
  32349             return -1;
  32350         s->line_number_last = s->line_num;
  32351         s->line_number_last_pc = 0;
  32352     }
  32353 
  32354     /* initialize the 'home_object' variable if needed */
  32355     if (s->home_object_var_idx >= 0) {
  32356         dbuf_putc(&bc_out, OP_special_object);
  32357         dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_HOME_OBJECT);
  32358         put_short_code(&bc_out, OP_put_loc, s->home_object_var_idx);
  32359     }
  32360     /* initialize the 'this.active_func' variable if needed */
  32361     if (s->this_active_func_var_idx >= 0) {
  32362         dbuf_putc(&bc_out, OP_special_object);
  32363         dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC);
  32364         put_short_code(&bc_out, OP_put_loc, s->this_active_func_var_idx);
  32365     }
  32366     /* initialize the 'new.target' variable if needed */
  32367     if (s->new_target_var_idx >= 0) {
  32368         dbuf_putc(&bc_out, OP_special_object);
  32369         dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_NEW_TARGET);
  32370         put_short_code(&bc_out, OP_put_loc, s->new_target_var_idx);
  32371     }
  32372     /* initialize the 'this' variable if needed. In a derived class
  32373        constructor, this is initially uninitialized. */
  32374     if (s->this_var_idx >= 0) {
  32375         if (s->is_derived_class_constructor) {
  32376             dbuf_putc(&bc_out, OP_set_loc_uninitialized);
  32377             dbuf_put_u16(&bc_out, s->this_var_idx);
  32378         } else {
  32379             dbuf_putc(&bc_out, OP_push_this);
  32380             put_short_code(&bc_out, OP_put_loc, s->this_var_idx);
  32381         }
  32382     }
  32383     /* initialize the 'arguments' variable if needed */
  32384     if (s->arguments_var_idx >= 0) {
  32385         if ((s->js_mode & JS_MODE_STRICT) || !s->has_simple_parameter_list) {
  32386             dbuf_putc(&bc_out, OP_special_object);
  32387             dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_ARGUMENTS);
  32388         } else {
  32389             dbuf_putc(&bc_out, OP_special_object);
  32390             dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS);
  32391         }
  32392         if (s->arguments_arg_idx >= 0)
  32393             put_short_code(&bc_out, OP_set_loc, s->arguments_arg_idx);
  32394         put_short_code(&bc_out, OP_put_loc, s->arguments_var_idx);
  32395     }
  32396     /* initialize a reference to the current function if needed */
  32397     if (s->func_var_idx >= 0) {
  32398         dbuf_putc(&bc_out, OP_special_object);
  32399         dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC);
  32400         put_short_code(&bc_out, OP_put_loc, s->func_var_idx);
  32401     }
  32402     /* initialize the variable environment object if needed */
  32403     if (s->var_object_idx >= 0) {
  32404         dbuf_putc(&bc_out, OP_special_object);
  32405         dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT);
  32406         put_short_code(&bc_out, OP_put_loc, s->var_object_idx);
  32407     }
  32408     if (s->arg_var_object_idx >= 0) {
  32409         dbuf_putc(&bc_out, OP_special_object);
  32410         dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT);
  32411         put_short_code(&bc_out, OP_put_loc, s->arg_var_object_idx);
  32412     }
  32413 
  32414     for (pos = 0; pos < bc_len; pos = pos_next) {
  32415         int val;
  32416         op = bc_buf[pos];
  32417         len = opcode_info[op].size;
  32418         pos_next = pos + len;
  32419         switch(op) {
  32420         case OP_line_num:
  32421             /* line number info (for debug). We put it in a separate
  32422                compressed table to reduce memory usage and get better
  32423                performance */
  32424             line_num = get_u32(bc_buf + pos + 1);
  32425             break;
  32426 
  32427         case OP_label:
  32428             {
  32429                 label = get_u32(bc_buf + pos + 1);
  32430                 assert(label >= 0 && label < s->label_count);
  32431                 ls = &label_slots[label];
  32432                 assert(ls->addr == -1);
  32433                 ls->addr = bc_out.size;
  32434                 /* resolve the relocation entries */
  32435                 for(re = ls->first_reloc; re != NULL; re = re_next) {
  32436                     int diff = ls->addr - re->addr;
  32437                     re_next = re->next;
  32438                     switch (re->size) {
  32439                     case 4:
  32440                         put_u32(bc_out.buf + re->addr, diff);
  32441                         break;
  32442                     case 2:
  32443                         assert(diff == (int16_t)diff);
  32444                         put_u16(bc_out.buf + re->addr, diff);
  32445                         break;
  32446                     case 1:
  32447                         assert(diff == (int8_t)diff);
  32448                         put_u8(bc_out.buf + re->addr, diff);
  32449                         break;
  32450                     }
  32451                     js_free(ctx, re);
  32452                 }
  32453                 ls->first_reloc = NULL;
  32454             }
  32455             break;
  32456 
  32457         case OP_call:
  32458         case OP_call_method:
  32459             {
  32460                 /* detect and transform tail calls */
  32461                 int argc;
  32462                 argc = get_u16(bc_buf + pos + 1);
  32463                 if (code_match(&cc, pos_next, OP_return, -1)) {
  32464                     if (cc.line_num >= 0) line_num = cc.line_num;
  32465                     add_pc2line_info(s, bc_out.size, line_num);
  32466                     put_short_code(&bc_out, op + 1, argc);
  32467                     pos_next = skip_dead_code(s, bc_buf, bc_len, cc.pos, &line_num);
  32468                     break;
  32469                 }
  32470                 add_pc2line_info(s, bc_out.size, line_num);
  32471                 put_short_code(&bc_out, op, argc);
  32472                 break;
  32473             }
  32474             goto no_change;
  32475 
  32476         case OP_return:
  32477         case OP_return_undef:
  32478         case OP_return_async:
  32479         case OP_throw:
  32480         case OP_throw_error:
  32481             pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);
  32482             goto no_change;
  32483 
  32484         case OP_goto:
  32485             label = get_u32(bc_buf + pos + 1);
  32486         has_goto:
  32487             if (OPTIMIZE) {
  32488                 int line1 = -1;
  32489                 /* Use custom matcher because multiple labels can follow */
  32490                 label = find_jump_target(s, label, &op1, &line1);
  32491                 if (code_has_label(&cc, pos_next, label)) {
  32492                     /* jump to next instruction: remove jump */
  32493                     update_label(s, label, -1);
  32494                     break;
  32495                 }
  32496                 if (op1 == OP_return || op1 == OP_return_undef || op1 == OP_throw) {
  32497                     /* jump to return/throw: remove jump, append return/throw */
  32498                     /* updating the line number obfuscates assembly listing */
  32499                     //if (line1 >= 0) line_num = line1;
  32500                     update_label(s, label, -1);
  32501                     add_pc2line_info(s, bc_out.size, line_num);
  32502                     dbuf_putc(&bc_out, op1);
  32503                     pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);
  32504                     break;
  32505                 }
  32506                 /* XXX: should duplicate single instructions followed by goto or return */
  32507                 /* For example, can match one of these followed by return:
  32508                    push_i32 / push_const / push_atom_value / get_var /
  32509                    undefined / null / push_false / push_true / get_ref_value /
  32510                    get_loc / get_arg / get_var_ref
  32511                  */
  32512             }
  32513             goto has_label;
  32514 
  32515         case OP_gosub:
  32516             label = get_u32(bc_buf + pos + 1);
  32517             if (0 && OPTIMIZE) {
  32518                 label = find_jump_target(s, label, &op1, NULL);
  32519                 if (op1 == OP_ret) {
  32520                     update_label(s, label, -1);
  32521                     /* empty finally clause: remove gosub */
  32522                     break;
  32523                 }
  32524             }
  32525             goto has_label;
  32526 
  32527         case OP_catch:
  32528             label = get_u32(bc_buf + pos + 1);
  32529             goto has_label;
  32530 
  32531         case OP_if_true:
  32532         case OP_if_false:
  32533             label = get_u32(bc_buf + pos + 1);
  32534             if (OPTIMIZE) {
  32535                 label = find_jump_target(s, label, &op1, NULL);
  32536                 /* transform if_false/if_true(l1) label(l1) -> drop label(l1) */
  32537                 if (code_has_label(&cc, pos_next, label)) {
  32538                     update_label(s, label, -1);
  32539                     dbuf_putc(&bc_out, OP_drop);
  32540                     break;
  32541                 }
  32542                 /* transform if_false(l1) goto(l2) label(l1) -> if_false(l2) label(l1) */
  32543                 if (code_match(&cc, pos_next, OP_goto, -1)) {
  32544                     int pos1 = cc.pos;
  32545                     int line1 = cc.line_num;
  32546                     if (code_has_label(&cc, pos1, label)) {
  32547                         if (line1 >= 0) line_num = line1;
  32548                         pos_next = pos1;
  32549                         update_label(s, label, -1);
  32550                         label = cc.label;
  32551                         op ^= OP_if_true ^ OP_if_false;
  32552                     }
  32553                 }
  32554             }
  32555         has_label:
  32556             add_pc2line_info(s, bc_out.size, line_num);
  32557             if (op == OP_goto) {
  32558                 pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);
  32559             }
  32560             assert(label >= 0 && label < s->label_count);
  32561             ls = &label_slots[label];
  32562 #if SHORT_OPCODES
  32563             jp = &s->jump_slots[s->jump_count++];
  32564             jp->op = op;
  32565             jp->size = 4;
  32566             jp->pos = bc_out.size + 1;
  32567             jp->label = label;
  32568 
  32569             if (ls->addr == -1) {
  32570                 int diff = ls->pos2 - pos - 1;
  32571                 if (diff < 128 && (op == OP_if_false || op == OP_if_true || op == OP_goto)) {
  32572                     jp->size = 1;
  32573                     jp->op = OP_if_false8 + (op - OP_if_false);
  32574                     dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false));
  32575                     dbuf_putc(&bc_out, 0);
  32576                     if (!add_reloc(ctx, ls, bc_out.size - 1, 1))
  32577                         goto fail;
  32578                     break;
  32579                 }
  32580                 if (diff < 32768 && op == OP_goto) {
  32581                     jp->size = 2;
  32582                     jp->op = OP_goto16;
  32583                     dbuf_putc(&bc_out, OP_goto16);
  32584                     dbuf_put_u16(&bc_out, 0);
  32585                     if (!add_reloc(ctx, ls, bc_out.size - 2, 2))
  32586                         goto fail;
  32587                     break;
  32588                 }
  32589             } else {
  32590                 int diff = ls->addr - bc_out.size - 1;
  32591                 if (diff == (int8_t)diff && (op == OP_if_false || op == OP_if_true || op == OP_goto)) {
  32592                     jp->size = 1;
  32593                     jp->op = OP_if_false8 + (op - OP_if_false);
  32594                     dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false));
  32595                     dbuf_putc(&bc_out, diff);
  32596                     break;
  32597                 }
  32598                 if (diff == (int16_t)diff && op == OP_goto) {
  32599                     jp->size = 2;
  32600                     jp->op = OP_goto16;
  32601                     dbuf_putc(&bc_out, OP_goto16);
  32602                     dbuf_put_u16(&bc_out, diff);
  32603                     break;
  32604                 }
  32605             }
  32606 #endif
  32607             dbuf_putc(&bc_out, op);
  32608             dbuf_put_u32(&bc_out, ls->addr - bc_out.size);
  32609             if (ls->addr == -1) {
  32610                 /* unresolved yet: create a new relocation entry */
  32611                 if (!add_reloc(ctx, ls, bc_out.size - 4, 4))
  32612                     goto fail;
  32613             }
  32614             break;
  32615         case OP_with_get_var:
  32616         case OP_with_put_var:
  32617         case OP_with_delete_var:
  32618         case OP_with_make_ref:
  32619         case OP_with_get_ref:
  32620         case OP_with_get_ref_undef:
  32621             {
  32622                 JSAtom atom;
  32623                 int is_with;
  32624 
  32625                 atom = get_u32(bc_buf + pos + 1);
  32626                 label = get_u32(bc_buf + pos + 5);
  32627                 is_with = bc_buf[pos + 9];
  32628                 if (OPTIMIZE) {
  32629                     label = find_jump_target(s, label, &op1, NULL);
  32630                 }
  32631                 assert(label >= 0 && label < s->label_count);
  32632                 ls = &label_slots[label];
  32633                 add_pc2line_info(s, bc_out.size, line_num);
  32634 #if SHORT_OPCODES
  32635                 jp = &s->jump_slots[s->jump_count++];
  32636                 jp->op = op;
  32637                 jp->size = 4;
  32638                 jp->pos = bc_out.size + 5;
  32639                 jp->label = label;
  32640 #endif
  32641                 dbuf_putc(&bc_out, op);
  32642                 dbuf_put_u32(&bc_out, atom);
  32643                 dbuf_put_u32(&bc_out, ls->addr - bc_out.size);
  32644                 if (ls->addr == -1) {
  32645                     /* unresolved yet: create a new relocation entry */
  32646                     if (!add_reloc(ctx, ls, bc_out.size - 4, 4))
  32647                         goto fail;
  32648                 }
  32649                 dbuf_putc(&bc_out, is_with);
  32650             }
  32651             break;
  32652 
  32653         case OP_drop:
  32654             if (OPTIMIZE) {
  32655                 /* remove useless drops before return */
  32656                 if (code_match(&cc, pos_next, OP_return_undef, -1)) {
  32657                     if (cc.line_num >= 0) line_num = cc.line_num;
  32658                     break;
  32659                 }
  32660             }
  32661             goto no_change;
  32662 
  32663         case OP_null:
  32664 #if SHORT_OPCODES
  32665             if (OPTIMIZE) {
  32666                 /* transform null strict_eq into is_null */
  32667                 if (code_match(&cc, pos_next, OP_strict_eq, -1)) {
  32668                     if (cc.line_num >= 0) line_num = cc.line_num;
  32669                     add_pc2line_info(s, bc_out.size, line_num);
  32670                     dbuf_putc(&bc_out, OP_is_null);
  32671                     pos_next = cc.pos;
  32672                     break;
  32673                 }
  32674                 /* transform null strict_neq if_false/if_true -> is_null if_true/if_false */
  32675                 if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) {
  32676                     if (cc.line_num >= 0) line_num = cc.line_num;
  32677                     add_pc2line_info(s, bc_out.size, line_num);
  32678                     dbuf_putc(&bc_out, OP_is_null);
  32679                     pos_next = cc.pos;
  32680                     label = cc.label;
  32681                     op = cc.op ^ OP_if_false ^ OP_if_true;
  32682                     goto has_label;
  32683                 }
  32684             }
  32685 #endif
  32686             /* fall thru */
  32687         case OP_push_false:
  32688         case OP_push_true:
  32689             if (OPTIMIZE) {
  32690                 val = (op == OP_push_true);
  32691                 if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {
  32692                 has_constant_test:
  32693                     if (cc.line_num >= 0) line_num = cc.line_num;
  32694                     if (val == cc.op - OP_if_false) {
  32695                         /* transform null if_false(l1) -> goto l1 */
  32696                         /* transform false if_false(l1) -> goto l1 */
  32697                         /* transform true if_true(l1) -> goto l1 */
  32698                         pos_next = cc.pos;
  32699                         op = OP_goto;
  32700                         label = cc.label;
  32701                         goto has_goto;
  32702                     } else {
  32703                         /* transform null if_true(l1) -> nop */
  32704                         /* transform false if_true(l1) -> nop */
  32705                         /* transform true if_false(l1) -> nop */
  32706                         pos_next = cc.pos;
  32707                         update_label(s, cc.label, -1);
  32708                         break;
  32709                     }
  32710                 }
  32711             }
  32712             goto no_change;
  32713 
  32714         case OP_push_i32:
  32715             if (OPTIMIZE) {
  32716                 /* transform i32(val) neg -> i32(-val) */
  32717                 val = get_i32(bc_buf + pos + 1);
  32718                 if ((val != INT32_MIN && val != 0)
  32719                 &&  code_match(&cc, pos_next, OP_neg, -1)) {
  32720                     if (cc.line_num >= 0) line_num = cc.line_num;
  32721                     if (code_match(&cc, cc.pos, OP_drop, -1)) {
  32722                         if (cc.line_num >= 0) line_num = cc.line_num;
  32723                     } else {
  32724                         add_pc2line_info(s, bc_out.size, line_num);
  32725                         push_short_int(&bc_out, -val);
  32726                     }
  32727                     pos_next = cc.pos;
  32728                     break;
  32729                 }
  32730                 /* remove push/drop pairs generated by the parser */
  32731                 if (code_match(&cc, pos_next, OP_drop, -1)) {
  32732                     if (cc.line_num >= 0) line_num = cc.line_num;
  32733                     pos_next = cc.pos;
  32734                     break;
  32735                 }
  32736                 /* Optimize constant tests: `if (0)`, `if (1)`, `if (!0)`... */
  32737                 if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {
  32738                     val = (val != 0);
  32739                     goto has_constant_test;
  32740                 }
  32741                 add_pc2line_info(s, bc_out.size, line_num);
  32742                 push_short_int(&bc_out, val);
  32743                 break;
  32744             }
  32745             goto no_change;
  32746 
  32747 #if SHORT_OPCODES
  32748         case OP_push_const:
  32749         case OP_fclosure:
  32750             if (OPTIMIZE) {
  32751                 int idx = get_u32(bc_buf + pos + 1);
  32752                 if (idx < 256) {
  32753                     add_pc2line_info(s, bc_out.size, line_num);
  32754                     dbuf_putc(&bc_out, OP_push_const8 + op - OP_push_const);
  32755                     dbuf_putc(&bc_out, idx);
  32756                     break;
  32757                 }
  32758             }
  32759             goto no_change;
  32760 
  32761         case OP_get_field:
  32762             if (OPTIMIZE) {
  32763                 JSAtom atom = get_u32(bc_buf + pos + 1);
  32764                 if (atom == JS_ATOM_length) {
  32765                     JS_FreeAtom(ctx, atom);
  32766                     add_pc2line_info(s, bc_out.size, line_num);
  32767                     dbuf_putc(&bc_out, OP_get_length);
  32768                     break;
  32769                 }
  32770             }
  32771             goto no_change;
  32772 #endif
  32773         case OP_push_atom_value:
  32774             if (OPTIMIZE) {
  32775                 JSAtom atom = get_u32(bc_buf + pos + 1);
  32776                 /* remove push/drop pairs generated by the parser */
  32777                 if (code_match(&cc, pos_next, OP_drop, -1)) {
  32778                     JS_FreeAtom(ctx, atom);
  32779                     if (cc.line_num >= 0) line_num = cc.line_num;
  32780                     pos_next = cc.pos;
  32781                     break;
  32782                 }
  32783 #if SHORT_OPCODES
  32784                 if (atom == JS_ATOM_empty_string) {
  32785                     JS_FreeAtom(ctx, atom);
  32786                     add_pc2line_info(s, bc_out.size, line_num);
  32787                     dbuf_putc(&bc_out, OP_push_empty_string);
  32788                     break;
  32789                 }
  32790 #endif
  32791             }
  32792             goto no_change;
  32793 
  32794         case OP_to_propkey:
  32795         case OP_to_propkey2:
  32796             if (OPTIMIZE) {
  32797                 /* remove redundant to_propkey/to_propkey2 opcodes when storing simple data */
  32798                 if (code_match(&cc, pos_next, M3(OP_get_loc, OP_get_arg, OP_get_var_ref), -1, OP_put_array_el, -1)
  32799                 ||  code_match(&cc, pos_next, M3(OP_push_i32, OP_push_const, OP_push_atom_value), OP_put_array_el, -1)
  32800                 ||  code_match(&cc, pos_next, M4(OP_undefined, OP_null, OP_push_true, OP_push_false), OP_put_array_el, -1)) {
  32801                     break;
  32802                 }
  32803             }
  32804             goto no_change;
  32805 
  32806         case OP_undefined:
  32807             if (OPTIMIZE) {
  32808                 /* remove push/drop pairs generated by the parser */
  32809                 if (code_match(&cc, pos_next, OP_drop, -1)) {
  32810                     if (cc.line_num >= 0) line_num = cc.line_num;
  32811                     pos_next = cc.pos;
  32812                     break;
  32813                 }
  32814                 /* transform undefined return -> return_undefined */
  32815                 if (code_match(&cc, pos_next, OP_return, -1)) {
  32816                     if (cc.line_num >= 0) line_num = cc.line_num;
  32817                     add_pc2line_info(s, bc_out.size, line_num);
  32818                     dbuf_putc(&bc_out, OP_return_undef);
  32819                     pos_next = cc.pos;
  32820                     break;
  32821                 }
  32822                 /* transform undefined if_true(l1)/if_false(l1) -> nop/goto(l1) */
  32823                 if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {
  32824                     val = 0;
  32825                     goto has_constant_test;
  32826                 }
  32827 #if SHORT_OPCODES
  32828                 /* transform undefined strict_eq -> is_undefined */
  32829                 if (code_match(&cc, pos_next, OP_strict_eq, -1)) {
  32830                     if (cc.line_num >= 0) line_num = cc.line_num;
  32831                     add_pc2line_info(s, bc_out.size, line_num);
  32832                     dbuf_putc(&bc_out, OP_is_undefined);
  32833                     pos_next = cc.pos;
  32834                     break;
  32835                 }
  32836                 /* transform undefined strict_neq if_false/if_true -> is_undefined if_true/if_false */
  32837                 if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) {
  32838                     if (cc.line_num >= 0) line_num = cc.line_num;
  32839                     add_pc2line_info(s, bc_out.size, line_num);
  32840                     dbuf_putc(&bc_out, OP_is_undefined);
  32841                     pos_next = cc.pos;
  32842                     label = cc.label;
  32843                     op = cc.op ^ OP_if_false ^ OP_if_true;
  32844                     goto has_label;
  32845                 }
  32846 #endif
  32847             }
  32848             goto no_change;
  32849 
  32850         case OP_insert2:
  32851             if (OPTIMIZE) {
  32852                 /* Transformation:
  32853                    insert2 put_field(a) drop -> put_field(a)
  32854                    insert2 put_var_strict(a) drop -> put_var_strict(a)
  32855                 */
  32856                 if (code_match(&cc, pos_next, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) {
  32857                     if (cc.line_num >= 0) line_num = cc.line_num;
  32858                     add_pc2line_info(s, bc_out.size, line_num);
  32859                     dbuf_putc(&bc_out, cc.op);
  32860                     dbuf_put_u32(&bc_out, cc.atom);
  32861                     pos_next = cc.pos;
  32862                     break;
  32863                 }
  32864             }
  32865             goto no_change;
  32866 
  32867         case OP_dup:
  32868             if (OPTIMIZE) {
  32869                 /* Transformation: dup put_x(n) drop -> put_x(n) */
  32870                 int op1, line2 = -1;
  32871                 /* Transformation: dup put_x(n) -> set_x(n) */
  32872                 if (code_match(&cc, pos_next, M3(OP_put_loc, OP_put_arg, OP_put_var_ref), -1, -1)) {
  32873                     if (cc.line_num >= 0) line_num = cc.line_num;
  32874                     op1 = cc.op + 1;  /* put_x -> set_x */
  32875                     pos_next = cc.pos;
  32876                     if (code_match(&cc, cc.pos, OP_drop, -1)) {
  32877                         if (cc.line_num >= 0) line_num = cc.line_num;
  32878                         op1 -= 1; /* set_x drop -> put_x */
  32879                         pos_next = cc.pos;
  32880                         if (code_match(&cc, cc.pos, op1 - 1, cc.idx, -1)) {
  32881                             line2 = cc.line_num; /* delay line number update */
  32882                             op1 += 1;   /* put_x(n) get_x(n) -> set_x(n) */
  32883                             pos_next = cc.pos;
  32884                         }
  32885                     }
  32886                     add_pc2line_info(s, bc_out.size, line_num);
  32887                     put_short_code(&bc_out, op1, cc.idx);
  32888                     if (line2 >= 0) line_num = line2;
  32889                     break;
  32890                 }
  32891             }
  32892             goto no_change;
  32893 
  32894         case OP_get_loc:
  32895             if (OPTIMIZE) {
  32896                 /* transformation:
  32897                    get_loc(n) post_dec put_loc(n) drop -> dec_loc(n)
  32898                    get_loc(n) post_inc put_loc(n) drop -> inc_loc(n)
  32899                    get_loc(n) dec dup put_loc(n) drop -> dec_loc(n)
  32900                    get_loc(n) inc dup put_loc(n) drop -> inc_loc(n)
  32901                  */
  32902                 int idx;
  32903                 idx = get_u16(bc_buf + pos + 1);
  32904                 if (idx >= 256)
  32905                     goto no_change;
  32906                 if (code_match(&cc, pos_next, M2(OP_post_dec, OP_post_inc), OP_put_loc, idx, OP_drop, -1) ||
  32907                     code_match(&cc, pos_next, M2(OP_dec, OP_inc), OP_dup, OP_put_loc, idx, OP_drop, -1)) {
  32908                     if (cc.line_num >= 0) line_num = cc.line_num;
  32909                     add_pc2line_info(s, bc_out.size, line_num);
  32910                     dbuf_putc(&bc_out, (cc.op == OP_inc || cc.op == OP_post_inc) ? OP_inc_loc : OP_dec_loc);
  32911                     dbuf_putc(&bc_out, idx);
  32912                     pos_next = cc.pos;
  32913                     break;
  32914                 }
  32915                 /* transformation:
  32916                    get_loc(n) push_atom_value(x) add dup put_loc(n) drop -> push_atom_value(x) add_loc(n)
  32917                  */
  32918                 if (code_match(&cc, pos_next, OP_push_atom_value, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) {
  32919                     if (cc.line_num >= 0) line_num = cc.line_num;
  32920                     add_pc2line_info(s, bc_out.size, line_num);
  32921 #if SHORT_OPCODES
  32922                     if (cc.atom == JS_ATOM_empty_string) {
  32923                         JS_FreeAtom(ctx, cc.atom);
  32924                         dbuf_putc(&bc_out, OP_push_empty_string);
  32925                     } else
  32926 #endif
  32927                     {
  32928                         dbuf_putc(&bc_out, OP_push_atom_value);
  32929                         dbuf_put_u32(&bc_out, cc.atom);
  32930                     }
  32931                     dbuf_putc(&bc_out, OP_add_loc);
  32932                     dbuf_putc(&bc_out, idx);
  32933                     pos_next = cc.pos;
  32934                     break;
  32935                 }
  32936                 /* transformation:
  32937                    get_loc(n) push_i32(x) add dup put_loc(n) drop -> push_i32(x) add_loc(n)
  32938                  */
  32939                 if (code_match(&cc, pos_next, OP_push_i32, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) {
  32940                     if (cc.line_num >= 0) line_num = cc.line_num;
  32941                     add_pc2line_info(s, bc_out.size, line_num);
  32942                     push_short_int(&bc_out, cc.label);
  32943                     dbuf_putc(&bc_out, OP_add_loc);
  32944                     dbuf_putc(&bc_out, idx);
  32945                     pos_next = cc.pos;
  32946                     break;
  32947                 }
  32948                 /* transformation: XXX: also do these:
  32949                    get_loc(n) get_loc(x) add dup put_loc(n) drop -> get_loc(x) add_loc(n)
  32950                    get_loc(n) get_arg(x) add dup put_loc(n) drop -> get_arg(x) add_loc(n)
  32951                    get_loc(n) get_var_ref(x) add dup put_loc(n) drop -> get_var_ref(x) add_loc(n)
  32952                  */
  32953                 if (code_match(&cc, pos_next, M3(OP_get_loc, OP_get_arg, OP_get_var_ref), -1, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) {
  32954                     if (cc.line_num >= 0) line_num = cc.line_num;
  32955                     add_pc2line_info(s, bc_out.size, line_num);
  32956                     put_short_code(&bc_out, cc.op, cc.idx);
  32957                     dbuf_putc(&bc_out, OP_add_loc);
  32958                     dbuf_putc(&bc_out, idx);
  32959                     pos_next = cc.pos;
  32960                     break;
  32961                 }
  32962                 add_pc2line_info(s, bc_out.size, line_num);
  32963                 put_short_code(&bc_out, op, idx);
  32964                 break;
  32965             }
  32966             goto no_change;
  32967 #if SHORT_OPCODES
  32968         case OP_get_arg:
  32969         case OP_get_var_ref:
  32970             if (OPTIMIZE) {
  32971                 int idx;
  32972                 idx = get_u16(bc_buf + pos + 1);
  32973                 add_pc2line_info(s, bc_out.size, line_num);
  32974                 put_short_code(&bc_out, op, idx);
  32975                 break;
  32976             }
  32977             goto no_change;
  32978 #endif
  32979         case OP_put_loc:
  32980         case OP_put_arg:
  32981         case OP_put_var_ref:
  32982             if (OPTIMIZE) {
  32983                 /* transformation: put_x(n) get_x(n) -> set_x(n) */
  32984                 int idx;
  32985                 idx = get_u16(bc_buf + pos + 1);
  32986                 if (code_match(&cc, pos_next, op - 1, idx, -1)) {
  32987                     if (cc.line_num >= 0) line_num = cc.line_num;
  32988                     add_pc2line_info(s, bc_out.size, line_num);
  32989                     put_short_code(&bc_out, op + 1, idx);
  32990                     pos_next = cc.pos;
  32991                     break;
  32992                 }
  32993                 add_pc2line_info(s, bc_out.size, line_num);
  32994                 put_short_code(&bc_out, op, idx);
  32995                 break;
  32996             }
  32997             goto no_change;
  32998 
  32999         case OP_post_inc:
  33000         case OP_post_dec:
  33001             if (OPTIMIZE) {
  33002                 /* transformation:
  33003                    post_inc put_x drop -> inc put_x
  33004                    post_inc perm3 put_field drop -> inc put_field
  33005                    post_inc perm3 put_var_strict drop -> inc put_var_strict
  33006                    post_inc perm4 put_array_el drop -> inc put_array_el
  33007                  */
  33008                 int op1, idx;
  33009                 if (code_match(&cc, pos_next, M3(OP_put_loc, OP_put_arg, OP_put_var_ref), -1, OP_drop, -1)) {
  33010                     if (cc.line_num >= 0) line_num = cc.line_num;
  33011                     op1 = cc.op;
  33012                     idx = cc.idx;
  33013                     pos_next = cc.pos;
  33014                     if (code_match(&cc, cc.pos, op1 - 1, idx, -1)) {
  33015                         if (cc.line_num >= 0) line_num = cc.line_num;
  33016                         op1 += 1;   /* put_x(n) get_x(n) -> set_x(n) */
  33017                         pos_next = cc.pos;
  33018                     }
  33019                     add_pc2line_info(s, bc_out.size, line_num);
  33020                     dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));
  33021                     put_short_code(&bc_out, op1, idx);
  33022                     break;
  33023                 }
  33024                 if (code_match(&cc, pos_next, OP_perm3, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) {
  33025                     if (cc.line_num >= 0) line_num = cc.line_num;
  33026                     add_pc2line_info(s, bc_out.size, line_num);
  33027                     dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));
  33028                     dbuf_putc(&bc_out, cc.op);
  33029                     dbuf_put_u32(&bc_out, cc.atom);
  33030                     pos_next = cc.pos;
  33031                     break;
  33032                 }
  33033                 if (code_match(&cc, pos_next, OP_perm4, OP_put_array_el, OP_drop, -1)) {
  33034                     if (cc.line_num >= 0) line_num = cc.line_num;
  33035                     add_pc2line_info(s, bc_out.size, line_num);
  33036                     dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));
  33037                     dbuf_putc(&bc_out, OP_put_array_el);
  33038                     pos_next = cc.pos;
  33039                     break;
  33040                 }
  33041             }
  33042             goto no_change;
  33043 
  33044 #if SHORT_OPCODES
  33045         case OP_typeof:
  33046             if (OPTIMIZE) {
  33047                 /* simplify typeof tests */
  33048                 if (code_match(&cc, pos_next, OP_push_atom_value, M4(OP_strict_eq, OP_strict_neq, OP_eq, OP_neq), -1)) {
  33049                     if (cc.line_num >= 0) line_num = cc.line_num;
  33050                     int op1 = (cc.op == OP_strict_eq || cc.op == OP_eq) ? OP_strict_eq : OP_strict_neq;
  33051                     int op2 = -1;
  33052                     switch (cc.atom) {
  33053                     case JS_ATOM_undefined:
  33054                         op2 = OP_typeof_is_undefined;
  33055                         break;
  33056                     case JS_ATOM_function:
  33057                         op2 = OP_typeof_is_function;
  33058                         break;
  33059                     }
  33060                     if (op2 >= 0) {
  33061                         /* transform typeof(s) == "<type>" into is_<type> */
  33062                         if (op1 == OP_strict_eq) {
  33063                             add_pc2line_info(s, bc_out.size, line_num);
  33064                             dbuf_putc(&bc_out, op2);
  33065                             JS_FreeAtom(ctx, cc.atom);
  33066                             pos_next = cc.pos;
  33067                             break;
  33068                         }
  33069                         if (op1 == OP_strict_neq && code_match(&cc, cc.pos, OP_if_false, -1)) {
  33070                             /* transform typeof(s) != "<type>" if_false into is_<type> if_true */
  33071                             if (cc.line_num >= 0) line_num = cc.line_num;
  33072                             add_pc2line_info(s, bc_out.size, line_num);
  33073                             dbuf_putc(&bc_out, op2);
  33074                             JS_FreeAtom(ctx, cc.atom);
  33075                             pos_next = cc.pos;
  33076                             label = cc.label;
  33077                             op = OP_if_true;
  33078                             goto has_label;
  33079                         }
  33080                     }
  33081                 }
  33082             }
  33083             goto no_change;
  33084 #endif
  33085 
  33086         default:
  33087         no_change:
  33088             add_pc2line_info(s, bc_out.size, line_num);
  33089             dbuf_put(&bc_out, bc_buf + pos, len);
  33090             break;
  33091         }
  33092     }
  33093 
  33094     /* check that there were no missing labels */
  33095     for(i = 0; i < s->label_count; i++) {
  33096         assert(label_slots[i].first_reloc == NULL);
  33097     }
  33098 #if SHORT_OPCODES
  33099     if (OPTIMIZE) {
  33100         /* more jump optimizations */
  33101         int patch_offsets = 0;
  33102         for (i = 0, jp = s->jump_slots; i < s->jump_count; i++, jp++) {
  33103             LabelSlot *ls;
  33104             JumpSlot *jp1;
  33105             int j, pos, diff, delta;
  33106 
  33107             delta = 3;
  33108             switch (op = jp->op) {
  33109             case OP_goto16:
  33110                 delta = 1;
  33111                 /* fall thru */
  33112             case OP_if_false:
  33113             case OP_if_true:
  33114             case OP_goto:
  33115                 pos = jp->pos;
  33116                 diff = s->label_slots[jp->label].addr - pos;
  33117                 if (diff >= -128 && diff <= 127 + delta) {
  33118                     //put_u8(bc_out.buf + pos, diff);
  33119                     jp->size = 1;
  33120                     if (op == OP_goto16) {
  33121                         bc_out.buf[pos - 1] = jp->op = OP_goto8;
  33122                     } else {
  33123                         bc_out.buf[pos - 1] = jp->op = OP_if_false8 + (op - OP_if_false);
  33124                     }
  33125                     goto shrink;
  33126                 } else
  33127                 if (diff == (int16_t)diff && op == OP_goto) {
  33128                     //put_u16(bc_out.buf + pos, diff);
  33129                     jp->size = 2;
  33130                     delta = 2;
  33131                     bc_out.buf[pos - 1] = jp->op = OP_goto16;
  33132                 shrink:
  33133                     /* XXX: should reduce complexity, using 2 finger copy scheme */
  33134                     memmove(bc_out.buf + pos + jp->size, bc_out.buf + pos + jp->size + delta,
  33135                             bc_out.size - pos - jp->size - delta);
  33136                     bc_out.size -= delta;
  33137                     patch_offsets++;
  33138                     for (j = 0, ls = s->label_slots; j < s->label_count; j++, ls++) {
  33139                         if (ls->addr > pos)
  33140                             ls->addr -= delta;
  33141                     }
  33142                     for (j = i + 1, jp1 = jp + 1; j < s->jump_count; j++, jp1++) {
  33143                         if (jp1->pos > pos)
  33144                             jp1->pos -= delta;
  33145                     }
  33146                     for (j = 0; j < s->line_number_count; j++) {
  33147                         if (s->line_number_slots[j].pc > pos)
  33148                             s->line_number_slots[j].pc -= delta;
  33149                     }
  33150                     continue;
  33151                 }
  33152                 break;
  33153             }
  33154         }
  33155         if (patch_offsets) {
  33156             JumpSlot *jp1;
  33157             int j;
  33158             for (j = 0, jp1 = s->jump_slots; j < s->jump_count; j++, jp1++) {
  33159                 int diff1 = s->label_slots[jp1->label].addr - jp1->pos;
  33160                 switch (jp1->size) {
  33161                 case 1:
  33162                     put_u8(bc_out.buf + jp1->pos, diff1);
  33163                     break;
  33164                 case 2:
  33165                     put_u16(bc_out.buf + jp1->pos, diff1);
  33166                     break;
  33167                 case 4:
  33168                     put_u32(bc_out.buf + jp1->pos, diff1);
  33169                     break;
  33170                 }
  33171             }
  33172         }
  33173     }
  33174     js_free(ctx, s->jump_slots);
  33175     s->jump_slots = NULL;
  33176 #endif
  33177     js_free(ctx, s->label_slots);
  33178     s->label_slots = NULL;
  33179     /* XXX: should delay until copying to runtime bytecode function */
  33180     compute_pc2line_info(s);
  33181     js_free(ctx, s->line_number_slots);
  33182     s->line_number_slots = NULL;
  33183     /* set the new byte code */
  33184     dbuf_free(&s->byte_code);
  33185     s->byte_code = bc_out;
  33186     s->use_short_opcodes = TRUE;
  33187     if (dbuf_error(&s->byte_code)) {
  33188         JS_ThrowOutOfMemory(ctx);
  33189         return -1;
  33190     }
  33191     return 0;
  33192  fail:
  33193     /* XXX: not safe */
  33194     dbuf_free(&bc_out);
  33195     return -1;
  33196 }
  33197 
  33198 /* compute the maximum stack size needed by the function */
  33199 
  33200 typedef struct StackSizeState {
  33201     int bc_len;
  33202     int stack_len_max;
  33203     uint16_t *stack_level_tab;
  33204     int32_t *catch_pos_tab;
  33205     int *pc_stack;
  33206     int pc_stack_len;
  33207     int pc_stack_size;
  33208 } StackSizeState;
  33209 
  33210 /* 'op' is only used for error indication */
  33211 static __exception int ss_check(JSContext *ctx, StackSizeState *s,
  33212                                 int pos, int op, int stack_len, int catch_pos)
  33213 {
  33214     if ((unsigned)pos >= s->bc_len) {
  33215         JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);
  33216         return -1;
  33217     }
  33218     if (stack_len > s->stack_len_max) {
  33219         s->stack_len_max = stack_len;
  33220         if (s->stack_len_max > JS_STACK_SIZE_MAX) {
  33221             JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos);
  33222             return -1;
  33223         }
  33224     }
  33225     if (s->stack_level_tab[pos] != 0xffff) {
  33226         /* already explored: check that the stack size is consistent */
  33227         if (s->stack_level_tab[pos] != stack_len) {
  33228             JS_ThrowInternalError(ctx, "inconsistent stack size: %d %d (pc=%d)",
  33229                                   s->stack_level_tab[pos], stack_len, pos);
  33230             return -1;
  33231         } else if (s->catch_pos_tab[pos] != catch_pos) {
  33232             JS_ThrowInternalError(ctx, "inconsistent catch position: %d %d (pc=%d)",
  33233                                   s->catch_pos_tab[pos], catch_pos, pos);
  33234             return -1;
  33235         } else {
  33236             return 0;
  33237         }
  33238     }
  33239 
  33240     /* mark as explored and store the stack size */
  33241     s->stack_level_tab[pos] = stack_len;
  33242     s->catch_pos_tab[pos] = catch_pos;
  33243 
  33244     /* queue the new PC to explore */
  33245     if (js_resize_array(ctx, (void **)&s->pc_stack, sizeof(s->pc_stack[0]),
  33246                         &s->pc_stack_size, s->pc_stack_len + 1))
  33247         return -1;
  33248     s->pc_stack[s->pc_stack_len++] = pos;
  33249     return 0;
  33250 }
  33251 
  33252 static __exception int compute_stack_size(JSContext *ctx,
  33253                                           JSFunctionDef *fd,
  33254                                           int *pstack_size)
  33255 {
  33256     StackSizeState s_s, *s = &s_s;
  33257     int i, diff, n_pop, pos_next, stack_len, pos, op, catch_pos, catch_level;
  33258     const JSOpCode *oi;
  33259     const uint8_t *bc_buf;
  33260 
  33261     bc_buf = fd->byte_code.buf;
  33262     s->bc_len = fd->byte_code.size;
  33263     /* bc_len > 0 */
  33264     s->stack_level_tab = js_malloc(ctx, sizeof(s->stack_level_tab[0]) *
  33265                                    s->bc_len);
  33266     if (!s->stack_level_tab)
  33267         return -1;
  33268     for(i = 0; i < s->bc_len; i++)
  33269         s->stack_level_tab[i] = 0xffff;
  33270     s->pc_stack = NULL;
  33271     s->catch_pos_tab = js_malloc(ctx, sizeof(s->catch_pos_tab[0]) *
  33272                                    s->bc_len);
  33273     if (!s->catch_pos_tab)
  33274         goto fail;
  33275 
  33276     s->stack_len_max = 0;
  33277     s->pc_stack_len = 0;
  33278     s->pc_stack_size = 0;
  33279 
  33280     /* breadth-first graph exploration */
  33281     if (ss_check(ctx, s, 0, OP_invalid, 0, -1))
  33282         goto fail;
  33283 
  33284     while (s->pc_stack_len > 0) {
  33285         pos = s->pc_stack[--s->pc_stack_len];
  33286         stack_len = s->stack_level_tab[pos];
  33287         catch_pos = s->catch_pos_tab[pos];
  33288         op = bc_buf[pos];
  33289         if (op == 0 || op >= OP_COUNT) {
  33290             JS_ThrowInternalError(ctx, "invalid opcode (op=%d, pc=%d)", op, pos);
  33291             goto fail;
  33292         }
  33293         oi = &short_opcode_info(op);
  33294 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 64)
  33295         printf("%5d: %10s %5d %5d\n", pos, oi->name, stack_len, catch_pos);
  33296 #endif
  33297         pos_next = pos + oi->size;
  33298         if (pos_next > s->bc_len) {
  33299             JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);
  33300             goto fail;
  33301         }
  33302         n_pop = oi->n_pop;
  33303         /* call pops a variable number of arguments */
  33304         if (oi->fmt == OP_FMT_npop || oi->fmt == OP_FMT_npop_u16) {
  33305             n_pop += get_u16(bc_buf + pos + 1);
  33306         } else {
  33307 #if SHORT_OPCODES
  33308             if (oi->fmt == OP_FMT_npopx) {
  33309                 n_pop += op - OP_call0;
  33310             }
  33311 #endif
  33312         }
  33313 
  33314         if (stack_len < n_pop) {
  33315             JS_ThrowInternalError(ctx, "stack underflow (op=%d, pc=%d)", op, pos);
  33316             goto fail;
  33317         }
  33318         stack_len += oi->n_push - n_pop;
  33319         if (stack_len > s->stack_len_max) {
  33320             s->stack_len_max = stack_len;
  33321             if (s->stack_len_max > JS_STACK_SIZE_MAX) {
  33322                 JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos);
  33323                 goto fail;
  33324             }
  33325         }
  33326         switch(op) {
  33327         case OP_tail_call:
  33328         case OP_tail_call_method:
  33329         case OP_return:
  33330         case OP_return_undef:
  33331         case OP_return_async:
  33332         case OP_throw:
  33333         case OP_throw_error:
  33334         case OP_ret:
  33335             goto done_insn;
  33336         case OP_goto:
  33337             diff = get_u32(bc_buf + pos + 1);
  33338             pos_next = pos + 1 + diff;
  33339             break;
  33340 #if SHORT_OPCODES
  33341         case OP_goto16:
  33342             diff = (int16_t)get_u16(bc_buf + pos + 1);
  33343             pos_next = pos + 1 + diff;
  33344             break;
  33345         case OP_goto8:
  33346             diff = (int8_t)bc_buf[pos + 1];
  33347             pos_next = pos + 1 + diff;
  33348             break;
  33349         case OP_if_true8:
  33350         case OP_if_false8:
  33351             diff = (int8_t)bc_buf[pos + 1];
  33352             if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos))
  33353                 goto fail;
  33354             break;
  33355 #endif
  33356         case OP_if_true:
  33357         case OP_if_false:
  33358             diff = get_u32(bc_buf + pos + 1);
  33359             if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos))
  33360                 goto fail;
  33361             break;
  33362         case OP_gosub:
  33363             diff = get_u32(bc_buf + pos + 1);
  33364             if (ss_check(ctx, s, pos + 1 + diff, op, stack_len + 1, catch_pos))
  33365                 goto fail;
  33366             break;
  33367         case OP_with_get_var:
  33368         case OP_with_delete_var:
  33369             diff = get_u32(bc_buf + pos + 5);
  33370             if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 1, catch_pos))
  33371                 goto fail;
  33372             break;
  33373         case OP_with_make_ref:
  33374         case OP_with_get_ref:
  33375         case OP_with_get_ref_undef:
  33376             diff = get_u32(bc_buf + pos + 5);
  33377             if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 2, catch_pos))
  33378                 goto fail;
  33379             break;
  33380         case OP_with_put_var:
  33381             diff = get_u32(bc_buf + pos + 5);
  33382             if (ss_check(ctx, s, pos + 5 + diff, op, stack_len - 1, catch_pos))
  33383                 goto fail;
  33384             break;
  33385         case OP_catch:
  33386             diff = get_u32(bc_buf + pos + 1);
  33387             if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos))
  33388                 goto fail;
  33389             catch_pos = pos;
  33390             break;
  33391         case OP_for_of_start:
  33392         case OP_for_await_of_start:
  33393             catch_pos = pos;
  33394             break;
  33395             /* we assume the catch offset entry is only removed with
  33396                some op codes */
  33397         case OP_drop:
  33398             catch_level = stack_len;
  33399             goto check_catch;
  33400         case OP_nip:
  33401             catch_level = stack_len - 1;
  33402             goto check_catch;
  33403         case OP_nip1:
  33404             catch_level = stack_len - 1;
  33405             goto check_catch;
  33406         case OP_iterator_close:
  33407             catch_level = stack_len + 2;
  33408         check_catch:
  33409             /* Note: for for_of_start/for_await_of_start we consider
  33410                the catch offset is on the first stack entry instead of
  33411                the thirst */
  33412             if (catch_pos >= 0) {
  33413                 int level;
  33414                 level = s->stack_level_tab[catch_pos];
  33415                 if (bc_buf[catch_pos] != OP_catch)
  33416                     level++; /* for_of_start, for_wait_of_start */
  33417                 /* catch_level = stack_level before op_catch is executed ? */
  33418                 if (catch_level == level) {
  33419                     catch_pos = s->catch_pos_tab[catch_pos];
  33420                 }
  33421             }
  33422             break;
  33423         case OP_nip_catch:
  33424             if (catch_pos < 0) {
  33425                 JS_ThrowInternalError(ctx, "nip_catch: no catch op (pc=%d)", pos);
  33426                 goto fail;
  33427             }
  33428             stack_len = s->stack_level_tab[catch_pos];
  33429             if (bc_buf[catch_pos] != OP_catch)
  33430                 stack_len++; /* for_of_start, for_wait_of_start */
  33431             stack_len++; /* no stack overflow is possible by construction */
  33432             catch_pos = s->catch_pos_tab[catch_pos];
  33433             break;
  33434         default:
  33435             break;
  33436         }
  33437         if (ss_check(ctx, s, pos_next, op, stack_len, catch_pos))
  33438             goto fail;
  33439     done_insn: ;
  33440     }
  33441     js_free(ctx, s->pc_stack);
  33442     js_free(ctx, s->catch_pos_tab);
  33443     js_free(ctx, s->stack_level_tab);
  33444     *pstack_size = s->stack_len_max;
  33445     return 0;
  33446  fail:
  33447     js_free(ctx, s->pc_stack);
  33448     js_free(ctx, s->catch_pos_tab);
  33449     js_free(ctx, s->stack_level_tab);
  33450     *pstack_size = 0;
  33451     return -1;
  33452 }
  33453 
  33454 static int add_module_variables(JSContext *ctx, JSFunctionDef *fd)
  33455 {
  33456     int i, idx;
  33457     JSModuleDef *m = fd->module;
  33458     JSExportEntry *me;
  33459     JSGlobalVar *hf;
  33460 
  33461     /* The imported global variables were added as closure variables
  33462        in js_parse_import(). We add here the module global
  33463        variables. */
  33464 
  33465     for(i = 0; i < fd->global_var_count; i++) {
  33466         hf = &fd->global_vars[i];
  33467         if (add_closure_var(ctx, fd, TRUE, FALSE, i, hf->var_name, hf->is_const,
  33468                             hf->is_lexical, FALSE) < 0)
  33469             return -1;
  33470     }
  33471 
  33472     /* resolve the variable names of the local exports */
  33473     for(i = 0; i < m->export_entries_count; i++) {
  33474         me = &m->export_entries[i];
  33475         if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
  33476             idx = find_closure_var(ctx, fd, me->local_name);
  33477             if (idx < 0) {
  33478                 JS_ThrowSyntaxErrorAtom(ctx, "exported variable '%s' does not exist",
  33479                                         me->local_name);
  33480                 return -1;
  33481             }
  33482             me->u.local.var_idx = idx;
  33483         }
  33484     }
  33485     return 0;
  33486 }
  33487 
  33488 /* create a function object from a function definition. The function
  33489    definition is freed. All the child functions are also created. It
  33490    must be done this way to resolve all the variables. */
  33491 static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
  33492 {
  33493     JSValue func_obj;
  33494     JSFunctionBytecode *b;
  33495     struct list_head *el, *el1;
  33496     int stack_size, scope, idx;
  33497     int function_size, byte_code_offset, cpool_offset;
  33498     int closure_var_offset, vardefs_offset;
  33499 
  33500     /* recompute scope linkage */
  33501     for (scope = 0; scope < fd->scope_count; scope++) {
  33502         fd->scopes[scope].first = -1;
  33503     }
  33504     if (fd->has_parameter_expressions) {
  33505         /* special end of variable list marker for the argument scope */
  33506         fd->scopes[ARG_SCOPE_INDEX].first = ARG_SCOPE_END;
  33507     }
  33508     for (idx = 0; idx < fd->var_count; idx++) {
  33509         JSVarDef *vd = &fd->vars[idx];
  33510         vd->scope_next = fd->scopes[vd->scope_level].first;
  33511         fd->scopes[vd->scope_level].first = idx;
  33512     }
  33513     for (scope = 2; scope < fd->scope_count; scope++) {
  33514         JSVarScope *sd = &fd->scopes[scope];
  33515         if (sd->first < 0)
  33516             sd->first = fd->scopes[sd->parent].first;
  33517     }
  33518     for (idx = 0; idx < fd->var_count; idx++) {
  33519         JSVarDef *vd = &fd->vars[idx];
  33520         if (vd->scope_next < 0 && vd->scope_level > 1) {
  33521             scope = fd->scopes[vd->scope_level].parent;
  33522             vd->scope_next = fd->scopes[scope].first;
  33523         }
  33524     }
  33525 
  33526     /* if the function contains an eval call, the closure variables
  33527        are used to compile the eval and they must be ordered by scope,
  33528        so it is necessary to create the closure variables before any
  33529        other variable lookup is done. */
  33530     if (fd->has_eval_call)
  33531         add_eval_variables(ctx, fd);
  33532 
  33533     /* add the module global variables in the closure */
  33534     if (fd->module) {
  33535         if (add_module_variables(ctx, fd))
  33536             goto fail;
  33537     }
  33538 
  33539     /* first create all the child functions */
  33540     list_for_each_safe(el, el1, &fd->child_list) {
  33541         JSFunctionDef *fd1;
  33542         int cpool_idx;
  33543 
  33544         fd1 = list_entry(el, JSFunctionDef, link);
  33545         cpool_idx = fd1->parent_cpool_idx;
  33546         func_obj = js_create_function(ctx, fd1);
  33547         if (JS_IsException(func_obj))
  33548             goto fail;
  33549         /* save it in the constant pool */
  33550         assert(cpool_idx >= 0);
  33551         fd->cpool[cpool_idx] = func_obj;
  33552     }
  33553 
  33554 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 4)
  33555     if (!(fd->js_mode & JS_MODE_STRIP)) {
  33556         printf("pass 1\n");
  33557         dump_byte_code(ctx, 1, fd->byte_code.buf, fd->byte_code.size,
  33558                        fd->args, fd->arg_count, fd->vars, fd->var_count,
  33559                        fd->closure_var, fd->closure_var_count,
  33560                        fd->cpool, fd->cpool_count, fd->source, fd->line_num,
  33561                        fd->label_slots, NULL);
  33562         printf("\n");
  33563     }
  33564 #endif
  33565 
  33566     if (resolve_variables(ctx, fd))
  33567         goto fail;
  33568 
  33569 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 2)
  33570     if (!(fd->js_mode & JS_MODE_STRIP)) {
  33571         printf("pass 2\n");
  33572         dump_byte_code(ctx, 2, fd->byte_code.buf, fd->byte_code.size,
  33573                        fd->args, fd->arg_count, fd->vars, fd->var_count,
  33574                        fd->closure_var, fd->closure_var_count,
  33575                        fd->cpool, fd->cpool_count, fd->source, fd->line_num,
  33576                        fd->label_slots, NULL);
  33577         printf("\n");
  33578     }
  33579 #endif
  33580 
  33581     if (resolve_labels(ctx, fd))
  33582         goto fail;
  33583 
  33584     if (compute_stack_size(ctx, fd, &stack_size) < 0)
  33585         goto fail;
  33586 
  33587     if (fd->js_mode & JS_MODE_STRIP) {
  33588         function_size = offsetof(JSFunctionBytecode, debug);
  33589     } else {
  33590         function_size = sizeof(*b);
  33591     }
  33592     cpool_offset = function_size;
  33593     function_size += fd->cpool_count * sizeof(*fd->cpool);
  33594     vardefs_offset = function_size;
  33595     if (!(fd->js_mode & JS_MODE_STRIP) || fd->has_eval_call) {
  33596         function_size += (fd->arg_count + fd->var_count) * sizeof(*b->vardefs);
  33597     }
  33598     closure_var_offset = function_size;
  33599     function_size += fd->closure_var_count * sizeof(*fd->closure_var);
  33600     byte_code_offset = function_size;
  33601     function_size += fd->byte_code.size;
  33602 
  33603     b = js_mallocz(ctx, function_size);
  33604     if (!b)
  33605         goto fail;
  33606     b->header.ref_count = 1;
  33607 
  33608     b->byte_code_buf = (void *)((uint8_t*)b + byte_code_offset);
  33609     b->byte_code_len = fd->byte_code.size;
  33610     memcpy(b->byte_code_buf, fd->byte_code.buf, fd->byte_code.size);
  33611     js_free(ctx, fd->byte_code.buf);
  33612     fd->byte_code.buf = NULL;
  33613 
  33614     b->func_name = fd->func_name;
  33615     if (fd->arg_count + fd->var_count > 0) {
  33616         if ((fd->js_mode & JS_MODE_STRIP) && !fd->has_eval_call) {
  33617             /* Strip variable definitions not needed at runtime */
  33618             int i;
  33619             for(i = 0; i < fd->var_count; i++) {
  33620                 JS_FreeAtom(ctx, fd->vars[i].var_name);
  33621             }
  33622             for(i = 0; i < fd->arg_count; i++) {
  33623                 JS_FreeAtom(ctx, fd->args[i].var_name);
  33624             }
  33625             for(i = 0; i < fd->closure_var_count; i++) {
  33626                 JS_FreeAtom(ctx, fd->closure_var[i].var_name);
  33627                 fd->closure_var[i].var_name = JS_ATOM_NULL;
  33628             }
  33629         } else {
  33630             b->vardefs = (void *)((uint8_t*)b + vardefs_offset);
  33631             memcpy_no_ub(b->vardefs, fd->args, fd->arg_count * sizeof(fd->args[0]));
  33632             memcpy_no_ub(b->vardefs + fd->arg_count, fd->vars, fd->var_count * sizeof(fd->vars[0]));
  33633         }
  33634         b->var_count = fd->var_count;
  33635         b->arg_count = fd->arg_count;
  33636         b->defined_arg_count = fd->defined_arg_count;
  33637         js_free(ctx, fd->args);
  33638         js_free(ctx, fd->vars);
  33639     }
  33640     b->cpool_count = fd->cpool_count;
  33641     if (b->cpool_count) {
  33642         b->cpool = (void *)((uint8_t*)b + cpool_offset);
  33643         memcpy(b->cpool, fd->cpool, b->cpool_count * sizeof(*b->cpool));
  33644     }
  33645     js_free(ctx, fd->cpool);
  33646     fd->cpool = NULL;
  33647 
  33648     b->stack_size = stack_size;
  33649 
  33650     b->perf_trampoline = NULL;
  33651 
  33652     if (fd->js_mode & JS_MODE_STRIP) {
  33653         JS_FreeAtom(ctx, fd->filename);
  33654         dbuf_free(&fd->pc2line);    // probably useless
  33655     } else {
  33656         /* XXX: source and pc2line info should be packed at the end of the
  33657            JSFunctionBytecode structure, avoiding allocation overhead
  33658          */
  33659         b->has_debug = 1;
  33660         b->debug.filename = fd->filename;
  33661         b->debug.line_num = fd->line_num;
  33662 
  33663         //DynBuf pc2line;
  33664         //compute_pc2line_info(fd, &pc2line);
  33665         //js_free(ctx, fd->line_number_slots)
  33666         b->debug.pc2line_buf = js_realloc(ctx, fd->pc2line.buf, fd->pc2line.size);
  33667         if (!b->debug.pc2line_buf)
  33668             b->debug.pc2line_buf = fd->pc2line.buf;
  33669         b->debug.pc2line_len = fd->pc2line.size;
  33670         b->debug.source = fd->source;
  33671         b->debug.source_len = fd->source_len;
  33672     }
  33673     if (fd->scopes != fd->def_scope_array)
  33674         js_free(ctx, fd->scopes);
  33675 
  33676     b->closure_var_count = fd->closure_var_count;
  33677     if (b->closure_var_count) {
  33678         b->closure_var = (void *)((uint8_t*)b + closure_var_offset);
  33679         memcpy(b->closure_var, fd->closure_var, b->closure_var_count * sizeof(*b->closure_var));
  33680     }
  33681     js_free(ctx, fd->closure_var);
  33682     fd->closure_var = NULL;
  33683 
  33684     b->has_prototype = fd->has_prototype;
  33685     b->has_simple_parameter_list = fd->has_simple_parameter_list;
  33686     b->js_mode = fd->js_mode;
  33687     b->is_derived_class_constructor = fd->is_derived_class_constructor;
  33688     b->func_kind = fd->func_kind;
  33689     b->need_home_object = (fd->home_object_var_idx >= 0 ||
  33690                            fd->need_home_object);
  33691     b->new_target_allowed = fd->new_target_allowed;
  33692     b->super_call_allowed = fd->super_call_allowed;
  33693     b->super_allowed = fd->super_allowed;
  33694     b->arguments_allowed = fd->arguments_allowed;
  33695     b->backtrace_barrier = fd->backtrace_barrier;
  33696     b->is_direct_or_indirect_eval = (fd->eval_type == JS_EVAL_TYPE_DIRECT ||
  33697                                      fd->eval_type == JS_EVAL_TYPE_INDIRECT);
  33698     b->realm = JS_DupContext(ctx);
  33699 
  33700     add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
  33701 
  33702 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 1)
  33703     if (!(fd->js_mode & JS_MODE_STRIP)) {
  33704         js_dump_function_bytecode(ctx, b);
  33705     }
  33706 #endif
  33707 
  33708     if (fd->parent) {
  33709         /* remove from parent list */
  33710         list_del(&fd->link);
  33711     }
  33712 
  33713     js_free(ctx, fd);
  33714     return JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b);
  33715  fail:
  33716     js_free_function_def(ctx, fd);
  33717     return JS_EXCEPTION;
  33718 }
  33719 
  33720 static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b)
  33721 {
  33722     int i;
  33723 
  33724 #if 0
  33725     {
  33726         char buf[ATOM_GET_STR_BUF_SIZE];
  33727         printf("freeing %s\n",
  33728                JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));
  33729     }
  33730 #endif
  33731     free_bytecode_atoms(rt, b->byte_code_buf, b->byte_code_len, TRUE);
  33732 
  33733     if (b->vardefs) {
  33734         for(i = 0; i < b->arg_count + b->var_count; i++) {
  33735             JS_FreeAtomRT(rt, b->vardefs[i].var_name);
  33736         }
  33737     }
  33738     for(i = 0; i < b->cpool_count; i++)
  33739         JS_FreeValueRT(rt, b->cpool[i]);
  33740 
  33741     for(i = 0; i < b->closure_var_count; i++) {
  33742         JSClosureVar *cv = &b->closure_var[i];
  33743         JS_FreeAtomRT(rt, cv->var_name);
  33744     }
  33745     if (b->realm)
  33746         JS_FreeContext(b->realm);
  33747 
  33748     JS_FreeAtomRT(rt, b->func_name);
  33749     if (b->has_debug) {
  33750         JS_FreeAtomRT(rt, b->debug.filename);
  33751         js_free_rt(rt, b->debug.pc2line_buf);
  33752         js_free_rt(rt, b->debug.source);
  33753     }
  33754 
  33755     remove_gc_object(&b->header);
  33756     if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && b->header.ref_count != 0) {
  33757         list_add_tail(&b->header.link, &rt->gc_zero_ref_count_list);
  33758     } else {
  33759         js_free_rt(rt, b);
  33760     }
  33761 }
  33762 
  33763 static __exception int js_parse_directives(JSParseState *s)
  33764 {
  33765     char str[20];
  33766     JSParsePos pos;
  33767     BOOL has_semi;
  33768 
  33769     if (s->token.val != TOK_STRING)
  33770         return 0;
  33771 
  33772     js_parse_get_pos(s, &pos);
  33773 
  33774     while(s->token.val == TOK_STRING) {
  33775         /* Copy actual source string representation */
  33776         snprintf(str, sizeof str, "%.*s",
  33777                  (int)(s->buf_ptr - s->token.ptr - 2), s->token.ptr + 1);
  33778 
  33779         if (next_token(s))
  33780             return -1;
  33781 
  33782         has_semi = FALSE;
  33783         switch (s->token.val) {
  33784         case ';':
  33785             if (next_token(s))
  33786                 return -1;
  33787             has_semi = TRUE;
  33788             break;
  33789         case '}':
  33790         case TOK_EOF:
  33791             has_semi = TRUE;
  33792             break;
  33793         case TOK_NUMBER:
  33794         case TOK_STRING:
  33795         case TOK_TEMPLATE:
  33796         case TOK_IDENT:
  33797         case TOK_REGEXP:
  33798         case TOK_DEC:
  33799         case TOK_INC:
  33800         case TOK_NULL:
  33801         case TOK_FALSE:
  33802         case TOK_TRUE:
  33803         case TOK_IF:
  33804         case TOK_RETURN:
  33805         case TOK_VAR:
  33806         case TOK_THIS:
  33807         case TOK_DELETE:
  33808         case TOK_TYPEOF:
  33809         case TOK_NEW:
  33810         case TOK_DO:
  33811         case TOK_WHILE:
  33812         case TOK_FOR:
  33813         case TOK_SWITCH:
  33814         case TOK_THROW:
  33815         case TOK_TRY:
  33816         case TOK_FUNCTION:
  33817         case TOK_DEBUGGER:
  33818         case TOK_WITH:
  33819         case TOK_CLASS:
  33820         case TOK_CONST:
  33821         case TOK_ENUM:
  33822         case TOK_EXPORT:
  33823         case TOK_IMPORT:
  33824         case TOK_SUPER:
  33825         case TOK_INTERFACE:
  33826         case TOK_LET:
  33827         case TOK_PACKAGE:
  33828         case TOK_PRIVATE:
  33829         case TOK_PROTECTED:
  33830         case TOK_PUBLIC:
  33831         case TOK_STATIC:
  33832             /* automatic insertion of ';' */
  33833             if (s->got_lf)
  33834                 has_semi = TRUE;
  33835             break;
  33836         default:
  33837             break;
  33838         }
  33839         if (!has_semi)
  33840             break;
  33841         if (!strcmp(str, "use strict")) {
  33842             s->cur_func->has_use_strict = TRUE;
  33843             s->cur_func->js_mode |= JS_MODE_STRICT;
  33844         }
  33845 #if !defined(DUMP_BYTECODE) || !(DUMP_BYTECODE & 8)
  33846         else if (!strcmp(str, "use strip")) {
  33847             s->cur_func->js_mode |= JS_MODE_STRIP;
  33848         }
  33849 #endif
  33850 #ifdef CONFIG_BIGNUM
  33851         else if (s->ctx->bignum_ext && !strcmp(str, "use math")) {
  33852             s->cur_func->js_mode |= JS_MODE_MATH;
  33853         }
  33854 #endif
  33855     }
  33856     return js_parse_seek_token(s, &pos);
  33857 }
  33858 
  33859 static int js_parse_function_check_names(JSParseState *s, JSFunctionDef *fd,
  33860                                          JSAtom func_name)
  33861 {
  33862     JSAtom name;
  33863     int i, idx;
  33864 
  33865     if (fd->js_mode & JS_MODE_STRICT) {
  33866         if (!fd->has_simple_parameter_list && fd->has_use_strict) {
  33867             return js_parse_error(s, "\"use strict\" not allowed in function with default or destructuring parameter");
  33868         }
  33869         if (func_name == JS_ATOM_eval || func_name == JS_ATOM_arguments) {
  33870             return js_parse_error(s, "invalid function name in strict code");
  33871         }
  33872         for (idx = 0; idx < fd->arg_count; idx++) {
  33873             name = fd->args[idx].var_name;
  33874 
  33875             if (name == JS_ATOM_eval || name == JS_ATOM_arguments) {
  33876                 return js_parse_error(s, "invalid argument name in strict code");
  33877             }
  33878         }
  33879     }
  33880     /* check async_generator case */
  33881     if ((fd->js_mode & JS_MODE_STRICT)
  33882     ||  !fd->has_simple_parameter_list
  33883     ||  (fd->func_type == JS_PARSE_FUNC_METHOD && fd->func_kind == JS_FUNC_ASYNC)
  33884     ||  fd->func_type == JS_PARSE_FUNC_ARROW
  33885     ||  fd->func_type == JS_PARSE_FUNC_METHOD) {
  33886         for (idx = 0; idx < fd->arg_count; idx++) {
  33887             name = fd->args[idx].var_name;
  33888             if (name != JS_ATOM_NULL) {
  33889                 for (i = 0; i < idx; i++) {
  33890                     if (fd->args[i].var_name == name)
  33891                         goto duplicate;
  33892                 }
  33893                 /* Check if argument name duplicates a destructuring parameter */
  33894                 /* XXX: should have a flag for such variables */
  33895                 for (i = 0; i < fd->var_count; i++) {
  33896                     if (fd->vars[i].var_name == name &&
  33897                         fd->vars[i].scope_level == 0)
  33898                         goto duplicate;
  33899                 }
  33900             }
  33901         }
  33902     }
  33903     return 0;
  33904 
  33905 duplicate:
  33906     return js_parse_error(s, "duplicate argument names not allowed in this context");
  33907 }
  33908 
  33909 /* create a function to initialize class fields */
  33910 static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s)
  33911 {
  33912     JSFunctionDef *fd;
  33913 
  33914     fd = js_new_function_def(s->ctx, s->cur_func, FALSE, FALSE,
  33915                              s->filename, 0);
  33916     if (!fd)
  33917         return NULL;
  33918     fd->func_name = JS_ATOM_NULL;
  33919     fd->has_prototype = FALSE;
  33920     fd->has_home_object = TRUE;
  33921 
  33922     fd->has_arguments_binding = FALSE;
  33923     fd->has_this_binding = TRUE;
  33924     fd->is_derived_class_constructor = FALSE;
  33925     fd->new_target_allowed = TRUE;
  33926     fd->super_call_allowed = FALSE;
  33927     fd->super_allowed = fd->has_home_object;
  33928     fd->arguments_allowed = FALSE;
  33929 
  33930     fd->func_kind = JS_FUNC_NORMAL;
  33931     fd->func_type = JS_PARSE_FUNC_METHOD;
  33932     return fd;
  33933 }
  33934 
  33935 /* func_name must be JS_ATOM_NULL for JS_PARSE_FUNC_STATEMENT and
  33936    JS_PARSE_FUNC_EXPR, JS_PARSE_FUNC_ARROW and JS_PARSE_FUNC_VAR */
  33937 static __exception int js_parse_function_decl2(JSParseState *s,
  33938                                                JSParseFunctionEnum func_type,
  33939                                                JSFunctionKindEnum func_kind,
  33940                                                JSAtom func_name,
  33941                                                const uint8_t *ptr,
  33942                                                int function_line_num,
  33943                                                JSParseExportEnum export_flag,
  33944                                                JSFunctionDef **pfd)
  33945 {
  33946     JSContext *ctx = s->ctx;
  33947     JSFunctionDef *fd = s->cur_func;
  33948     BOOL is_expr;
  33949     int func_idx, lexical_func_idx = -1;
  33950     BOOL has_opt_arg;
  33951     BOOL create_func_var = FALSE;
  33952 
  33953     is_expr = (func_type != JS_PARSE_FUNC_STATEMENT &&
  33954                func_type != JS_PARSE_FUNC_VAR);
  33955 
  33956     if (func_type == JS_PARSE_FUNC_STATEMENT ||
  33957         func_type == JS_PARSE_FUNC_VAR ||
  33958         func_type == JS_PARSE_FUNC_EXPR) {
  33959         if (func_kind == JS_FUNC_NORMAL &&
  33960             token_is_pseudo_keyword(s, JS_ATOM_async) &&
  33961             peek_token(s, TRUE) != '\n') {
  33962             if (next_token(s))
  33963                 return -1;
  33964             func_kind = JS_FUNC_ASYNC;
  33965         }
  33966         if (next_token(s))
  33967             return -1;
  33968         if (s->token.val == '*') {
  33969             if (next_token(s))
  33970                 return -1;
  33971             func_kind |= JS_FUNC_GENERATOR;
  33972         }
  33973 
  33974         if (s->token.val == TOK_IDENT) {
  33975             if (s->token.u.ident.is_reserved ||
  33976                 (s->token.u.ident.atom == JS_ATOM_yield &&
  33977                  func_type == JS_PARSE_FUNC_EXPR &&
  33978                  (func_kind & JS_FUNC_GENERATOR)) ||
  33979                 (s->token.u.ident.atom == JS_ATOM_await &&
  33980                  ((func_type == JS_PARSE_FUNC_EXPR &&
  33981                    (func_kind & JS_FUNC_ASYNC)) ||
  33982                   func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT))) {
  33983                 return js_parse_error_reserved_identifier(s);
  33984             }
  33985         }
  33986         if (s->token.val == TOK_IDENT ||
  33987             (((s->token.val == TOK_YIELD && !(fd->js_mode & JS_MODE_STRICT)) ||
  33988              (s->token.val == TOK_AWAIT && !s->is_module)) &&
  33989              func_type == JS_PARSE_FUNC_EXPR)) {
  33990             func_name = JS_DupAtom(ctx, s->token.u.ident.atom);
  33991             if (next_token(s)) {
  33992                 JS_FreeAtom(ctx, func_name);
  33993                 return -1;
  33994             }
  33995         } else {
  33996             if (func_type != JS_PARSE_FUNC_EXPR &&
  33997                 export_flag != JS_PARSE_EXPORT_DEFAULT) {
  33998                 return js_parse_error(s, "function name expected");
  33999             }
  34000         }
  34001     } else if (func_type != JS_PARSE_FUNC_ARROW) {
  34002         func_name = JS_DupAtom(ctx, func_name);
  34003     }
  34004 
  34005     if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_MODULE &&
  34006         (func_type == JS_PARSE_FUNC_STATEMENT || func_type == JS_PARSE_FUNC_VAR)) {
  34007         JSGlobalVar *hf;
  34008         hf = find_global_var(fd, func_name);
  34009         /* XXX: should check scope chain */
  34010         if (hf && hf->scope_level == fd->scope_level) {
  34011             js_parse_error(s, "invalid redefinition of global identifier in module code");
  34012             JS_FreeAtom(ctx, func_name);
  34013             return -1;
  34014         }
  34015     }
  34016 
  34017     if (func_type == JS_PARSE_FUNC_VAR) {
  34018         if (!(fd->js_mode & JS_MODE_STRICT)
  34019         && func_kind == JS_FUNC_NORMAL
  34020         &&  find_lexical_decl(ctx, fd, func_name, fd->scope_first, FALSE) < 0
  34021         &&  !((func_idx = find_var(ctx, fd, func_name)) >= 0 && (func_idx & ARGUMENT_VAR_OFFSET))
  34022         &&  !(func_name == JS_ATOM_arguments && fd->has_arguments_binding)) {
  34023             create_func_var = TRUE;
  34024         }
  34025         /* Create the lexical name here so that the function closure
  34026            contains it */
  34027         if (fd->is_eval &&
  34028             (fd->eval_type == JS_EVAL_TYPE_GLOBAL ||
  34029              fd->eval_type == JS_EVAL_TYPE_MODULE) &&
  34030             fd->scope_level == fd->body_scope) {
  34031             /* avoid creating a lexical variable in the global
  34032                scope. XXX: check annex B */
  34033             JSGlobalVar *hf;
  34034             hf = find_global_var(fd, func_name);
  34035             /* XXX: should check scope chain */
  34036             if (hf && hf->scope_level == fd->scope_level) {
  34037                 js_parse_error(s, "invalid redefinition of global identifier");
  34038                 JS_FreeAtom(ctx, func_name);
  34039                 return -1;
  34040             }
  34041         } else {
  34042             /* Always create a lexical name, fail if at the same scope as
  34043                existing name */
  34044             /* Lexical variable will be initialized upon entering scope */
  34045             lexical_func_idx = define_var(s, fd, func_name,
  34046                                           func_kind != JS_FUNC_NORMAL ?
  34047                                           JS_VAR_DEF_NEW_FUNCTION_DECL :
  34048                                           JS_VAR_DEF_FUNCTION_DECL);
  34049             if (lexical_func_idx < 0) {
  34050                 JS_FreeAtom(ctx, func_name);
  34051                 return -1;
  34052             }
  34053         }
  34054     }
  34055 
  34056     fd = js_new_function_def(ctx, fd, FALSE, is_expr,
  34057                              s->filename, function_line_num);
  34058     if (!fd) {
  34059         JS_FreeAtom(ctx, func_name);
  34060         return -1;
  34061     }
  34062     if (pfd)
  34063         *pfd = fd;
  34064     s->cur_func = fd;
  34065     fd->func_name = func_name;
  34066     /* XXX: test !fd->is_generator is always false */
  34067     fd->has_prototype = (func_type == JS_PARSE_FUNC_STATEMENT ||
  34068                          func_type == JS_PARSE_FUNC_VAR ||
  34069                          func_type == JS_PARSE_FUNC_EXPR) &&
  34070                         func_kind == JS_FUNC_NORMAL;
  34071     fd->has_home_object = (func_type == JS_PARSE_FUNC_METHOD ||
  34072                            func_type == JS_PARSE_FUNC_GETTER ||
  34073                            func_type == JS_PARSE_FUNC_SETTER ||
  34074                            func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR ||
  34075                            func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR);
  34076     fd->has_arguments_binding = (func_type != JS_PARSE_FUNC_ARROW &&
  34077                                  func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT);
  34078     fd->has_this_binding = fd->has_arguments_binding;
  34079     fd->is_derived_class_constructor = (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR);
  34080     if (func_type == JS_PARSE_FUNC_ARROW) {
  34081         fd->new_target_allowed = fd->parent->new_target_allowed;
  34082         fd->super_call_allowed = fd->parent->super_call_allowed;
  34083         fd->super_allowed = fd->parent->super_allowed;
  34084         fd->arguments_allowed = fd->parent->arguments_allowed;
  34085     } else if (func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT) {
  34086         fd->new_target_allowed = TRUE; // although new.target === undefined
  34087         fd->super_call_allowed = FALSE;
  34088         fd->super_allowed = TRUE;
  34089         fd->arguments_allowed = FALSE;
  34090     } else {
  34091         fd->new_target_allowed = TRUE;
  34092         fd->super_call_allowed = fd->is_derived_class_constructor;
  34093         fd->super_allowed = fd->has_home_object;
  34094         fd->arguments_allowed = TRUE;
  34095     }
  34096 
  34097     /* fd->in_function_body == FALSE prevents yield/await during the parsing
  34098        of the arguments in generator/async functions. They are parsed as
  34099        regular identifiers for other function kinds. */
  34100     fd->func_kind = func_kind;
  34101     fd->func_type = func_type;
  34102 
  34103     if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR ||
  34104         func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) {
  34105         /* error if not invoked as a constructor */
  34106         emit_op(s, OP_check_ctor);
  34107     }
  34108 
  34109     if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) {
  34110         emit_class_field_init(s);
  34111     }
  34112 
  34113     /* parse arguments */
  34114     fd->has_simple_parameter_list = TRUE;
  34115     fd->has_parameter_expressions = FALSE;
  34116     has_opt_arg = FALSE;
  34117     if (func_type == JS_PARSE_FUNC_ARROW && s->token.val == TOK_IDENT) {
  34118         JSAtom name;
  34119         if (s->token.u.ident.is_reserved) {
  34120             js_parse_error_reserved_identifier(s);
  34121             goto fail;
  34122         }
  34123         name = s->token.u.ident.atom;
  34124         if (add_arg(ctx, fd, name) < 0)
  34125             goto fail;
  34126         fd->defined_arg_count = 1;
  34127     } else if (func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT) {
  34128         if (s->token.val == '(') {
  34129             int skip_bits;
  34130             /* if there is an '=' inside the parameter list, we
  34131                consider there is a parameter expression inside */
  34132             js_parse_skip_parens_token(s, &skip_bits, FALSE);
  34133             if (skip_bits & SKIP_HAS_ASSIGNMENT)
  34134                 fd->has_parameter_expressions = TRUE;
  34135             if (next_token(s))
  34136                 goto fail;
  34137         } else {
  34138             if (js_parse_expect(s, '('))
  34139                 goto fail;
  34140         }
  34141 
  34142         if (fd->has_parameter_expressions) {
  34143             fd->scope_level = -1; /* force no parent scope */
  34144             if (push_scope(s) < 0)
  34145                 return -1;
  34146         }
  34147 
  34148         while (s->token.val != ')') {
  34149             JSAtom name;
  34150             BOOL rest = FALSE;
  34151             int idx, has_initializer;
  34152 
  34153             if (s->token.val == TOK_ELLIPSIS) {
  34154                 fd->has_simple_parameter_list = FALSE;
  34155                 rest = TRUE;
  34156                 if (next_token(s))
  34157                     goto fail;
  34158             }
  34159             if (s->token.val == '[' || s->token.val == '{') {
  34160                 fd->has_simple_parameter_list = FALSE;
  34161                 if (rest) {
  34162                     emit_op(s, OP_rest);
  34163                     emit_u16(s, fd->arg_count);
  34164                 } else {
  34165                     /* unnamed arg for destructuring */
  34166                     idx = add_arg(ctx, fd, JS_ATOM_NULL);
  34167                     emit_op(s, OP_get_arg);
  34168                     emit_u16(s, idx);
  34169                 }
  34170                 has_initializer = js_parse_destructuring_element(s, fd->has_parameter_expressions ? TOK_LET : TOK_VAR, 1, TRUE, -1, TRUE);
  34171                 if (has_initializer < 0)
  34172                     goto fail;
  34173                 if (has_initializer)
  34174                     has_opt_arg = TRUE;
  34175                 if (!has_opt_arg)
  34176                     fd->defined_arg_count++;
  34177             } else if (s->token.val == TOK_IDENT) {
  34178                 if (s->token.u.ident.is_reserved) {
  34179                     js_parse_error_reserved_identifier(s);
  34180                     goto fail;
  34181                 }
  34182                 name = s->token.u.ident.atom;
  34183                 if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) {
  34184                     js_parse_error_reserved_identifier(s);
  34185                     goto fail;
  34186                 }
  34187                 if (fd->has_parameter_expressions) {
  34188                     if (js_parse_check_duplicate_parameter(s, name))
  34189                         goto fail;
  34190                     if (define_var(s, fd, name, JS_VAR_DEF_LET) < 0)
  34191                         goto fail;
  34192                 }
  34193                 /* XXX: could avoid allocating an argument if rest is true */
  34194                 idx = add_arg(ctx, fd, name);
  34195                 if (idx < 0)
  34196                     goto fail;
  34197                 if (next_token(s))
  34198                     goto fail;
  34199                 if (rest) {
  34200                     emit_op(s, OP_rest);
  34201                     emit_u16(s, idx);
  34202                     if (fd->has_parameter_expressions) {
  34203                         emit_op(s, OP_dup);
  34204                         emit_op(s, OP_scope_put_var_init);
  34205                         emit_atom(s, name);
  34206                         emit_u16(s, fd->scope_level);
  34207                     }
  34208                     emit_op(s, OP_put_arg);
  34209                     emit_u16(s, idx);
  34210                     fd->has_simple_parameter_list = FALSE;
  34211                     has_opt_arg = TRUE;
  34212                 } else if (s->token.val == '=') {
  34213                     int label;
  34214 
  34215                     fd->has_simple_parameter_list = FALSE;
  34216                     has_opt_arg = TRUE;
  34217 
  34218                     if (next_token(s))
  34219                         goto fail;
  34220 
  34221                     label = new_label(s);
  34222                     emit_op(s, OP_get_arg);
  34223                     emit_u16(s, idx);
  34224                     emit_op(s, OP_dup);
  34225                     emit_op(s, OP_undefined);
  34226                     emit_op(s, OP_strict_eq);
  34227                     emit_goto(s, OP_if_false, label);
  34228                     emit_op(s, OP_drop);
  34229                     if (js_parse_assign_expr(s))
  34230                         goto fail;
  34231                     set_object_name(s, name);
  34232                     emit_op(s, OP_dup);
  34233                     emit_op(s, OP_put_arg);
  34234                     emit_u16(s, idx);
  34235                     emit_label(s, label);
  34236                     emit_op(s, OP_scope_put_var_init);
  34237                     emit_atom(s, name);
  34238                     emit_u16(s, fd->scope_level);
  34239                 } else {
  34240                     if (!has_opt_arg) {
  34241                         fd->defined_arg_count++;
  34242                     }
  34243                     if (fd->has_parameter_expressions) {
  34244                         /* copy the argument to the argument scope */
  34245                         emit_op(s, OP_get_arg);
  34246                         emit_u16(s, idx);
  34247                         emit_op(s, OP_scope_put_var_init);
  34248                         emit_atom(s, name);
  34249                         emit_u16(s, fd->scope_level);
  34250                     }
  34251                 }
  34252             } else {
  34253                 js_parse_error(s, "missing formal parameter");
  34254                 goto fail;
  34255             }
  34256             if (rest && s->token.val != ')') {
  34257                 js_parse_expect(s, ')');
  34258                 goto fail;
  34259             }
  34260             if (s->token.val == ')')
  34261                 break;
  34262             if (js_parse_expect(s, ','))
  34263                 goto fail;
  34264         }
  34265         if ((func_type == JS_PARSE_FUNC_GETTER && fd->arg_count != 0) ||
  34266             (func_type == JS_PARSE_FUNC_SETTER && fd->arg_count != 1)) {
  34267             js_parse_error(s, "invalid number of arguments for getter or setter");
  34268             goto fail;
  34269         }
  34270     }
  34271 
  34272     if (fd->has_parameter_expressions) {
  34273         int idx;
  34274 
  34275         /* Copy the variables in the argument scope to the variable
  34276            scope (see FunctionDeclarationInstantiation() in spec). The
  34277            normal arguments are already present, so no need to copy
  34278            them. */
  34279         idx = fd->scopes[fd->scope_level].first;
  34280         while (idx >= 0) {
  34281             JSVarDef *vd = &fd->vars[idx];
  34282             if (vd->scope_level != fd->scope_level)
  34283                 break;
  34284             if (find_var(ctx, fd, vd->var_name) < 0) {
  34285                 if (add_var(ctx, fd, vd->var_name) < 0)
  34286                     goto fail;
  34287                 vd = &fd->vars[idx]; /* fd->vars may have been reallocated */
  34288                 emit_op(s, OP_scope_get_var);
  34289                 emit_atom(s, vd->var_name);
  34290                 emit_u16(s, fd->scope_level);
  34291                 emit_op(s, OP_scope_put_var);
  34292                 emit_atom(s, vd->var_name);
  34293                 emit_u16(s, 0);
  34294             }
  34295             idx = vd->scope_next;
  34296         }
  34297 
  34298         /* the argument scope has no parent, hence we don't use pop_scope(s) */
  34299         emit_op(s, OP_leave_scope);
  34300         emit_u16(s, fd->scope_level);
  34301 
  34302         /* set the variable scope as the current scope */
  34303         fd->scope_level = 0;
  34304         fd->scope_first = fd->scopes[fd->scope_level].first;
  34305     }
  34306 
  34307     if (next_token(s))
  34308         goto fail;
  34309 
  34310     /* generator function: yield after the parameters are evaluated */
  34311     if (func_kind == JS_FUNC_GENERATOR ||
  34312         func_kind == JS_FUNC_ASYNC_GENERATOR)
  34313         emit_op(s, OP_initial_yield);
  34314 
  34315     /* in generators, yield expression is forbidden during the parsing
  34316        of the arguments */
  34317     fd->in_function_body = TRUE;
  34318     push_scope(s);  /* enter body scope */
  34319     fd->body_scope = fd->scope_level;
  34320 
  34321     if (s->token.val == TOK_ARROW) {
  34322         if (next_token(s))
  34323             goto fail;
  34324 
  34325         if (s->token.val != '{') {
  34326             if (js_parse_function_check_names(s, fd, func_name))
  34327                 goto fail;
  34328 
  34329             if (js_parse_assign_expr(s))
  34330                 goto fail;
  34331 
  34332             if (func_kind != JS_FUNC_NORMAL)
  34333                 emit_op(s, OP_return_async);
  34334             else
  34335                 emit_op(s, OP_return);
  34336 
  34337             if (!(fd->js_mode & JS_MODE_STRIP)) {
  34338                 /* save the function source code */
  34339                 /* the end of the function source code is after the last
  34340                    token of the function source stored into s->last_ptr */
  34341                 fd->source_len = s->last_ptr - ptr;
  34342                 fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len);
  34343                 if (!fd->source)
  34344                     goto fail;
  34345             }
  34346             goto done;
  34347         }
  34348     }
  34349 
  34350     if (func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT) {
  34351         if (js_parse_expect(s, '{'))
  34352             goto fail;
  34353     }
  34354 
  34355     if (js_parse_directives(s))
  34356         goto fail;
  34357 
  34358     /* in strict_mode, check function and argument names */
  34359     if (js_parse_function_check_names(s, fd, func_name))
  34360         goto fail;
  34361 
  34362     while (s->token.val != '}') {
  34363         if (js_parse_source_element(s))
  34364             goto fail;
  34365     }
  34366     if (!(fd->js_mode & JS_MODE_STRIP)) {
  34367         /* save the function source code */
  34368         fd->source_len = s->buf_ptr - ptr;
  34369         fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len);
  34370         if (!fd->source)
  34371             goto fail;
  34372     }
  34373 
  34374     if (next_token(s)) {
  34375         /* consume the '}' */
  34376         goto fail;
  34377     }
  34378 
  34379     /* in case there is no return, add one */
  34380     if (js_is_live_code(s)) {
  34381         emit_return(s, FALSE);
  34382     }
  34383  done:
  34384     s->cur_func = fd->parent;
  34385 
  34386     /* Reparse identifiers after the function is terminated so that
  34387        the token is parsed in the englobing function. It could be done
  34388        by just using next_token() here for normal functions, but it is
  34389        necessary for arrow functions with an expression body. */
  34390     reparse_ident_token(s);
  34391 
  34392     /* create the function object */
  34393     {
  34394         int idx;
  34395         JSAtom func_name = fd->func_name;
  34396 
  34397         /* the real object will be set at the end of the compilation */
  34398         idx = cpool_add(s, JS_NULL);
  34399         fd->parent_cpool_idx = idx;
  34400 
  34401         if (is_expr) {
  34402             /* for constructors, no code needs to be generated here */
  34403             if (func_type != JS_PARSE_FUNC_CLASS_CONSTRUCTOR &&
  34404                 func_type != JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) {
  34405                 /* OP_fclosure creates the function object from the bytecode
  34406                    and adds the scope information */
  34407                 emit_op(s, OP_fclosure);
  34408                 emit_u32(s, idx);
  34409                 if (func_name == JS_ATOM_NULL) {
  34410                     emit_op(s, OP_set_name);
  34411                     emit_u32(s, JS_ATOM_NULL);
  34412                 }
  34413             }
  34414         } else if (func_type == JS_PARSE_FUNC_VAR) {
  34415             emit_op(s, OP_fclosure);
  34416             emit_u32(s, idx);
  34417             if (create_func_var) {
  34418                 if (s->cur_func->is_global_var) {
  34419                     JSGlobalVar *hf;
  34420                     /* the global variable must be defined at the start of the
  34421                        function */
  34422                     hf = add_global_var(ctx, s->cur_func, func_name);
  34423                     if (!hf)
  34424                         goto fail;
  34425                     /* it is considered as defined at the top level
  34426                        (needed for annex B.3.3.4 and B.3.3.5
  34427                        checks) */
  34428                     hf->scope_level = 0;
  34429                     hf->force_init = ((s->cur_func->js_mode & JS_MODE_STRICT) != 0);
  34430                     /* store directly into global var, bypass lexical scope */
  34431                     emit_op(s, OP_dup);
  34432                     emit_op(s, OP_scope_put_var);
  34433                     emit_atom(s, func_name);
  34434                     emit_u16(s, 0);
  34435                 } else {
  34436                     /* do not call define_var to bypass lexical scope check */
  34437                     func_idx = find_var(ctx, s->cur_func, func_name);
  34438                     if (func_idx < 0) {
  34439                         func_idx = add_var(ctx, s->cur_func, func_name);
  34440                         if (func_idx < 0)
  34441                             goto fail;
  34442                     }
  34443                     /* store directly into local var, bypass lexical catch scope */
  34444                     emit_op(s, OP_dup);
  34445                     emit_op(s, OP_scope_put_var);
  34446                     emit_atom(s, func_name);
  34447                     emit_u16(s, 0);
  34448                 }
  34449             }
  34450             if (lexical_func_idx >= 0) {
  34451                 /* lexical variable will be initialized upon entering scope */
  34452                 s->cur_func->vars[lexical_func_idx].func_pool_idx = idx;
  34453                 emit_op(s, OP_drop);
  34454             } else {
  34455                 /* store function object into its lexical name */
  34456                 /* XXX: could use OP_put_loc directly */
  34457                 emit_op(s, OP_scope_put_var_init);
  34458                 emit_atom(s, func_name);
  34459                 emit_u16(s, s->cur_func->scope_level);
  34460             }
  34461         } else {
  34462             if (!s->cur_func->is_global_var) {
  34463                 int var_idx = define_var(s, s->cur_func, func_name, JS_VAR_DEF_VAR);
  34464 
  34465                 if (var_idx < 0)
  34466                     goto fail;
  34467                 /* the variable will be assigned at the top of the function */
  34468                 if (var_idx & ARGUMENT_VAR_OFFSET) {
  34469                     s->cur_func->args[var_idx - ARGUMENT_VAR_OFFSET].func_pool_idx = idx;
  34470                 } else {
  34471                     s->cur_func->vars[var_idx].func_pool_idx = idx;
  34472                 }
  34473             } else {
  34474                 JSAtom func_var_name;
  34475                 JSGlobalVar *hf;
  34476                 if (func_name == JS_ATOM_NULL)
  34477                     func_var_name = JS_ATOM__default_; /* export default */
  34478                 else
  34479                     func_var_name = func_name;
  34480                 /* the variable will be assigned at the top of the function */
  34481                 hf = add_global_var(ctx, s->cur_func, func_var_name);
  34482                 if (!hf)
  34483                     goto fail;
  34484                 hf->cpool_idx = idx;
  34485                 if (export_flag != JS_PARSE_EXPORT_NONE) {
  34486                     if (!add_export_entry(s, s->cur_func->module, func_var_name,
  34487                                           export_flag == JS_PARSE_EXPORT_NAMED ? func_var_name : JS_ATOM_default, JS_EXPORT_TYPE_LOCAL))
  34488                         goto fail;
  34489                 }
  34490             }
  34491         }
  34492     }
  34493     return 0;
  34494  fail:
  34495     s->cur_func = fd->parent;
  34496     js_free_function_def(ctx, fd);
  34497     if (pfd)
  34498         *pfd = NULL;
  34499     return -1;
  34500 }
  34501 
  34502 static __exception int js_parse_function_decl(JSParseState *s,
  34503                                               JSParseFunctionEnum func_type,
  34504                                               JSFunctionKindEnum func_kind,
  34505                                               JSAtom func_name,
  34506                                               const uint8_t *ptr,
  34507                                               int function_line_num)
  34508 {
  34509     return js_parse_function_decl2(s, func_type, func_kind, func_name, ptr,
  34510                                    function_line_num, JS_PARSE_EXPORT_NONE,
  34511                                    NULL);
  34512 }
  34513 
  34514 static __exception int js_parse_program(JSParseState *s)
  34515 {
  34516     JSFunctionDef *fd = s->cur_func;
  34517     int idx;
  34518 
  34519     if (next_token(s))
  34520         return -1;
  34521 
  34522     if (js_parse_directives(s))
  34523         return -1;
  34524 
  34525     fd->is_global_var = (fd->eval_type == JS_EVAL_TYPE_GLOBAL) ||
  34526         (fd->eval_type == JS_EVAL_TYPE_MODULE) ||
  34527         !(fd->js_mode & JS_MODE_STRICT);
  34528 
  34529     if (!s->is_module) {
  34530         /* hidden variable for the return value */
  34531         fd->eval_ret_idx = idx = add_var(s->ctx, fd, JS_ATOM__ret_);
  34532         if (idx < 0)
  34533             return -1;
  34534     }
  34535 
  34536     while (s->token.val != TOK_EOF) {
  34537         if (js_parse_source_element(s))
  34538             return -1;
  34539     }
  34540 
  34541     if (!s->is_module) {
  34542         /* return the value of the hidden variable eval_ret_idx  */
  34543         if (fd->func_kind == JS_FUNC_ASYNC) {
  34544             /* wrap the return value in an object so that promises can
  34545                be safely returned */
  34546             emit_op(s, OP_object);
  34547             emit_op(s, OP_dup);
  34548 
  34549             emit_op(s, OP_get_loc);
  34550             emit_u16(s, fd->eval_ret_idx);
  34551 
  34552             emit_op(s, OP_put_field);
  34553             emit_atom(s, JS_ATOM_value);
  34554         } else {
  34555             emit_op(s, OP_get_loc);
  34556             emit_u16(s, fd->eval_ret_idx);
  34557         }
  34558         emit_return(s, TRUE);
  34559     } else {
  34560         emit_return(s, FALSE);
  34561     }
  34562 
  34563     return 0;
  34564 }
  34565 
  34566 static void js_parse_init(JSContext *ctx, JSParseState *s,
  34567                           const char *input, size_t input_len,
  34568                           const char *filename)
  34569 {
  34570     memset(s, 0, sizeof(*s));
  34571     s->ctx = ctx;
  34572     s->filename = filename;
  34573     s->line_num = 1;
  34574     s->buf_ptr = (const uint8_t *)input;
  34575     s->buf_end = s->buf_ptr + input_len;
  34576     s->token.val = ' ';
  34577     s->token.line_num = 1;
  34578 }
  34579 
  34580 static JSValue JS_EvalFunctionInternal(JSContext *ctx, JSValue fun_obj,
  34581                                        JSValueConst this_obj,
  34582                                        JSVarRef **var_refs, JSStackFrame *sf)
  34583 {
  34584     JSValue ret_val;
  34585     uint32_t tag;
  34586 
  34587     tag = JS_VALUE_GET_TAG(fun_obj);
  34588     if (tag == JS_TAG_FUNCTION_BYTECODE) {
  34589         fun_obj = js_closure(ctx, fun_obj, var_refs, sf);
  34590         ret_val = JS_CallFree(ctx, fun_obj, this_obj, 0, NULL);
  34591     } else if (tag == JS_TAG_MODULE) {
  34592         JSModuleDef *m;
  34593         m = JS_VALUE_GET_PTR(fun_obj);
  34594         /* the module refcount should be >= 2 */
  34595         JS_FreeValue(ctx, fun_obj);
  34596         if (js_create_module_function(ctx, m) < 0)
  34597             goto fail;
  34598         if (js_link_module(ctx, m) < 0)
  34599             goto fail;
  34600         ret_val = js_evaluate_module(ctx, m);
  34601         if (JS_IsException(ret_val)) {
  34602         fail:
  34603             return JS_EXCEPTION;
  34604         }
  34605     } else {
  34606         JS_FreeValue(ctx, fun_obj);
  34607         ret_val = JS_ThrowTypeError(ctx, "bytecode function expected");
  34608     }
  34609     return ret_val;
  34610 }
  34611 
  34612 JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj)
  34613 {
  34614     return JS_EvalFunctionInternal(ctx, fun_obj, ctx->global_obj, NULL, NULL);
  34615 }
  34616 
  34617 /* 'input' must be zero terminated i.e. input[input_len] = '\0'. */
  34618 static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
  34619                                  const char *input, size_t input_len,
  34620                                  const char *filename, int flags, int scope_idx)
  34621 {
  34622     JSParseState s1, *s = &s1;
  34623     int err, js_mode, eval_type;
  34624     JSValue fun_obj, ret_val;
  34625     JSStackFrame *sf;
  34626     JSVarRef **var_refs;
  34627     JSFunctionBytecode *b;
  34628     JSFunctionDef *fd;
  34629     JSModuleDef *m;
  34630 
  34631     js_parse_init(ctx, s, input, input_len, filename);
  34632     skip_shebang(&s->buf_ptr, s->buf_end);
  34633 
  34634     eval_type = flags & JS_EVAL_TYPE_MASK;
  34635     m = NULL;
  34636     if (eval_type == JS_EVAL_TYPE_DIRECT) {
  34637         JSObject *p;
  34638         sf = ctx->rt->current_stack_frame;
  34639         assert(sf != NULL);
  34640         assert(JS_VALUE_GET_TAG(sf->cur_func) == JS_TAG_OBJECT);
  34641         p = JS_VALUE_GET_OBJ(sf->cur_func);
  34642         assert(js_class_has_bytecode(p->class_id));
  34643         b = p->u.func.function_bytecode;
  34644         var_refs = p->u.func.var_refs;
  34645         js_mode = b->js_mode;
  34646     } else {
  34647         sf = NULL;
  34648         b = NULL;
  34649         var_refs = NULL;
  34650         js_mode = 0;
  34651         if (flags & JS_EVAL_FLAG_STRICT)
  34652             js_mode |= JS_MODE_STRICT;
  34653         if (flags & JS_EVAL_FLAG_STRIP)
  34654             js_mode |= JS_MODE_STRIP;
  34655         if (eval_type == JS_EVAL_TYPE_MODULE) {
  34656             JSAtom module_name = JS_NewAtom(ctx, filename);
  34657             if (module_name == JS_ATOM_NULL)
  34658                 return JS_EXCEPTION;
  34659             m = js_new_module_def(ctx, module_name);
  34660             if (!m)
  34661                 return JS_EXCEPTION;
  34662             js_mode |= JS_MODE_STRICT;
  34663         }
  34664     }
  34665     fd = js_new_function_def(ctx, NULL, TRUE, FALSE, filename, 1);
  34666     if (!fd) {
  34667         goto fail1;
  34668     }
  34669     s->cur_func = fd;
  34670     fd->eval_type = eval_type;
  34671     fd->has_this_binding = (eval_type != JS_EVAL_TYPE_DIRECT);
  34672     fd->backtrace_barrier = ((flags & JS_EVAL_FLAG_BACKTRACE_BARRIER) != 0);
  34673     if (eval_type == JS_EVAL_TYPE_DIRECT) {
  34674         fd->new_target_allowed = b->new_target_allowed;
  34675         fd->super_call_allowed = b->super_call_allowed;
  34676         fd->super_allowed = b->super_allowed;
  34677         fd->arguments_allowed = b->arguments_allowed;
  34678     } else {
  34679         fd->new_target_allowed = FALSE;
  34680         fd->super_call_allowed = FALSE;
  34681         fd->super_allowed = FALSE;
  34682         fd->arguments_allowed = TRUE;
  34683     }
  34684     fd->js_mode = js_mode;
  34685     fd->func_name = JS_DupAtom(ctx, JS_ATOM__eval_);
  34686     if (b) {
  34687         if (add_closure_variables(ctx, fd, b, scope_idx)) {
  34688             goto fail;
  34689         }
  34690     }
  34691     fd->module = m;
  34692     if (m != NULL || (flags & JS_EVAL_FLAG_ASYNC)) {
  34693         fd->in_function_body = TRUE;
  34694         fd->func_kind = JS_FUNC_ASYNC;
  34695     }
  34696     s->is_module = (m != NULL);
  34697     s->allow_html_comments = !s->is_module;
  34698 
  34699     push_scope(s); /* body scope */
  34700     fd->body_scope = fd->scope_level;
  34701 
  34702     err = js_parse_program(s);
  34703     if (err) {
  34704     fail:
  34705         free_token(s, &s->token);
  34706         js_free_function_def(ctx, fd);
  34707         goto fail1;
  34708     }
  34709 
  34710     if (m != NULL)
  34711         m->has_tla = fd->has_await;
  34712 
  34713     /* create the function object and all the enclosed functions */
  34714     fun_obj = js_create_function(ctx, fd);
  34715     if (JS_IsException(fun_obj))
  34716         goto fail1;
  34717     /* Could add a flag to avoid resolution if necessary */
  34718     if (m) {
  34719         m->func_obj = fun_obj;
  34720         if (js_resolve_module(ctx, m) < 0)
  34721             goto fail1;
  34722         fun_obj = JS_NewModuleValue(ctx, m);
  34723     }
  34724     if (flags & JS_EVAL_FLAG_COMPILE_ONLY) {
  34725         ret_val = fun_obj;
  34726     } else {
  34727         ret_val = JS_EvalFunctionInternal(ctx, fun_obj, this_obj, var_refs, sf);
  34728     }
  34729     return ret_val;
  34730  fail1:
  34731     /* XXX: should free all the unresolved dependencies */
  34732     if (m)
  34733         js_free_module_def(ctx, m);
  34734     return JS_EXCEPTION;
  34735 }
  34736 
  34737 /* the indirection is needed to make 'eval' optional */
  34738 static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
  34739                                const char *input, size_t input_len,
  34740                                const char *filename, int flags, int scope_idx)
  34741 {
  34742     if (unlikely(!ctx->eval_internal)) {
  34743         return JS_ThrowTypeError(ctx, "eval is not supported");
  34744     }
  34745     return ctx->eval_internal(ctx, this_obj, input, input_len, filename,
  34746                               flags, scope_idx);
  34747 }
  34748 
  34749 static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
  34750                              JSValueConst val, int flags, int scope_idx)
  34751 {
  34752     JSValue ret;
  34753     const char *str;
  34754     size_t len;
  34755 
  34756     if (!JS_IsString(val))
  34757         return JS_DupValue(ctx, val);
  34758     str = JS_ToCStringLen(ctx, &len, val);
  34759     if (!str)
  34760         return JS_EXCEPTION;
  34761     ret = JS_EvalInternal(ctx, this_obj, str, len, "<input>", flags, scope_idx);
  34762     JS_FreeCString(ctx, str);
  34763     return ret;
  34764 
  34765 }
  34766 
  34767 JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj,
  34768                     const char *input, size_t input_len,
  34769                     const char *filename, int eval_flags)
  34770 {
  34771     int eval_type = eval_flags & JS_EVAL_TYPE_MASK;
  34772     JSValue ret;
  34773 
  34774     assert(eval_type == JS_EVAL_TYPE_GLOBAL ||
  34775            eval_type == JS_EVAL_TYPE_MODULE);
  34776     ret = JS_EvalInternal(ctx, this_obj, input, input_len, filename,
  34777                           eval_flags, -1);
  34778     return ret;
  34779 }
  34780 
  34781 JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len,
  34782                 const char *filename, int eval_flags)
  34783 {
  34784     return JS_EvalThis(ctx, ctx->global_obj, input, input_len, filename,
  34785                        eval_flags);
  34786 }
  34787 
  34788 int JS_ResolveModule(JSContext *ctx, JSValueConst obj)
  34789 {
  34790     if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) {
  34791         JSModuleDef *m = JS_VALUE_GET_PTR(obj);
  34792         if (js_resolve_module(ctx, m) < 0) {
  34793             js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED);
  34794             return -1;
  34795         }
  34796     }
  34797     return 0;
  34798 }
  34799 
  34800 /*******************************************************************/
  34801 /* object list */
  34802 
  34803 typedef struct {
  34804     JSObject *obj;
  34805     uint32_t hash_next; /* -1 if no next entry */
  34806 } JSObjectListEntry;
  34807 
  34808 /* XXX: reuse it to optimize weak references */
  34809 typedef struct {
  34810     JSObjectListEntry *object_tab;
  34811     int object_count;
  34812     int object_size;
  34813     uint32_t *hash_table;
  34814     uint32_t hash_size;
  34815 } JSObjectList;
  34816 
  34817 static void js_object_list_init(JSObjectList *s)
  34818 {
  34819     memset(s, 0, sizeof(*s));
  34820 }
  34821 
  34822 static uint32_t js_object_list_get_hash(JSObject *p, uint32_t hash_size)
  34823 {
  34824     return ((uintptr_t)p * 3163) & (hash_size - 1);
  34825 }
  34826 
  34827 static int js_object_list_resize_hash(JSContext *ctx, JSObjectList *s,
  34828                                  uint32_t new_hash_size)
  34829 {
  34830     JSObjectListEntry *e;
  34831     uint32_t i, h, *new_hash_table;
  34832 
  34833     new_hash_table = js_malloc(ctx, sizeof(new_hash_table[0]) * new_hash_size);
  34834     if (!new_hash_table)
  34835         return -1;
  34836     js_free(ctx, s->hash_table);
  34837     s->hash_table = new_hash_table;
  34838     s->hash_size = new_hash_size;
  34839 
  34840     for(i = 0; i < s->hash_size; i++) {
  34841         s->hash_table[i] = -1;
  34842     }
  34843     for(i = 0; i < s->object_count; i++) {
  34844         e = &s->object_tab[i];
  34845         h = js_object_list_get_hash(e->obj, s->hash_size);
  34846         e->hash_next = s->hash_table[h];
  34847         s->hash_table[h] = i;
  34848     }
  34849     return 0;
  34850 }
  34851 
  34852 /* the reference count of 'obj' is not modified. Return 0 if OK, -1 if
  34853    memory error */
  34854 static int js_object_list_add(JSContext *ctx, JSObjectList *s, JSObject *obj)
  34855 {
  34856     JSObjectListEntry *e;
  34857     uint32_t h, new_hash_size;
  34858 
  34859     if (js_resize_array(ctx, (void *)&s->object_tab,
  34860                         sizeof(s->object_tab[0]),
  34861                         &s->object_size, s->object_count + 1))
  34862         return -1;
  34863     if (unlikely((s->object_count + 1) >= s->hash_size)) {
  34864         new_hash_size = max_uint32(s->hash_size, 4);
  34865         while (new_hash_size <= s->object_count)
  34866             new_hash_size *= 2;
  34867         if (js_object_list_resize_hash(ctx, s, new_hash_size))
  34868             return -1;
  34869     }
  34870     e = &s->object_tab[s->object_count++];
  34871     h = js_object_list_get_hash(obj, s->hash_size);
  34872     e->obj = obj;
  34873     e->hash_next = s->hash_table[h];
  34874     s->hash_table[h] = s->object_count - 1;
  34875     return 0;
  34876 }
  34877 
  34878 /* return -1 if not present or the object index */
  34879 static int js_object_list_find(JSContext *ctx, JSObjectList *s, JSObject *obj)
  34880 {
  34881     JSObjectListEntry *e;
  34882     uint32_t h, p;
  34883 
  34884     /* must test empty size because there is no hash table */
  34885     if (s->object_count == 0)
  34886         return -1;
  34887     h = js_object_list_get_hash(obj, s->hash_size);
  34888     p = s->hash_table[h];
  34889     while (p != -1) {
  34890         e = &s->object_tab[p];
  34891         if (e->obj == obj)
  34892             return p;
  34893         p = e->hash_next;
  34894     }
  34895     return -1;
  34896 }
  34897 
  34898 static void js_object_list_end(JSContext *ctx, JSObjectList *s)
  34899 {
  34900     js_free(ctx, s->object_tab);
  34901     js_free(ctx, s->hash_table);
  34902 }
  34903 
  34904 /*******************************************************************/
  34905 /* binary object writer & reader */
  34906 
  34907 typedef enum BCTagEnum {
  34908     BC_TAG_NULL = 1,
  34909     BC_TAG_UNDEFINED,
  34910     BC_TAG_BOOL_FALSE,
  34911     BC_TAG_BOOL_TRUE,
  34912     BC_TAG_INT32,
  34913     BC_TAG_FLOAT64,
  34914     BC_TAG_STRING,
  34915     BC_TAG_OBJECT,
  34916     BC_TAG_ARRAY,
  34917     BC_TAG_BIG_INT,
  34918     BC_TAG_TEMPLATE_OBJECT,
  34919     BC_TAG_FUNCTION_BYTECODE,
  34920     BC_TAG_MODULE,
  34921     BC_TAG_TYPED_ARRAY,
  34922     BC_TAG_ARRAY_BUFFER,
  34923     BC_TAG_SHARED_ARRAY_BUFFER,
  34924     BC_TAG_DATE,
  34925     BC_TAG_OBJECT_VALUE,
  34926     BC_TAG_OBJECT_REFERENCE,
  34927 #ifdef CONFIG_BIGNUM
  34928     BC_TAG_BIG_FLOAT,
  34929     BC_TAG_BIG_DECIMAL,
  34930 #endif
  34931 } BCTagEnum;
  34932 
  34933 #ifdef CONFIG_BIGNUM
  34934 #define BC_VERSION 0x43
  34935 #else
  34936 #define BC_VERSION 3
  34937 #endif
  34938 
  34939 typedef struct BCWriterState {
  34940     JSContext *ctx;
  34941     DynBuf dbuf;
  34942     BOOL allow_bytecode : 8;
  34943     BOOL allow_sab : 8;
  34944     BOOL allow_reference : 8;
  34945     uint32_t first_atom;
  34946     uint32_t *atom_to_idx;
  34947     int atom_to_idx_size;
  34948     JSAtom *idx_to_atom;
  34949     int idx_to_atom_count;
  34950     int idx_to_atom_size;
  34951     uint8_t **sab_tab;
  34952     int sab_tab_len;
  34953     int sab_tab_size;
  34954     /* list of referenced objects (used if allow_reference = TRUE) */
  34955     JSObjectList object_list;
  34956 } BCWriterState;
  34957 
  34958 #ifdef DUMP_READ_OBJECT
  34959 static const char * const bc_tag_str[] = {
  34960     "invalid",
  34961     "null",
  34962     "undefined",
  34963     "false",
  34964     "true",
  34965     "int32",
  34966     "float64",
  34967     "string",
  34968     "object",
  34969     "array",
  34970     "bigint",
  34971     "template",
  34972     "function",
  34973     "module",
  34974     "TypedArray",
  34975     "ArrayBuffer",
  34976     "SharedArrayBuffer",
  34977     "Date",
  34978     "ObjectValue",
  34979     "ObjectReference",
  34980 #ifdef CONFIG_BIGNUM
  34981     "bigfloat",
  34982     "bigdecimal",
  34983 #endif
  34984 };
  34985 #endif
  34986 
  34987 static inline BOOL is_be(void)
  34988 {
  34989     union {
  34990         uint16_t a;
  34991         uint8_t  b;
  34992     } u = {0x100};
  34993     return u.b;
  34994 }
  34995 
  34996 static void bc_put_u8(BCWriterState *s, uint8_t v)
  34997 {
  34998     dbuf_putc(&s->dbuf, v);
  34999 }
  35000 
  35001 static void bc_put_u16(BCWriterState *s, uint16_t v)
  35002 {
  35003     if (is_be())
  35004         v = bswap16(v);
  35005     dbuf_put_u16(&s->dbuf, v);
  35006 }
  35007 
  35008 static __maybe_unused void bc_put_u32(BCWriterState *s, uint32_t v)
  35009 {
  35010     if (is_be())
  35011         v = bswap32(v);
  35012     dbuf_put_u32(&s->dbuf, v);
  35013 }
  35014 
  35015 static void bc_put_u64(BCWriterState *s, uint64_t v)
  35016 {
  35017     if (is_be())
  35018         v = bswap64(v);
  35019     dbuf_put(&s->dbuf, (uint8_t *)&v, sizeof(v));
  35020 }
  35021 
  35022 static void bc_put_leb128(BCWriterState *s, uint32_t v)
  35023 {
  35024     dbuf_put_leb128(&s->dbuf, v);
  35025 }
  35026 
  35027 static void bc_put_sleb128(BCWriterState *s, int32_t v)
  35028 {
  35029     dbuf_put_sleb128(&s->dbuf, v);
  35030 }
  35031 
  35032 static void bc_set_flags(uint32_t *pflags, int *pidx, uint32_t val, int n)
  35033 {
  35034     *pflags = *pflags | (val << *pidx);
  35035     *pidx += n;
  35036 }
  35037 
  35038 static int bc_atom_to_idx(BCWriterState *s, uint32_t *pres, JSAtom atom)
  35039 {
  35040     uint32_t v;
  35041 
  35042     if (atom < s->first_atom || __JS_AtomIsTaggedInt(atom)) {
  35043         *pres = atom;
  35044         return 0;
  35045     }
  35046     atom -= s->first_atom;
  35047     if (atom < s->atom_to_idx_size && s->atom_to_idx[atom] != 0) {
  35048         *pres = s->atom_to_idx[atom];
  35049         return 0;
  35050     }
  35051     if (atom >= s->atom_to_idx_size) {
  35052         int old_size, i;
  35053         old_size = s->atom_to_idx_size;
  35054         if (js_resize_array(s->ctx, (void **)&s->atom_to_idx,
  35055                             sizeof(s->atom_to_idx[0]), &s->atom_to_idx_size,
  35056                             atom + 1))
  35057             return -1;
  35058         /* XXX: could add a specific js_resize_array() function to do it */
  35059         for(i = old_size; i < s->atom_to_idx_size; i++)
  35060             s->atom_to_idx[i] = 0;
  35061     }
  35062     if (js_resize_array(s->ctx, (void **)&s->idx_to_atom,
  35063                         sizeof(s->idx_to_atom[0]),
  35064                         &s->idx_to_atom_size, s->idx_to_atom_count + 1))
  35065         goto fail;
  35066 
  35067     v = s->idx_to_atom_count++;
  35068     s->idx_to_atom[v] = atom + s->first_atom;
  35069     v += s->first_atom;
  35070     s->atom_to_idx[atom] = v;
  35071     *pres = v;
  35072     return 0;
  35073  fail:
  35074     *pres = 0;
  35075     return -1;
  35076 }
  35077 
  35078 static int bc_put_atom(BCWriterState *s, JSAtom atom)
  35079 {
  35080     uint32_t v;
  35081 
  35082     if (__JS_AtomIsTaggedInt(atom)) {
  35083         v = (__JS_AtomToUInt32(atom) << 1) | 1;
  35084     } else {
  35085         if (bc_atom_to_idx(s, &v, atom))
  35086             return -1;
  35087         v <<= 1;
  35088     }
  35089     bc_put_leb128(s, v);
  35090     return 0;
  35091 }
  35092 
  35093 static void bc_byte_swap(uint8_t *bc_buf, int bc_len)
  35094 {
  35095     int pos, len, op, fmt;
  35096 
  35097     pos = 0;
  35098     while (pos < bc_len) {
  35099         op = bc_buf[pos];
  35100         len = short_opcode_info(op).size;
  35101         fmt = short_opcode_info(op).fmt;
  35102         switch(fmt) {
  35103         case OP_FMT_u16:
  35104         case OP_FMT_i16:
  35105         case OP_FMT_label16:
  35106         case OP_FMT_npop:
  35107         case OP_FMT_loc:
  35108         case OP_FMT_arg:
  35109         case OP_FMT_var_ref:
  35110             put_u16(bc_buf + pos + 1,
  35111                     bswap16(get_u16(bc_buf + pos + 1)));
  35112             break;
  35113         case OP_FMT_i32:
  35114         case OP_FMT_u32:
  35115         case OP_FMT_const:
  35116         case OP_FMT_label:
  35117         case OP_FMT_atom:
  35118         case OP_FMT_atom_u8:
  35119             put_u32(bc_buf + pos + 1,
  35120                     bswap32(get_u32(bc_buf + pos + 1)));
  35121             break;
  35122         case OP_FMT_atom_u16:
  35123         case OP_FMT_label_u16:
  35124             put_u32(bc_buf + pos + 1,
  35125                     bswap32(get_u32(bc_buf + pos + 1)));
  35126             put_u16(bc_buf + pos + 1 + 4,
  35127                     bswap16(get_u16(bc_buf + pos + 1 + 4)));
  35128             break;
  35129         case OP_FMT_atom_label_u8:
  35130         case OP_FMT_atom_label_u16:
  35131             put_u32(bc_buf + pos + 1,
  35132                     bswap32(get_u32(bc_buf + pos + 1)));
  35133             put_u32(bc_buf + pos + 1 + 4,
  35134                     bswap32(get_u32(bc_buf + pos + 1 + 4)));
  35135             if (fmt == OP_FMT_atom_label_u16) {
  35136                 put_u16(bc_buf + pos + 1 + 4 + 4,
  35137                         bswap16(get_u16(bc_buf + pos + 1 + 4 + 4)));
  35138             }
  35139             break;
  35140         case OP_FMT_npop_u16:
  35141             put_u16(bc_buf + pos + 1,
  35142                     bswap16(get_u16(bc_buf + pos + 1)));
  35143             put_u16(bc_buf + pos + 1 + 2,
  35144                     bswap16(get_u16(bc_buf + pos + 1 + 2)));
  35145             break;
  35146         default:
  35147             break;
  35148         }
  35149         pos += len;
  35150     }
  35151 }
  35152 
  35153 static int JS_WriteFunctionBytecode(BCWriterState *s,
  35154                                     const uint8_t *bc_buf1, int bc_len)
  35155 {
  35156     int pos, len, op;
  35157     JSAtom atom;
  35158     uint8_t *bc_buf;
  35159     uint32_t val;
  35160 
  35161     bc_buf = js_malloc(s->ctx, bc_len);
  35162     if (!bc_buf)
  35163         return -1;
  35164     memcpy(bc_buf, bc_buf1, bc_len);
  35165 
  35166     pos = 0;
  35167     while (pos < bc_len) {
  35168         op = bc_buf[pos];
  35169         len = short_opcode_info(op).size;
  35170         switch(short_opcode_info(op).fmt) {
  35171         case OP_FMT_atom:
  35172         case OP_FMT_atom_u8:
  35173         case OP_FMT_atom_u16:
  35174         case OP_FMT_atom_label_u8:
  35175         case OP_FMT_atom_label_u16:
  35176             atom = get_u32(bc_buf + pos + 1);
  35177             if (bc_atom_to_idx(s, &val, atom))
  35178                 goto fail;
  35179             put_u32(bc_buf + pos + 1, val);
  35180             break;
  35181         default:
  35182             break;
  35183         }
  35184         pos += len;
  35185     }
  35186 
  35187     if (is_be())
  35188         bc_byte_swap(bc_buf, bc_len);
  35189 
  35190     dbuf_put(&s->dbuf, bc_buf, bc_len);
  35191 
  35192     js_free(s->ctx, bc_buf);
  35193     return 0;
  35194  fail:
  35195     js_free(s->ctx, bc_buf);
  35196     return -1;
  35197 }
  35198 
  35199 static void JS_WriteString(BCWriterState *s, JSString *p)
  35200 {
  35201     int i;
  35202     bc_put_leb128(s, ((uint32_t)p->len << 1) | p->is_wide_char);
  35203     if (p->is_wide_char) {
  35204         for(i = 0; i < p->len; i++)
  35205             bc_put_u16(s, p->u.str16[i]);
  35206     } else {
  35207         dbuf_put(&s->dbuf, p->u.str8, p->len);
  35208     }
  35209 }
  35210 
  35211 static int JS_WriteBigNum(BCWriterState *s, JSValueConst obj)
  35212 {
  35213     uint32_t tag, tag1;
  35214     int64_t e;
  35215     JSBigFloat *bf = JS_VALUE_GET_PTR(obj);
  35216     bf_t *a = &bf->num;
  35217     size_t len, i, n1, j;
  35218     limb_t v;
  35219 
  35220     tag = JS_VALUE_GET_TAG(obj);
  35221     switch(tag) {
  35222     case JS_TAG_BIG_INT:
  35223         tag1 = BC_TAG_BIG_INT;
  35224         break;
  35225 #ifdef CONFIG_BIGNUM
  35226     case JS_TAG_BIG_FLOAT:
  35227         tag1 = BC_TAG_BIG_FLOAT;
  35228         break;
  35229     case JS_TAG_BIG_DECIMAL:
  35230         tag1 = BC_TAG_BIG_DECIMAL;
  35231         break;
  35232 #endif
  35233     default:
  35234         abort();
  35235     }
  35236     bc_put_u8(s, tag1);
  35237 
  35238     /* sign + exponent */
  35239     if (a->expn == BF_EXP_ZERO)
  35240         e = 0;
  35241     else if (a->expn == BF_EXP_INF)
  35242         e = 1;
  35243     else if (a->expn == BF_EXP_NAN)
  35244         e = 2;
  35245     else if (a->expn >= 0)
  35246         e = a->expn + 3;
  35247     else
  35248         e = a->expn;
  35249     e = (e * 2) | a->sign;
  35250     if (e < INT32_MIN || e > INT32_MAX) {
  35251         JS_ThrowInternalError(s->ctx, "bignum exponent is too large");
  35252         return -1;
  35253     }
  35254     bc_put_sleb128(s, e);
  35255 
  35256     /* mantissa */
  35257     if (a->len != 0) {
  35258         if (tag != JS_TAG_BIG_DECIMAL) {
  35259             i = 0;
  35260             while (i < a->len && a->tab[i] == 0)
  35261                 i++;
  35262             assert(i < a->len);
  35263             v = a->tab[i];
  35264             n1 = sizeof(limb_t);
  35265             while ((v & 0xff) == 0) {
  35266                 n1--;
  35267                 v >>= 8;
  35268             }
  35269             i++;
  35270             len = (a->len - i) * sizeof(limb_t) + n1;
  35271             if (len > INT32_MAX) {
  35272                 JS_ThrowInternalError(s->ctx, "bignum is too large");
  35273                 return -1;
  35274             }
  35275             bc_put_leb128(s, len);
  35276             /* always saved in byte based little endian representation */
  35277             for(j = 0; j < n1; j++) {
  35278                 bc_put_u8(s, v >> (j * 8));
  35279             }
  35280             for(; i < a->len; i++) {
  35281                 limb_t v = a->tab[i];
  35282 #if LIMB_BITS == 32
  35283                 bc_put_u32(s, v);
  35284 #else
  35285                 bc_put_u64(s, v);
  35286 #endif
  35287             }
  35288         } else {
  35289             int bpos, d;
  35290             uint8_t v8;
  35291             size_t i0;
  35292 
  35293             /* little endian BCD */
  35294             i = 0;
  35295             while (i < a->len && a->tab[i] == 0)
  35296                 i++;
  35297             assert(i < a->len);
  35298             len = a->len * LIMB_DIGITS;
  35299             v = a->tab[i];
  35300             j = 0;
  35301             while ((v % 10) == 0) {
  35302                 j++;
  35303                 v /= 10;
  35304             }
  35305             len -= j;
  35306             assert(len > 0);
  35307             if (len > INT32_MAX) {
  35308                 JS_ThrowInternalError(s->ctx, "bignum is too large");
  35309                 return -1;
  35310             }
  35311             bc_put_leb128(s, len);
  35312 
  35313             bpos = 0;
  35314             v8 = 0;
  35315             i0 = i;
  35316             for(; i < a->len; i++) {
  35317                 if (i != i0) {
  35318                     v = a->tab[i];
  35319                     j = 0;
  35320                 }
  35321                 for(; j < LIMB_DIGITS; j++) {
  35322                     d = v % 10;
  35323                     v /= 10;
  35324                     if (bpos == 0) {
  35325                         v8 = d;
  35326                         bpos = 1;
  35327                     } else {
  35328                         bc_put_u8(s, v8 | (d << 4));
  35329                         bpos = 0;
  35330                     }
  35331                 }
  35332             }
  35333             /* flush the last digit */
  35334             if (bpos) {
  35335                 bc_put_u8(s, v8);
  35336             }
  35337         }
  35338     }
  35339     return 0;
  35340 }
  35341 
  35342 static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj);
  35343 
  35344 static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj)
  35345 {
  35346     JSFunctionBytecode *b = JS_VALUE_GET_PTR(obj);
  35347     uint32_t flags;
  35348     int idx, i;
  35349 
  35350     bc_put_u8(s, BC_TAG_FUNCTION_BYTECODE);
  35351     flags = idx = 0;
  35352     bc_set_flags(&flags, &idx, b->has_prototype, 1);
  35353     bc_set_flags(&flags, &idx, b->has_simple_parameter_list, 1);
  35354     bc_set_flags(&flags, &idx, b->is_derived_class_constructor, 1);
  35355     bc_set_flags(&flags, &idx, b->need_home_object, 1);
  35356     bc_set_flags(&flags, &idx, b->func_kind, 2);
  35357     bc_set_flags(&flags, &idx, b->new_target_allowed, 1);
  35358     bc_set_flags(&flags, &idx, b->super_call_allowed, 1);
  35359     bc_set_flags(&flags, &idx, b->super_allowed, 1);
  35360     bc_set_flags(&flags, &idx, b->arguments_allowed, 1);
  35361     bc_set_flags(&flags, &idx, b->has_debug, 1);
  35362     bc_set_flags(&flags, &idx, b->backtrace_barrier, 1);
  35363     bc_set_flags(&flags, &idx, b->is_direct_or_indirect_eval, 1);
  35364     assert(idx <= 16);
  35365     bc_put_u16(s, flags);
  35366     bc_put_u8(s, b->js_mode);
  35367     bc_put_atom(s, b->func_name);
  35368 
  35369     bc_put_leb128(s, b->arg_count);
  35370     bc_put_leb128(s, b->var_count);
  35371     bc_put_leb128(s, b->defined_arg_count);
  35372     bc_put_leb128(s, b->stack_size);
  35373     bc_put_leb128(s, b->closure_var_count);
  35374     bc_put_leb128(s, b->cpool_count);
  35375     bc_put_leb128(s, b->byte_code_len);
  35376     if (b->vardefs) {
  35377         /* XXX: this field is redundant */
  35378         bc_put_leb128(s, b->arg_count + b->var_count);
  35379         for(i = 0; i < b->arg_count + b->var_count; i++) {
  35380             JSVarDef *vd = &b->vardefs[i];
  35381             bc_put_atom(s, vd->var_name);
  35382             bc_put_leb128(s, vd->scope_level);
  35383             bc_put_leb128(s, vd->scope_next + 1);
  35384             flags = idx = 0;
  35385             bc_set_flags(&flags, &idx, vd->var_kind, 4);
  35386             bc_set_flags(&flags, &idx, vd->is_const, 1);
  35387             bc_set_flags(&flags, &idx, vd->is_lexical, 1);
  35388             bc_set_flags(&flags, &idx, vd->is_captured, 1);
  35389             assert(idx <= 8);
  35390             bc_put_u8(s, flags);
  35391         }
  35392     } else {
  35393         bc_put_leb128(s, 0);
  35394     }
  35395 
  35396     for(i = 0; i < b->closure_var_count; i++) {
  35397         JSClosureVar *cv = &b->closure_var[i];
  35398         bc_put_atom(s, cv->var_name);
  35399         bc_put_leb128(s, cv->var_idx);
  35400         flags = idx = 0;
  35401         bc_set_flags(&flags, &idx, cv->is_local, 1);
  35402         bc_set_flags(&flags, &idx, cv->is_arg, 1);
  35403         bc_set_flags(&flags, &idx, cv->is_const, 1);
  35404         bc_set_flags(&flags, &idx, cv->is_lexical, 1);
  35405         bc_set_flags(&flags, &idx, cv->var_kind, 4);
  35406         assert(idx <= 8);
  35407         bc_put_u8(s, flags);
  35408     }
  35409 
  35410     if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len))
  35411         goto fail;
  35412 
  35413     if (b->has_debug) {
  35414         bc_put_atom(s, b->debug.filename);
  35415         bc_put_leb128(s, b->debug.line_num);
  35416         bc_put_leb128(s, b->debug.pc2line_len);
  35417         dbuf_put(&s->dbuf, b->debug.pc2line_buf, b->debug.pc2line_len);
  35418     }
  35419 
  35420     for(i = 0; i < b->cpool_count; i++) {
  35421         if (JS_WriteObjectRec(s, b->cpool[i]))
  35422             goto fail;
  35423     }
  35424     return 0;
  35425  fail:
  35426     return -1;
  35427 }
  35428 
  35429 static int JS_WriteModule(BCWriterState *s, JSValueConst obj)
  35430 {
  35431     JSModuleDef *m = JS_VALUE_GET_PTR(obj);
  35432     int i;
  35433 
  35434     bc_put_u8(s, BC_TAG_MODULE);
  35435     bc_put_atom(s, m->module_name);
  35436 
  35437     bc_put_leb128(s, m->req_module_entries_count);
  35438     for(i = 0; i < m->req_module_entries_count; i++) {
  35439         JSReqModuleEntry *rme = &m->req_module_entries[i];
  35440         bc_put_atom(s, rme->module_name);
  35441     }
  35442 
  35443     bc_put_leb128(s, m->export_entries_count);
  35444     for(i = 0; i < m->export_entries_count; i++) {
  35445         JSExportEntry *me = &m->export_entries[i];
  35446         bc_put_u8(s, me->export_type);
  35447         if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
  35448             bc_put_leb128(s, me->u.local.var_idx);
  35449         } else {
  35450             bc_put_leb128(s, me->u.req_module_idx);
  35451             bc_put_atom(s, me->local_name);
  35452         }
  35453         bc_put_atom(s, me->export_name);
  35454     }
  35455 
  35456     bc_put_leb128(s, m->star_export_entries_count);
  35457     for(i = 0; i < m->star_export_entries_count; i++) {
  35458         JSStarExportEntry *se = &m->star_export_entries[i];
  35459         bc_put_leb128(s, se->req_module_idx);
  35460     }
  35461 
  35462     bc_put_leb128(s, m->import_entries_count);
  35463     for(i = 0; i < m->import_entries_count; i++) {
  35464         JSImportEntry *mi = &m->import_entries[i];
  35465         bc_put_leb128(s, mi->var_idx);
  35466         bc_put_atom(s, mi->import_name);
  35467         bc_put_leb128(s, mi->req_module_idx);
  35468     }
  35469 
  35470     bc_put_u8(s, m->has_tla);
  35471 
  35472     if (JS_WriteObjectRec(s, m->func_obj))
  35473         goto fail;
  35474     return 0;
  35475  fail:
  35476     return -1;
  35477 }
  35478 
  35479 static int JS_WriteArray(BCWriterState *s, JSValueConst obj)
  35480 {
  35481     JSObject *p = JS_VALUE_GET_OBJ(obj);
  35482     uint32_t i, len;
  35483     JSValue val;
  35484     int ret;
  35485     BOOL is_template;
  35486 
  35487     if (s->allow_bytecode && !p->extensible) {
  35488         /* not extensible array: we consider it is a
  35489            template when we are saving bytecode */
  35490         bc_put_u8(s, BC_TAG_TEMPLATE_OBJECT);
  35491         is_template = TRUE;
  35492     } else {
  35493         bc_put_u8(s, BC_TAG_ARRAY);
  35494         is_template = FALSE;
  35495     }
  35496     if (js_get_length32(s->ctx, &len, obj))
  35497         goto fail1;
  35498     bc_put_leb128(s, len);
  35499     for(i = 0; i < len; i++) {
  35500         val = JS_GetPropertyUint32(s->ctx, obj, i);
  35501         if (JS_IsException(val))
  35502             goto fail1;
  35503         ret = JS_WriteObjectRec(s, val);
  35504         JS_FreeValue(s->ctx, val);
  35505         if (ret)
  35506             goto fail1;
  35507     }
  35508     if (is_template) {
  35509         val = JS_GetProperty(s->ctx, obj, JS_ATOM_raw);
  35510         if (JS_IsException(val))
  35511             goto fail1;
  35512         ret = JS_WriteObjectRec(s, val);
  35513         JS_FreeValue(s->ctx, val);
  35514         if (ret)
  35515             goto fail1;
  35516     }
  35517     return 0;
  35518  fail1:
  35519     return -1;
  35520 }
  35521 
  35522 static int JS_WriteObjectTag(BCWriterState *s, JSValueConst obj)
  35523 {
  35524     JSObject *p = JS_VALUE_GET_OBJ(obj);
  35525     uint32_t i, prop_count;
  35526     JSShape *sh;
  35527     JSShapeProperty *pr;
  35528     int pass;
  35529     JSAtom atom;
  35530 
  35531     bc_put_u8(s, BC_TAG_OBJECT);
  35532     prop_count = 0;
  35533     sh = p->shape;
  35534     for(pass = 0; pass < 2; pass++) {
  35535         if (pass == 1)
  35536             bc_put_leb128(s, prop_count);
  35537         for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {
  35538             atom = pr->atom;
  35539             if (atom != JS_ATOM_NULL &&
  35540                 JS_AtomIsString(s->ctx, atom) &&
  35541                 (pr->flags & JS_PROP_ENUMERABLE)) {
  35542                 if (pr->flags & JS_PROP_TMASK) {
  35543                     JS_ThrowTypeError(s->ctx, "only value properties are supported");
  35544                     goto fail;
  35545                 }
  35546                 if (pass == 0) {
  35547                     prop_count++;
  35548                 } else {
  35549                     bc_put_atom(s, atom);
  35550                     if (JS_WriteObjectRec(s, p->prop[i].u.value))
  35551                         goto fail;
  35552                 }
  35553             }
  35554         }
  35555     }
  35556     return 0;
  35557  fail:
  35558     return -1;
  35559 }
  35560 
  35561 static int JS_WriteTypedArray(BCWriterState *s, JSValueConst obj)
  35562 {
  35563     JSObject *p = JS_VALUE_GET_OBJ(obj);
  35564     JSTypedArray *ta = p->u.typed_array;
  35565 
  35566     bc_put_u8(s, BC_TAG_TYPED_ARRAY);
  35567     bc_put_u8(s, p->class_id - JS_CLASS_UINT8C_ARRAY);
  35568     bc_put_leb128(s, p->u.array.count);
  35569     bc_put_leb128(s, ta->offset);
  35570     if (JS_WriteObjectRec(s, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)))
  35571         return -1;
  35572     return 0;
  35573 }
  35574 
  35575 static int JS_WriteArrayBuffer(BCWriterState *s, JSValueConst obj)
  35576 {
  35577     JSObject *p = JS_VALUE_GET_OBJ(obj);
  35578     JSArrayBuffer *abuf = p->u.array_buffer;
  35579     if (abuf->detached) {
  35580         JS_ThrowTypeErrorDetachedArrayBuffer(s->ctx);
  35581         return -1;
  35582     }
  35583     bc_put_u8(s, BC_TAG_ARRAY_BUFFER);
  35584     bc_put_leb128(s, abuf->byte_length);
  35585     dbuf_put(&s->dbuf, abuf->data, abuf->byte_length);
  35586     return 0;
  35587 }
  35588 
  35589 static int JS_WriteSharedArrayBuffer(BCWriterState *s, JSValueConst obj)
  35590 {
  35591     JSObject *p = JS_VALUE_GET_OBJ(obj);
  35592     JSArrayBuffer *abuf = p->u.array_buffer;
  35593     assert(!abuf->detached); /* SharedArrayBuffer are never detached */
  35594     bc_put_u8(s, BC_TAG_SHARED_ARRAY_BUFFER);
  35595     bc_put_leb128(s, abuf->byte_length);
  35596     bc_put_u64(s, (uintptr_t)abuf->data);
  35597     if (js_resize_array(s->ctx, (void **)&s->sab_tab, sizeof(s->sab_tab[0]),
  35598                         &s->sab_tab_size, s->sab_tab_len + 1))
  35599         return -1;
  35600     /* keep the SAB pointer so that the user can clone it or free it */
  35601     s->sab_tab[s->sab_tab_len++] = abuf->data;
  35602     return 0;
  35603 }
  35604 
  35605 static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj)
  35606 {
  35607     uint32_t tag;
  35608 
  35609     if (js_check_stack_overflow(s->ctx->rt, 0)) {
  35610         JS_ThrowStackOverflow(s->ctx);
  35611         return -1;
  35612     }
  35613 
  35614     tag = JS_VALUE_GET_NORM_TAG(obj);
  35615     switch(tag) {
  35616     case JS_TAG_NULL:
  35617         bc_put_u8(s, BC_TAG_NULL);
  35618         break;
  35619     case JS_TAG_UNDEFINED:
  35620         bc_put_u8(s, BC_TAG_UNDEFINED);
  35621         break;
  35622     case JS_TAG_BOOL:
  35623         bc_put_u8(s, BC_TAG_BOOL_FALSE + JS_VALUE_GET_INT(obj));
  35624         break;
  35625     case JS_TAG_INT:
  35626         bc_put_u8(s, BC_TAG_INT32);
  35627         bc_put_sleb128(s, JS_VALUE_GET_INT(obj));
  35628         break;
  35629     case JS_TAG_FLOAT64:
  35630         {
  35631             JSFloat64Union u;
  35632             bc_put_u8(s, BC_TAG_FLOAT64);
  35633             u.d = JS_VALUE_GET_FLOAT64(obj);
  35634             bc_put_u64(s, u.u64);
  35635         }
  35636         break;
  35637     case JS_TAG_STRING:
  35638         {
  35639             JSString *p = JS_VALUE_GET_STRING(obj);
  35640             bc_put_u8(s, BC_TAG_STRING);
  35641             JS_WriteString(s, p);
  35642         }
  35643         break;
  35644     case JS_TAG_FUNCTION_BYTECODE:
  35645         if (!s->allow_bytecode)
  35646             goto invalid_tag;
  35647         if (JS_WriteFunctionTag(s, obj))
  35648             goto fail;
  35649         break;
  35650     case JS_TAG_MODULE:
  35651         if (!s->allow_bytecode)
  35652             goto invalid_tag;
  35653         if (JS_WriteModule(s, obj))
  35654             goto fail;
  35655         break;
  35656     case JS_TAG_OBJECT:
  35657         {
  35658             JSObject *p = JS_VALUE_GET_OBJ(obj);
  35659             int ret, idx;
  35660 
  35661             if (s->allow_reference) {
  35662                 idx = js_object_list_find(s->ctx, &s->object_list, p);
  35663                 if (idx >= 0) {
  35664                     bc_put_u8(s, BC_TAG_OBJECT_REFERENCE);
  35665                     bc_put_leb128(s, idx);
  35666                     break;
  35667                 } else {
  35668                     if (js_object_list_add(s->ctx, &s->object_list, p))
  35669                         goto fail;
  35670                 }
  35671             } else {
  35672                 if (p->tmp_mark) {
  35673                     JS_ThrowTypeError(s->ctx, "circular reference");
  35674                     goto fail;
  35675                 }
  35676                 p->tmp_mark = 1;
  35677             }
  35678             switch(p->class_id) {
  35679             case JS_CLASS_ARRAY:
  35680                 ret = JS_WriteArray(s, obj);
  35681                 break;
  35682             case JS_CLASS_OBJECT:
  35683                 ret = JS_WriteObjectTag(s, obj);
  35684                 break;
  35685             case JS_CLASS_ARRAY_BUFFER:
  35686                 ret = JS_WriteArrayBuffer(s, obj);
  35687                 break;
  35688             case JS_CLASS_SHARED_ARRAY_BUFFER:
  35689                 if (!s->allow_sab)
  35690                     goto invalid_tag;
  35691                 ret = JS_WriteSharedArrayBuffer(s, obj);
  35692                 break;
  35693             case JS_CLASS_DATE:
  35694                 bc_put_u8(s, BC_TAG_DATE);
  35695                 ret = JS_WriteObjectRec(s, p->u.object_data);
  35696                 break;
  35697             case JS_CLASS_NUMBER:
  35698             case JS_CLASS_STRING:
  35699             case JS_CLASS_BOOLEAN:
  35700             case JS_CLASS_BIG_INT:
  35701 #ifdef CONFIG_BIGNUM
  35702             case JS_CLASS_BIG_FLOAT:
  35703             case JS_CLASS_BIG_DECIMAL:
  35704 #endif
  35705                 bc_put_u8(s, BC_TAG_OBJECT_VALUE);
  35706                 ret = JS_WriteObjectRec(s, p->u.object_data);
  35707                 break;
  35708             default:
  35709                 if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
  35710                     p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
  35711                     ret = JS_WriteTypedArray(s, obj);
  35712                 } else {
  35713                     JS_ThrowTypeError(s->ctx, "unsupported object class");
  35714                     ret = -1;
  35715                 }
  35716                 break;
  35717             }
  35718             p->tmp_mark = 0;
  35719             if (ret)
  35720                 goto fail;
  35721         }
  35722         break;
  35723     case JS_TAG_BIG_INT:
  35724 #ifdef CONFIG_BIGNUM
  35725     case JS_TAG_BIG_FLOAT:
  35726     case JS_TAG_BIG_DECIMAL:
  35727 #endif
  35728         if (JS_WriteBigNum(s, obj))
  35729             goto fail;
  35730         break;
  35731     default:
  35732     invalid_tag:
  35733         JS_ThrowInternalError(s->ctx, "unsupported tag (%d)", tag);
  35734         goto fail;
  35735     }
  35736     return 0;
  35737 
  35738  fail:
  35739     return -1;
  35740 }
  35741 
  35742 /* create the atom table */
  35743 static int JS_WriteObjectAtoms(BCWriterState *s)
  35744 {
  35745     JSRuntime *rt = s->ctx->rt;
  35746     DynBuf dbuf1;
  35747     int i, atoms_size;
  35748 
  35749     dbuf1 = s->dbuf;
  35750     js_dbuf_init(s->ctx, &s->dbuf);
  35751     bc_put_u8(s, BC_VERSION);
  35752 
  35753     bc_put_leb128(s, s->idx_to_atom_count);
  35754     for(i = 0; i < s->idx_to_atom_count; i++) {
  35755         JSAtomStruct *p = rt->atom_array[s->idx_to_atom[i]];
  35756         JS_WriteString(s, p);
  35757     }
  35758     /* XXX: should check for OOM in above phase */
  35759 
  35760     /* move the atoms at the start */
  35761     /* XXX: could just append dbuf1 data, but it uses more memory if
  35762        dbuf1 is larger than dbuf */
  35763     atoms_size = s->dbuf.size;
  35764     if (dbuf_realloc(&dbuf1, dbuf1.size + atoms_size))
  35765         goto fail;
  35766     memmove(dbuf1.buf + atoms_size, dbuf1.buf, dbuf1.size);
  35767     memcpy(dbuf1.buf, s->dbuf.buf, atoms_size);
  35768     dbuf1.size += atoms_size;
  35769     dbuf_free(&s->dbuf);
  35770     s->dbuf = dbuf1;
  35771     return 0;
  35772  fail:
  35773     dbuf_free(&dbuf1);
  35774     return -1;
  35775 }
  35776 
  35777 uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj,
  35778                          int flags, uint8_t ***psab_tab, size_t *psab_tab_len)
  35779 {
  35780     BCWriterState ss, *s = &ss;
  35781 
  35782     memset(s, 0, sizeof(*s));
  35783     s->ctx = ctx;
  35784     s->allow_bytecode = ((flags & JS_WRITE_OBJ_BYTECODE) != 0);
  35785     s->allow_sab = ((flags & JS_WRITE_OBJ_SAB) != 0);
  35786     s->allow_reference = ((flags & JS_WRITE_OBJ_REFERENCE) != 0);
  35787     /* XXX: could use a different version when bytecode is included */
  35788     if (s->allow_bytecode)
  35789         s->first_atom = JS_ATOM_END;
  35790     else
  35791         s->first_atom = 1;
  35792     js_dbuf_init(ctx, &s->dbuf);
  35793     js_object_list_init(&s->object_list);
  35794 
  35795     if (JS_WriteObjectRec(s, obj))
  35796         goto fail;
  35797     if (JS_WriteObjectAtoms(s))
  35798         goto fail;
  35799     js_object_list_end(ctx, &s->object_list);
  35800     js_free(ctx, s->atom_to_idx);
  35801     js_free(ctx, s->idx_to_atom);
  35802     *psize = s->dbuf.size;
  35803     if (psab_tab)
  35804         *psab_tab = s->sab_tab;
  35805     if (psab_tab_len)
  35806         *psab_tab_len = s->sab_tab_len;
  35807     return s->dbuf.buf;
  35808  fail:
  35809     js_object_list_end(ctx, &s->object_list);
  35810     js_free(ctx, s->atom_to_idx);
  35811     js_free(ctx, s->idx_to_atom);
  35812     dbuf_free(&s->dbuf);
  35813     *psize = 0;
  35814     if (psab_tab)
  35815         *psab_tab = NULL;
  35816     if (psab_tab_len)
  35817         *psab_tab_len = 0;
  35818     return NULL;
  35819 }
  35820 
  35821 uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj,
  35822                         int flags)
  35823 {
  35824     return JS_WriteObject2(ctx, psize, obj, flags, NULL, NULL);
  35825 }
  35826 
  35827 typedef struct BCReaderState {
  35828     JSContext *ctx;
  35829     const uint8_t *buf_start, *ptr, *buf_end;
  35830     uint32_t first_atom;
  35831     uint32_t idx_to_atom_count;
  35832     JSAtom *idx_to_atom;
  35833     int error_state;
  35834     BOOL allow_sab : 8;
  35835     BOOL allow_bytecode : 8;
  35836     BOOL is_rom_data : 8;
  35837     BOOL allow_reference : 8;
  35838     /* object references */
  35839     JSObject **objects;
  35840     int objects_count;
  35841     int objects_size;
  35842 
  35843 #ifdef DUMP_READ_OBJECT
  35844     const uint8_t *ptr_last;
  35845     int level;
  35846 #endif
  35847 } BCReaderState;
  35848 
  35849 #ifdef DUMP_READ_OBJECT
  35850 static void __attribute__((format(printf, 2, 3))) bc_read_trace(BCReaderState *s, const char *fmt, ...) {
  35851     va_list ap;
  35852     int i, n, n0;
  35853 
  35854     if (!s->ptr_last)
  35855         s->ptr_last = s->buf_start;
  35856 
  35857     n = n0 = 0;
  35858     if (s->ptr > s->ptr_last || s->ptr == s->buf_start) {
  35859         n0 = printf("%04x: ", (int)(s->ptr_last - s->buf_start));
  35860         n += n0;
  35861     }
  35862     for (i = 0; s->ptr_last < s->ptr; i++) {
  35863         if ((i & 7) == 0 && i > 0) {
  35864             printf("\n%*s", n0, "");
  35865             n = n0;
  35866         }
  35867         n += printf(" %02x", *s->ptr_last++);
  35868     }
  35869     if (*fmt == '}')
  35870         s->level--;
  35871     if (n < 32 + s->level * 2) {
  35872         printf("%*s", 32 + s->level * 2 - n, "");
  35873     }
  35874     va_start(ap, fmt);
  35875     vfprintf(stdout, fmt, ap);
  35876     va_end(ap);
  35877     if (strchr(fmt, '{'))
  35878         s->level++;
  35879 }
  35880 #else
  35881 #define bc_read_trace(...)
  35882 #endif
  35883 
  35884 static int bc_read_error_end(BCReaderState *s)
  35885 {
  35886     if (!s->error_state) {
  35887         JS_ThrowSyntaxError(s->ctx, "read after the end of the buffer");
  35888     }
  35889     return s->error_state = -1;
  35890 }
  35891 
  35892 static int bc_get_u8(BCReaderState *s, uint8_t *pval)
  35893 {
  35894     if (unlikely(s->buf_end - s->ptr < 1)) {
  35895         *pval = 0; /* avoid warning */
  35896         return bc_read_error_end(s);
  35897     }
  35898     *pval = *s->ptr++;
  35899     return 0;
  35900 }
  35901 
  35902 static int bc_get_u16(BCReaderState *s, uint16_t *pval)
  35903 {
  35904     uint16_t v;
  35905     if (unlikely(s->buf_end - s->ptr < 2)) {
  35906         *pval = 0; /* avoid warning */
  35907         return bc_read_error_end(s);
  35908     }
  35909     v = get_u16(s->ptr);
  35910     if (is_be())
  35911         v = bswap16(v);
  35912     *pval = v;
  35913     s->ptr += 2;
  35914     return 0;
  35915 }
  35916 
  35917 static __maybe_unused int bc_get_u32(BCReaderState *s, uint32_t *pval)
  35918 {
  35919     uint32_t v;
  35920     if (unlikely(s->buf_end - s->ptr < 4)) {
  35921         *pval = 0; /* avoid warning */
  35922         return bc_read_error_end(s);
  35923     }
  35924     v = get_u32(s->ptr);
  35925     if (is_be())
  35926         v = bswap32(v);
  35927     *pval = v;
  35928     s->ptr += 4;
  35929     return 0;
  35930 }
  35931 
  35932 static int bc_get_u64(BCReaderState *s, uint64_t *pval)
  35933 {
  35934     uint64_t v;
  35935     if (unlikely(s->buf_end - s->ptr < 8)) {
  35936         *pval = 0; /* avoid warning */
  35937         return bc_read_error_end(s);
  35938     }
  35939     v = get_u64(s->ptr);
  35940     if (is_be())
  35941         v = bswap64(v);
  35942     *pval = v;
  35943     s->ptr += 8;
  35944     return 0;
  35945 }
  35946 
  35947 static int bc_get_leb128(BCReaderState *s, uint32_t *pval)
  35948 {
  35949     int ret;
  35950     ret = get_leb128(pval, s->ptr, s->buf_end);
  35951     if (unlikely(ret < 0))
  35952         return bc_read_error_end(s);
  35953     s->ptr += ret;
  35954     return 0;
  35955 }
  35956 
  35957 static int bc_get_sleb128(BCReaderState *s, int32_t *pval)
  35958 {
  35959     int ret;
  35960     ret = get_sleb128(pval, s->ptr, s->buf_end);
  35961     if (unlikely(ret < 0))
  35962         return bc_read_error_end(s);
  35963     s->ptr += ret;
  35964     return 0;
  35965 }
  35966 
  35967 /* XXX: used to read an `int` with a positive value */
  35968 static int bc_get_leb128_int(BCReaderState *s, int *pval)
  35969 {
  35970     return bc_get_leb128(s, (uint32_t *)pval);
  35971 }
  35972 
  35973 static int bc_get_leb128_u16(BCReaderState *s, uint16_t *pval)
  35974 {
  35975     uint32_t val;
  35976     if (bc_get_leb128(s, &val)) {
  35977         *pval = 0;
  35978         return -1;
  35979     }
  35980     *pval = val;
  35981     return 0;
  35982 }
  35983 
  35984 static int bc_get_buf(BCReaderState *s, uint8_t *buf, uint32_t buf_len)
  35985 {
  35986     if (buf_len != 0) {
  35987         if (unlikely(!buf || s->buf_end - s->ptr < buf_len))
  35988             return bc_read_error_end(s);
  35989         memcpy(buf, s->ptr, buf_len);
  35990         s->ptr += buf_len;
  35991     }
  35992     return 0;
  35993 }
  35994 
  35995 static int bc_idx_to_atom(BCReaderState *s, JSAtom *patom, uint32_t idx)
  35996 {
  35997     JSAtom atom;
  35998 
  35999     if (__JS_AtomIsTaggedInt(idx)) {
  36000         atom = idx;
  36001     } else if (idx < s->first_atom) {
  36002         atom = JS_DupAtom(s->ctx, idx);
  36003     } else {
  36004         idx -= s->first_atom;
  36005         if (idx >= s->idx_to_atom_count) {
  36006             JS_ThrowSyntaxError(s->ctx, "invalid atom index (pos=%u)",
  36007                                 (unsigned int)(s->ptr - s->buf_start));
  36008             *patom = JS_ATOM_NULL;
  36009             return s->error_state = -1;
  36010         }
  36011         atom = JS_DupAtom(s->ctx, s->idx_to_atom[idx]);
  36012     }
  36013     *patom = atom;
  36014     return 0;
  36015 }
  36016 
  36017 static int bc_get_atom(BCReaderState *s, JSAtom *patom)
  36018 {
  36019     uint32_t v;
  36020     if (bc_get_leb128(s, &v))
  36021         return -1;
  36022     if (v & 1) {
  36023         *patom = __JS_AtomFromUInt32(v >> 1);
  36024         return 0;
  36025     } else {
  36026         return bc_idx_to_atom(s, patom, v >> 1);
  36027     }
  36028 }
  36029 
  36030 static JSString *JS_ReadString(BCReaderState *s)
  36031 {
  36032     uint32_t len;
  36033     size_t size;
  36034     BOOL is_wide_char;
  36035     JSString *p;
  36036 
  36037     if (bc_get_leb128(s, &len))
  36038         return NULL;
  36039     is_wide_char = len & 1;
  36040     len >>= 1;
  36041     p = js_alloc_string(s->ctx, len, is_wide_char);
  36042     if (!p) {
  36043         s->error_state = -1;
  36044         return NULL;
  36045     }
  36046     size = (size_t)len << is_wide_char;
  36047     if ((s->buf_end - s->ptr) < size) {
  36048         bc_read_error_end(s);
  36049         js_free_string(s->ctx->rt, p);
  36050         return NULL;
  36051     }
  36052     memcpy(p->u.str8, s->ptr, size);
  36053     s->ptr += size;
  36054     if (is_wide_char) {
  36055         if (is_be()) {
  36056             uint32_t i;
  36057             for (i = 0; i < len; i++)
  36058                 p->u.str16[i] = bswap16(p->u.str16[i]);
  36059         }
  36060     } else {
  36061         p->u.str8[size] = '\0'; /* add the trailing zero for 8 bit strings */
  36062     }
  36063 #ifdef DUMP_READ_OBJECT
  36064     JS_DumpString(s->ctx->rt, p); printf("\n");
  36065 #endif
  36066     return p;
  36067 }
  36068 
  36069 static uint32_t bc_get_flags(uint32_t flags, int *pidx, int n)
  36070 {
  36071     uint32_t val;
  36072     /* XXX: this does not work for n == 32 */
  36073     val = (flags >> *pidx) & ((1U << n) - 1);
  36074     *pidx += n;
  36075     return val;
  36076 }
  36077 
  36078 static int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b,
  36079                                    int byte_code_offset, uint32_t bc_len)
  36080 {
  36081     uint8_t *bc_buf;
  36082     int pos, len, op;
  36083     JSAtom atom;
  36084     uint32_t idx;
  36085 
  36086     if (s->is_rom_data) {
  36087         /* directly use the input buffer */
  36088         if (unlikely(s->buf_end - s->ptr < bc_len))
  36089             return bc_read_error_end(s);
  36090         bc_buf = (uint8_t *)s->ptr;
  36091         s->ptr += bc_len;
  36092     } else {
  36093         bc_buf = (void *)((uint8_t*)b + byte_code_offset);
  36094         if (bc_get_buf(s, bc_buf, bc_len))
  36095             return -1;
  36096     }
  36097     b->byte_code_buf = bc_buf;
  36098 
  36099     if (is_be())
  36100         bc_byte_swap(bc_buf, bc_len);
  36101 
  36102     pos = 0;
  36103     while (pos < bc_len) {
  36104         op = bc_buf[pos];
  36105         len = short_opcode_info(op).size;
  36106         switch(short_opcode_info(op).fmt) {
  36107         case OP_FMT_atom:
  36108         case OP_FMT_atom_u8:
  36109         case OP_FMT_atom_u16:
  36110         case OP_FMT_atom_label_u8:
  36111         case OP_FMT_atom_label_u16:
  36112             idx = get_u32(bc_buf + pos + 1);
  36113             if (s->is_rom_data) {
  36114                 /* just increment the reference count of the atom */
  36115                 JS_DupAtom(s->ctx, (JSAtom)idx);
  36116             } else {
  36117                 if (bc_idx_to_atom(s, &atom, idx)) {
  36118                     /* Note: the atoms will be freed up to this position */
  36119                     b->byte_code_len = pos;
  36120                     return -1;
  36121                 }
  36122                 put_u32(bc_buf + pos + 1, atom);
  36123 #ifdef DUMP_READ_OBJECT
  36124                 bc_read_trace(s, "at %d, fixup atom: ", pos + 1); print_atom(s->ctx, atom); printf("\n");
  36125 #endif
  36126             }
  36127             break;
  36128         default:
  36129             break;
  36130         }
  36131         pos += len;
  36132     }
  36133     return 0;
  36134 }
  36135 
  36136 static JSValue JS_ReadBigNum(BCReaderState *s, int tag)
  36137 {
  36138     JSValue obj = JS_UNDEFINED;
  36139     uint8_t v8;
  36140     int32_t e;
  36141     uint32_t len;
  36142     limb_t l, i, n;
  36143     JSBigFloat *p;
  36144     limb_t v;
  36145     bf_t *a;
  36146 
  36147     p = js_new_bf(s->ctx);
  36148     if (!p)
  36149         goto fail;
  36150     switch(tag) {
  36151     case BC_TAG_BIG_INT:
  36152         obj = JS_MKPTR(JS_TAG_BIG_INT, p);
  36153         break;
  36154 #ifdef CONFIG_BIGNUM
  36155     case BC_TAG_BIG_FLOAT:
  36156         obj = JS_MKPTR(JS_TAG_BIG_FLOAT, p);
  36157         break;
  36158     case BC_TAG_BIG_DECIMAL:
  36159         obj = JS_MKPTR(JS_TAG_BIG_DECIMAL, p);
  36160         break;
  36161 #endif
  36162     default:
  36163         abort();
  36164     }
  36165 
  36166     /* sign + exponent */
  36167     if (bc_get_sleb128(s, &e))
  36168         goto fail;
  36169 
  36170     a = &p->num;
  36171     a->sign = e & 1;
  36172     e >>= 1;
  36173     if (e == 0)
  36174         a->expn = BF_EXP_ZERO;
  36175     else if (e == 1)
  36176         a->expn = BF_EXP_INF;
  36177     else if (e == 2)
  36178         a->expn = BF_EXP_NAN;
  36179     else if (e >= 3)
  36180         a->expn = e - 3;
  36181     else
  36182         a->expn = e;
  36183 
  36184     /* mantissa */
  36185     if (a->expn != BF_EXP_ZERO &&
  36186         a->expn != BF_EXP_INF &&
  36187         a->expn != BF_EXP_NAN) {
  36188         if (bc_get_leb128(s, &len))
  36189             goto fail;
  36190         bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len);
  36191         if (len == 0) {
  36192             JS_ThrowInternalError(s->ctx, "invalid bignum length");
  36193             goto fail;
  36194         }
  36195 #ifdef CONFIG_BIGNUM
  36196         if (tag == BC_TAG_BIG_DECIMAL) {
  36197             l = (len + LIMB_DIGITS - 1) / LIMB_DIGITS;
  36198         } else
  36199 #endif
  36200         {
  36201             l = (len + sizeof(limb_t) - 1) / sizeof(limb_t);
  36202         }
  36203         if (bf_resize(a, l)) {
  36204             JS_ThrowOutOfMemory(s->ctx);
  36205             goto fail;
  36206         }
  36207 #ifdef CONFIG_BIGNUM
  36208         if (tag == BC_TAG_BIG_DECIMAL) {
  36209             limb_t j;
  36210             int bpos, d;
  36211 
  36212             bpos = 0;
  36213             for(i = 0; i < l; i++) {
  36214                 if (i == 0 && (n = len % LIMB_DIGITS) != 0) {
  36215                     j = LIMB_DIGITS - n;
  36216                 } else {
  36217                     j = 0;
  36218                 }
  36219                 v = 0;
  36220                 for(; j < LIMB_DIGITS; j++) {
  36221                     if (bpos == 0) {
  36222                         if (bc_get_u8(s, &v8))
  36223                             goto fail;
  36224                         d = v8 & 0xf;
  36225                         bpos = 1;
  36226                     } else {
  36227                         d = v8 >> 4;
  36228                         bpos = 0;
  36229                     }
  36230                     if (d >= 10) {
  36231                         JS_ThrowInternalError(s->ctx, "invalid digit");
  36232                         goto fail;
  36233                     }
  36234                     v += mp_pow_dec[j] * d;
  36235                 }
  36236                 a->tab[i] = v;
  36237             }
  36238         } else
  36239 #endif  /* CONFIG_BIGNUM */
  36240         {
  36241             n = len & (sizeof(limb_t) - 1);
  36242             if (n != 0) {
  36243                 v = 0;
  36244                 for(i = 0; i < n; i++) {
  36245                     if (bc_get_u8(s, &v8))
  36246                         goto fail;
  36247                     v |= (limb_t)v8 << ((sizeof(limb_t) - n + i) * 8);
  36248                 }
  36249                 a->tab[0] = v;
  36250                 i = 1;
  36251             } else {
  36252                 i = 0;
  36253             }
  36254             for(; i < l; i++) {
  36255 #if LIMB_BITS == 32
  36256                 if (bc_get_u32(s, &v))
  36257                     goto fail;
  36258 #else
  36259                 if (bc_get_u64(s, &v))
  36260                     goto fail;
  36261 #endif
  36262                 a->tab[i] = v;
  36263             }
  36264         }
  36265     }
  36266     bc_read_trace(s, "}\n");
  36267     return obj;
  36268  fail:
  36269     JS_FreeValue(s->ctx, obj);
  36270     return JS_EXCEPTION;
  36271 }
  36272 
  36273 static JSValue JS_ReadObjectRec(BCReaderState *s);
  36274 
  36275 static int BC_add_object_ref1(BCReaderState *s, JSObject *p)
  36276 {
  36277     if (s->allow_reference) {
  36278         if (js_resize_array(s->ctx, (void *)&s->objects,
  36279                             sizeof(s->objects[0]),
  36280                             &s->objects_size, s->objects_count + 1))
  36281             return -1;
  36282         s->objects[s->objects_count++] = p;
  36283     }
  36284     return 0;
  36285 }
  36286 
  36287 static int BC_add_object_ref(BCReaderState *s, JSValueConst obj)
  36288 {
  36289     return BC_add_object_ref1(s, JS_VALUE_GET_OBJ(obj));
  36290 }
  36291 
  36292 static JSValue JS_ReadFunctionTag(BCReaderState *s)
  36293 {
  36294     JSContext *ctx = s->ctx;
  36295     JSFunctionBytecode bc, *b;
  36296     JSValue obj = JS_UNDEFINED;
  36297     uint16_t v16;
  36298     uint8_t v8;
  36299     int idx, i, local_count;
  36300     int function_size, cpool_offset, byte_code_offset;
  36301     int closure_var_offset, vardefs_offset;
  36302 
  36303     memset(&bc, 0, sizeof(bc));
  36304     bc.header.ref_count = 1;
  36305     //bc.gc_header.mark = 0;
  36306 
  36307     if (bc_get_u16(s, &v16))
  36308         goto fail;
  36309     idx = 0;
  36310     bc.has_prototype = bc_get_flags(v16, &idx, 1);
  36311     bc.has_simple_parameter_list = bc_get_flags(v16, &idx, 1);
  36312     bc.is_derived_class_constructor = bc_get_flags(v16, &idx, 1);
  36313     bc.need_home_object = bc_get_flags(v16, &idx, 1);
  36314     bc.func_kind = bc_get_flags(v16, &idx, 2);
  36315     bc.new_target_allowed = bc_get_flags(v16, &idx, 1);
  36316     bc.super_call_allowed = bc_get_flags(v16, &idx, 1);
  36317     bc.super_allowed = bc_get_flags(v16, &idx, 1);
  36318     bc.arguments_allowed = bc_get_flags(v16, &idx, 1);
  36319     bc.has_debug = bc_get_flags(v16, &idx, 1);
  36320     bc.backtrace_barrier = bc_get_flags(v16, &idx, 1);
  36321     bc.is_direct_or_indirect_eval = bc_get_flags(v16, &idx, 1);
  36322     bc.read_only_bytecode = s->is_rom_data;
  36323     if (bc_get_u8(s, &v8))
  36324         goto fail;
  36325     bc.js_mode = v8;
  36326     if (bc_get_atom(s, &bc.func_name))  //@ atom leak if failure
  36327         goto fail;
  36328     if (bc_get_leb128_u16(s, &bc.arg_count))
  36329         goto fail;
  36330     if (bc_get_leb128_u16(s, &bc.var_count))
  36331         goto fail;
  36332     if (bc_get_leb128_u16(s, &bc.defined_arg_count))
  36333         goto fail;
  36334     if (bc_get_leb128_u16(s, &bc.stack_size))
  36335         goto fail;
  36336     if (bc_get_leb128_int(s, &bc.closure_var_count))
  36337         goto fail;
  36338     if (bc_get_leb128_int(s, &bc.cpool_count))
  36339         goto fail;
  36340     if (bc_get_leb128_int(s, &bc.byte_code_len))
  36341         goto fail;
  36342     if (bc_get_leb128_int(s, &local_count))
  36343         goto fail;
  36344 
  36345     if (bc.has_debug) {
  36346         function_size = sizeof(*b);
  36347     } else {
  36348         function_size = offsetof(JSFunctionBytecode, debug);
  36349     }
  36350     cpool_offset = function_size;
  36351     function_size += bc.cpool_count * sizeof(*bc.cpool);
  36352     vardefs_offset = function_size;
  36353     function_size += local_count * sizeof(*bc.vardefs);
  36354     closure_var_offset = function_size;
  36355     function_size += bc.closure_var_count * sizeof(*bc.closure_var);
  36356     byte_code_offset = function_size;
  36357     if (!bc.read_only_bytecode) {
  36358         function_size += bc.byte_code_len;
  36359     }
  36360 
  36361     b = js_mallocz(ctx, function_size);
  36362     if (!b)
  36363         return JS_EXCEPTION;
  36364 
  36365     memcpy(b, &bc, offsetof(JSFunctionBytecode, debug));
  36366     b->header.ref_count = 1;
  36367     if (local_count != 0) {
  36368         b->vardefs = (void *)((uint8_t*)b + vardefs_offset);
  36369     }
  36370     if (b->closure_var_count != 0) {
  36371         b->closure_var = (void *)((uint8_t*)b + closure_var_offset);
  36372     }
  36373     if (b->cpool_count != 0) {
  36374         b->cpool = (void *)((uint8_t*)b + cpool_offset);
  36375     }
  36376 
  36377     add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
  36378 
  36379     obj = JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b);
  36380 
  36381 #ifdef DUMP_READ_OBJECT
  36382     bc_read_trace(s, "name: "); print_atom(s->ctx, b->func_name); printf("\n");
  36383 #endif
  36384     bc_read_trace(s, "args=%d vars=%d defargs=%d closures=%d cpool=%d\n",
  36385                   b->arg_count, b->var_count, b->defined_arg_count,
  36386                   b->closure_var_count, b->cpool_count);
  36387     bc_read_trace(s, "stack=%d bclen=%d locals=%d\n",
  36388                   b->stack_size, b->byte_code_len, local_count);
  36389 
  36390     if (local_count != 0) {
  36391         bc_read_trace(s, "vars {\n");
  36392         for(i = 0; i < local_count; i++) {
  36393             JSVarDef *vd = &b->vardefs[i];
  36394             if (bc_get_atom(s, &vd->var_name))
  36395                 goto fail;
  36396             if (bc_get_leb128_int(s, &vd->scope_level))
  36397                 goto fail;
  36398             if (bc_get_leb128_int(s, &vd->scope_next))
  36399                 goto fail;
  36400             vd->scope_next--;
  36401             if (bc_get_u8(s, &v8))
  36402                 goto fail;
  36403             idx = 0;
  36404             vd->var_kind = bc_get_flags(v8, &idx, 4);
  36405             vd->is_const = bc_get_flags(v8, &idx, 1);
  36406             vd->is_lexical = bc_get_flags(v8, &idx, 1);
  36407             vd->is_captured = bc_get_flags(v8, &idx, 1);
  36408 #ifdef DUMP_READ_OBJECT
  36409             bc_read_trace(s, "name: "); print_atom(s->ctx, vd->var_name); printf("\n");
  36410 #endif
  36411         }
  36412         bc_read_trace(s, "}\n");
  36413     }
  36414     if (b->closure_var_count != 0) {
  36415         bc_read_trace(s, "closure vars {\n");
  36416         for(i = 0; i < b->closure_var_count; i++) {
  36417             JSClosureVar *cv = &b->closure_var[i];
  36418             int var_idx;
  36419             if (bc_get_atom(s, &cv->var_name))
  36420                 goto fail;
  36421             if (bc_get_leb128_int(s, &var_idx))
  36422                 goto fail;
  36423             cv->var_idx = var_idx;
  36424             if (bc_get_u8(s, &v8))
  36425                 goto fail;
  36426             idx = 0;
  36427             cv->is_local = bc_get_flags(v8, &idx, 1);
  36428             cv->is_arg = bc_get_flags(v8, &idx, 1);
  36429             cv->is_const = bc_get_flags(v8, &idx, 1);
  36430             cv->is_lexical = bc_get_flags(v8, &idx, 1);
  36431             cv->var_kind = bc_get_flags(v8, &idx, 4);
  36432 #ifdef DUMP_READ_OBJECT
  36433             bc_read_trace(s, "name: "); print_atom(s->ctx, cv->var_name); printf("\n");
  36434 #endif
  36435         }
  36436         bc_read_trace(s, "}\n");
  36437     }
  36438     {
  36439         bc_read_trace(s, "bytecode {\n");
  36440         if (JS_ReadFunctionBytecode(s, b, byte_code_offset, b->byte_code_len))
  36441             goto fail;
  36442         bc_read_trace(s, "}\n");
  36443     }
  36444     if (b->has_debug) {
  36445         /* read optional debug information */
  36446         bc_read_trace(s, "debug {\n");
  36447         if (bc_get_atom(s, &b->debug.filename))
  36448             goto fail;
  36449         if (bc_get_leb128_int(s, &b->debug.line_num))
  36450             goto fail;
  36451         if (bc_get_leb128_int(s, &b->debug.pc2line_len))
  36452             goto fail;
  36453         if (b->debug.pc2line_len) {
  36454             b->debug.pc2line_buf = js_mallocz(ctx, b->debug.pc2line_len);
  36455             if (!b->debug.pc2line_buf)
  36456                 goto fail;
  36457             if (bc_get_buf(s, b->debug.pc2line_buf, b->debug.pc2line_len))
  36458                 goto fail;
  36459         }
  36460 #ifdef DUMP_READ_OBJECT
  36461         bc_read_trace(s, "filename: "); print_atom(s->ctx, b->debug.filename); printf("\n");
  36462 #endif
  36463         bc_read_trace(s, "}\n");
  36464     }
  36465     if (b->cpool_count != 0) {
  36466         bc_read_trace(s, "cpool {\n");
  36467         for(i = 0; i < b->cpool_count; i++) {
  36468             JSValue val;
  36469             val = JS_ReadObjectRec(s);
  36470             if (JS_IsException(val))
  36471                 goto fail;
  36472             b->cpool[i] = val;
  36473         }
  36474         bc_read_trace(s, "}\n");
  36475     }
  36476     b->realm = JS_DupContext(ctx);
  36477     return obj;
  36478  fail:
  36479     JS_FreeValue(ctx, obj);
  36480     return JS_EXCEPTION;
  36481 }
  36482 
  36483 static JSValue JS_ReadModule(BCReaderState *s)
  36484 {
  36485     JSContext *ctx = s->ctx;
  36486     JSValue obj;
  36487     JSModuleDef *m = NULL;
  36488     JSAtom module_name;
  36489     int i;
  36490     uint8_t v8;
  36491 
  36492     if (bc_get_atom(s, &module_name))
  36493         goto fail;
  36494 #ifdef DUMP_READ_OBJECT
  36495     bc_read_trace(s, "name: "); print_atom(s->ctx, module_name); printf("\n");
  36496 #endif
  36497     m = js_new_module_def(ctx, module_name);
  36498     if (!m)
  36499         goto fail;
  36500     obj = JS_NewModuleValue(ctx, m);
  36501     if (bc_get_leb128_int(s, &m->req_module_entries_count))
  36502         goto fail;
  36503     if (m->req_module_entries_count != 0) {
  36504         m->req_module_entries_size = m->req_module_entries_count;
  36505         m->req_module_entries = js_mallocz(ctx, sizeof(m->req_module_entries[0]) * m->req_module_entries_size);
  36506         if (!m->req_module_entries)
  36507             goto fail;
  36508         for(i = 0; i < m->req_module_entries_count; i++) {
  36509             JSReqModuleEntry *rme = &m->req_module_entries[i];
  36510             if (bc_get_atom(s, &rme->module_name))
  36511                 goto fail;
  36512         }
  36513     }
  36514 
  36515     if (bc_get_leb128_int(s, &m->export_entries_count))
  36516         goto fail;
  36517     if (m->export_entries_count != 0) {
  36518         m->export_entries_size = m->export_entries_count;
  36519         m->export_entries = js_mallocz(ctx, sizeof(m->export_entries[0]) * m->export_entries_size);
  36520         if (!m->export_entries)
  36521             goto fail;
  36522         for(i = 0; i < m->export_entries_count; i++) {
  36523             JSExportEntry *me = &m->export_entries[i];
  36524             if (bc_get_u8(s, &v8))
  36525                 goto fail;
  36526             me->export_type = v8;
  36527             if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
  36528                 if (bc_get_leb128_int(s, &me->u.local.var_idx))
  36529                     goto fail;
  36530             } else {
  36531                 if (bc_get_leb128_int(s, &me->u.req_module_idx))
  36532                     goto fail;
  36533                 if (bc_get_atom(s, &me->local_name))
  36534                     goto fail;
  36535             }
  36536             if (bc_get_atom(s, &me->export_name))
  36537                 goto fail;
  36538         }
  36539     }
  36540 
  36541     if (bc_get_leb128_int(s, &m->star_export_entries_count))
  36542         goto fail;
  36543     if (m->star_export_entries_count != 0) {
  36544         m->star_export_entries_size = m->star_export_entries_count;
  36545         m->star_export_entries = js_mallocz(ctx, sizeof(m->star_export_entries[0]) * m->star_export_entries_size);
  36546         if (!m->star_export_entries)
  36547             goto fail;
  36548         for(i = 0; i < m->star_export_entries_count; i++) {
  36549             JSStarExportEntry *se = &m->star_export_entries[i];
  36550             if (bc_get_leb128_int(s, &se->req_module_idx))
  36551                 goto fail;
  36552         }
  36553     }
  36554 
  36555     if (bc_get_leb128_int(s, &m->import_entries_count))
  36556         goto fail;
  36557     if (m->import_entries_count != 0) {
  36558         m->import_entries_size = m->import_entries_count;
  36559         m->import_entries = js_mallocz(ctx, sizeof(m->import_entries[0]) * m->import_entries_size);
  36560         if (!m->import_entries)
  36561             goto fail;
  36562         for(i = 0; i < m->import_entries_count; i++) {
  36563             JSImportEntry *mi = &m->import_entries[i];
  36564             if (bc_get_leb128_int(s, &mi->var_idx))
  36565                 goto fail;
  36566             if (bc_get_atom(s, &mi->import_name))
  36567                 goto fail;
  36568             if (bc_get_leb128_int(s, &mi->req_module_idx))
  36569                 goto fail;
  36570         }
  36571     }
  36572 
  36573     if (bc_get_u8(s, &v8))
  36574         goto fail;
  36575     m->has_tla = (v8 != 0);
  36576 
  36577     m->func_obj = JS_ReadObjectRec(s);
  36578     if (JS_IsException(m->func_obj))
  36579         goto fail;
  36580     return obj;
  36581  fail:
  36582     if (m) {
  36583         js_free_module_def(ctx, m);
  36584     }
  36585     return JS_EXCEPTION;
  36586 }
  36587 
  36588 static JSValue JS_ReadObjectTag(BCReaderState *s)
  36589 {
  36590     JSContext *ctx = s->ctx;
  36591     JSValue obj;
  36592     uint32_t prop_count, i;
  36593     JSAtom atom;
  36594     JSValue val;
  36595     int ret;
  36596 
  36597     obj = JS_NewObject(ctx);
  36598     if (BC_add_object_ref(s, obj))
  36599         goto fail;
  36600     if (bc_get_leb128(s, &prop_count))
  36601         goto fail;
  36602     for(i = 0; i < prop_count; i++) {
  36603         if (bc_get_atom(s, &atom))
  36604             goto fail;
  36605 #ifdef DUMP_READ_OBJECT
  36606         bc_read_trace(s, "propname: "); print_atom(s->ctx, atom); printf("\n");
  36607 #endif
  36608         val = JS_ReadObjectRec(s);
  36609         if (JS_IsException(val)) {
  36610             JS_FreeAtom(ctx, atom);
  36611             goto fail;
  36612         }
  36613         ret = JS_DefinePropertyValue(ctx, obj, atom, val, JS_PROP_C_W_E);
  36614         JS_FreeAtom(ctx, atom);
  36615         if (ret < 0)
  36616             goto fail;
  36617     }
  36618     return obj;
  36619  fail:
  36620     JS_FreeValue(ctx, obj);
  36621     return JS_EXCEPTION;
  36622 }
  36623 
  36624 static JSValue JS_ReadArray(BCReaderState *s, int tag)
  36625 {
  36626     JSContext *ctx = s->ctx;
  36627     JSValue obj;
  36628     uint32_t len, i;
  36629     JSValue val;
  36630     int ret, prop_flags;
  36631     BOOL is_template;
  36632 
  36633     obj = JS_NewArray(ctx);
  36634     if (BC_add_object_ref(s, obj))
  36635         goto fail;
  36636     is_template = (tag == BC_TAG_TEMPLATE_OBJECT);
  36637     if (bc_get_leb128(s, &len))
  36638         goto fail;
  36639     for(i = 0; i < len; i++) {
  36640         val = JS_ReadObjectRec(s);
  36641         if (JS_IsException(val))
  36642             goto fail;
  36643         if (is_template)
  36644             prop_flags = JS_PROP_ENUMERABLE;
  36645         else
  36646             prop_flags = JS_PROP_C_W_E;
  36647         ret = JS_DefinePropertyValueUint32(ctx, obj, i, val,
  36648                                            prop_flags);
  36649         if (ret < 0)
  36650             goto fail;
  36651     }
  36652     if (is_template) {
  36653         val = JS_ReadObjectRec(s);
  36654         if (JS_IsException(val))
  36655             goto fail;
  36656         if (!JS_IsUndefined(val)) {
  36657             ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_raw, val, 0);
  36658             if (ret < 0)
  36659                 goto fail;
  36660         }
  36661         JS_PreventExtensions(ctx, obj);
  36662     }
  36663     return obj;
  36664  fail:
  36665     JS_FreeValue(ctx, obj);
  36666     return JS_EXCEPTION;
  36667 }
  36668 
  36669 static JSValue JS_ReadTypedArray(BCReaderState *s)
  36670 {
  36671     JSContext *ctx = s->ctx;
  36672     JSValue obj = JS_UNDEFINED, array_buffer = JS_UNDEFINED;
  36673     uint8_t array_tag;
  36674     JSValueConst args[3];
  36675     uint32_t offset, len, idx;
  36676 
  36677     if (bc_get_u8(s, &array_tag))
  36678         return JS_EXCEPTION;
  36679     if (array_tag >= JS_TYPED_ARRAY_COUNT)
  36680         return JS_ThrowTypeError(ctx, "invalid typed array");
  36681     if (bc_get_leb128(s, &len))
  36682         return JS_EXCEPTION;
  36683     if (bc_get_leb128(s, &offset))
  36684         return JS_EXCEPTION;
  36685     /* XXX: this hack could be avoided if the typed array could be
  36686        created before the array buffer */
  36687     idx = s->objects_count;
  36688     if (BC_add_object_ref1(s, NULL))
  36689         goto fail;
  36690     array_buffer = JS_ReadObjectRec(s);
  36691     if (JS_IsException(array_buffer))
  36692         return JS_EXCEPTION;
  36693     if (!js_get_array_buffer(ctx, array_buffer)) {
  36694         JS_FreeValue(ctx, array_buffer);
  36695         return JS_EXCEPTION;
  36696     }
  36697     args[0] = array_buffer;
  36698     args[1] = JS_NewInt64(ctx, offset);
  36699     args[2] = JS_NewInt64(ctx, len);
  36700     obj = js_typed_array_constructor(ctx, JS_UNDEFINED,
  36701                                      3, args,
  36702                                      JS_CLASS_UINT8C_ARRAY + array_tag);
  36703     if (JS_IsException(obj))
  36704         goto fail;
  36705     if (s->allow_reference) {
  36706         s->objects[idx] = JS_VALUE_GET_OBJ(obj);
  36707     }
  36708     JS_FreeValue(ctx, array_buffer);
  36709     return obj;
  36710  fail:
  36711     JS_FreeValue(ctx, array_buffer);
  36712     JS_FreeValue(ctx, obj);
  36713     return JS_EXCEPTION;
  36714 }
  36715 
  36716 static JSValue JS_ReadArrayBuffer(BCReaderState *s)
  36717 {
  36718     JSContext *ctx = s->ctx;
  36719     uint32_t byte_length;
  36720     JSValue obj;
  36721 
  36722     if (bc_get_leb128(s, &byte_length))
  36723         return JS_EXCEPTION;
  36724     if (unlikely(s->buf_end - s->ptr < byte_length)) {
  36725         bc_read_error_end(s);
  36726         return JS_EXCEPTION;
  36727     }
  36728     obj = JS_NewArrayBufferCopy(ctx, s->ptr, byte_length);
  36729     if (JS_IsException(obj))
  36730         goto fail;
  36731     if (BC_add_object_ref(s, obj))
  36732         goto fail;
  36733     s->ptr += byte_length;
  36734     return obj;
  36735  fail:
  36736     JS_FreeValue(ctx, obj);
  36737     return JS_EXCEPTION;
  36738 }
  36739 
  36740 static JSValue JS_ReadSharedArrayBuffer(BCReaderState *s)
  36741 {
  36742     JSContext *ctx = s->ctx;
  36743     uint32_t byte_length;
  36744     uint8_t *data_ptr;
  36745     JSValue obj;
  36746     uint64_t u64;
  36747 
  36748     if (bc_get_leb128(s, &byte_length))
  36749         return JS_EXCEPTION;
  36750     if (bc_get_u64(s, &u64))
  36751         return JS_EXCEPTION;
  36752     data_ptr = (uint8_t *)(uintptr_t)u64;
  36753     /* the SharedArrayBuffer is cloned */
  36754     obj = js_array_buffer_constructor3(ctx, JS_UNDEFINED, byte_length,
  36755                                        JS_CLASS_SHARED_ARRAY_BUFFER,
  36756                                        data_ptr,
  36757                                        NULL, NULL, FALSE);
  36758     if (JS_IsException(obj))
  36759         goto fail;
  36760     if (BC_add_object_ref(s, obj))
  36761         goto fail;
  36762     return obj;
  36763  fail:
  36764     JS_FreeValue(ctx, obj);
  36765     return JS_EXCEPTION;
  36766 }
  36767 
  36768 static JSValue JS_ReadDate(BCReaderState *s)
  36769 {
  36770     JSContext *ctx = s->ctx;
  36771     JSValue val, obj = JS_UNDEFINED;
  36772 
  36773     val = JS_ReadObjectRec(s);
  36774     if (JS_IsException(val))
  36775         goto fail;
  36776     if (!JS_IsNumber(val)) {
  36777         JS_ThrowTypeError(ctx, "Number tag expected for date");
  36778         goto fail;
  36779     }
  36780     obj = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_DATE],
  36781                                  JS_CLASS_DATE);
  36782     if (JS_IsException(obj))
  36783         goto fail;
  36784     if (BC_add_object_ref(s, obj))
  36785         goto fail;
  36786     JS_SetObjectData(ctx, obj, val);
  36787     return obj;
  36788  fail:
  36789     JS_FreeValue(ctx, val);
  36790     JS_FreeValue(ctx, obj);
  36791     return JS_EXCEPTION;
  36792 }
  36793 
  36794 static JSValue JS_ReadObjectValue(BCReaderState *s)
  36795 {
  36796     JSContext *ctx = s->ctx;
  36797     JSValue val, obj = JS_UNDEFINED;
  36798 
  36799     val = JS_ReadObjectRec(s);
  36800     if (JS_IsException(val))
  36801         goto fail;
  36802     obj = JS_ToObject(ctx, val);
  36803     if (JS_IsException(obj))
  36804         goto fail;
  36805     if (BC_add_object_ref(s, obj))
  36806         goto fail;
  36807     JS_FreeValue(ctx, val);
  36808     return obj;
  36809  fail:
  36810     JS_FreeValue(ctx, val);
  36811     JS_FreeValue(ctx, obj);
  36812     return JS_EXCEPTION;
  36813 }
  36814 
  36815 static JSValue JS_ReadObjectRec(BCReaderState *s)
  36816 {
  36817     JSContext *ctx = s->ctx;
  36818     uint8_t tag;
  36819     JSValue obj = JS_UNDEFINED;
  36820 
  36821     if (js_check_stack_overflow(ctx->rt, 0))
  36822         return JS_ThrowStackOverflow(ctx);
  36823 
  36824     if (bc_get_u8(s, &tag))
  36825         return JS_EXCEPTION;
  36826 
  36827     bc_read_trace(s, "%s {\n", bc_tag_str[tag]);
  36828 
  36829     switch(tag) {
  36830     case BC_TAG_NULL:
  36831         obj = JS_NULL;
  36832         break;
  36833     case BC_TAG_UNDEFINED:
  36834         obj = JS_UNDEFINED;
  36835         break;
  36836     case BC_TAG_BOOL_FALSE:
  36837     case BC_TAG_BOOL_TRUE:
  36838         obj = JS_NewBool(ctx, tag - BC_TAG_BOOL_FALSE);
  36839         break;
  36840     case BC_TAG_INT32:
  36841         {
  36842             int32_t val;
  36843             if (bc_get_sleb128(s, &val))
  36844                 return JS_EXCEPTION;
  36845             bc_read_trace(s, "%d\n", val);
  36846             obj = JS_NewInt32(ctx, val);
  36847         }
  36848         break;
  36849     case BC_TAG_FLOAT64:
  36850         {
  36851             JSFloat64Union u;
  36852             if (bc_get_u64(s, &u.u64))
  36853                 return JS_EXCEPTION;
  36854             bc_read_trace(s, "%g\n", u.d);
  36855             obj = __JS_NewFloat64(ctx, u.d);
  36856         }
  36857         break;
  36858     case BC_TAG_STRING:
  36859         {
  36860             JSString *p;
  36861             p = JS_ReadString(s);
  36862             if (!p)
  36863                 return JS_EXCEPTION;
  36864             obj = JS_MKPTR(JS_TAG_STRING, p);
  36865         }
  36866         break;
  36867     case BC_TAG_FUNCTION_BYTECODE:
  36868         if (!s->allow_bytecode)
  36869             goto invalid_tag;
  36870         obj = JS_ReadFunctionTag(s);
  36871         break;
  36872     case BC_TAG_MODULE:
  36873         if (!s->allow_bytecode)
  36874             goto invalid_tag;
  36875         obj = JS_ReadModule(s);
  36876         break;
  36877     case BC_TAG_OBJECT:
  36878         obj = JS_ReadObjectTag(s);
  36879         break;
  36880     case BC_TAG_ARRAY:
  36881     case BC_TAG_TEMPLATE_OBJECT:
  36882         obj = JS_ReadArray(s, tag);
  36883         break;
  36884     case BC_TAG_TYPED_ARRAY:
  36885         obj = JS_ReadTypedArray(s);
  36886         break;
  36887     case BC_TAG_ARRAY_BUFFER:
  36888         obj = JS_ReadArrayBuffer(s);
  36889         break;
  36890     case BC_TAG_SHARED_ARRAY_BUFFER:
  36891         if (!s->allow_sab || !ctx->rt->sab_funcs.sab_dup)
  36892             goto invalid_tag;
  36893         obj = JS_ReadSharedArrayBuffer(s);
  36894         break;
  36895     case BC_TAG_DATE:
  36896         obj = JS_ReadDate(s);
  36897         break;
  36898     case BC_TAG_OBJECT_VALUE:
  36899         obj = JS_ReadObjectValue(s);
  36900         break;
  36901     case BC_TAG_BIG_INT:
  36902 #ifdef CONFIG_BIGNUM
  36903     case BC_TAG_BIG_FLOAT:
  36904     case BC_TAG_BIG_DECIMAL:
  36905 #endif
  36906         obj = JS_ReadBigNum(s, tag);
  36907         break;
  36908     case BC_TAG_OBJECT_REFERENCE:
  36909         {
  36910             uint32_t val;
  36911             if (!s->allow_reference)
  36912                 return JS_ThrowSyntaxError(ctx, "object references are not allowed");
  36913             if (bc_get_leb128(s, &val))
  36914                 return JS_EXCEPTION;
  36915             bc_read_trace(s, "%u\n", val);
  36916             if (val >= s->objects_count) {
  36917                 return JS_ThrowSyntaxError(ctx, "invalid object reference (%u >= %u)",
  36918                                            val, s->objects_count);
  36919             }
  36920             obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, s->objects[val]));
  36921         }
  36922         break;
  36923     default:
  36924     invalid_tag:
  36925         return JS_ThrowSyntaxError(ctx, "invalid tag (tag=%d pos=%u)",
  36926                                    tag, (unsigned int)(s->ptr - s->buf_start));
  36927     }
  36928     bc_read_trace(s, "}\n");
  36929     return obj;
  36930 }
  36931 
  36932 static int JS_ReadObjectAtoms(BCReaderState *s)
  36933 {
  36934     uint8_t v8;
  36935     JSString *p;
  36936     int i;
  36937     JSAtom atom;
  36938 
  36939     if (bc_get_u8(s, &v8))
  36940         return -1;
  36941     if (v8 != BC_VERSION) {
  36942         JS_ThrowSyntaxError(s->ctx, "invalid version (%d expected=%d)",
  36943                             v8, BC_VERSION);
  36944         return -1;
  36945     }
  36946     if (bc_get_leb128(s, &s->idx_to_atom_count))
  36947         return -1;
  36948 
  36949     bc_read_trace(s, "%d atom indexes {\n", s->idx_to_atom_count);
  36950 
  36951     if (s->idx_to_atom_count != 0) {
  36952         s->idx_to_atom = js_mallocz(s->ctx, s->idx_to_atom_count *
  36953                                     sizeof(s->idx_to_atom[0]));
  36954         if (!s->idx_to_atom)
  36955             return s->error_state = -1;
  36956     }
  36957     for(i = 0; i < s->idx_to_atom_count; i++) {
  36958         p = JS_ReadString(s);
  36959         if (!p)
  36960             return -1;
  36961         atom = JS_NewAtomStr(s->ctx, p);
  36962         if (atom == JS_ATOM_NULL)
  36963             return s->error_state = -1;
  36964         s->idx_to_atom[i] = atom;
  36965         if (s->is_rom_data && (atom != (i + s->first_atom)))
  36966             s->is_rom_data = FALSE; /* atoms must be relocated */
  36967     }
  36968     bc_read_trace(s, "}\n");
  36969     return 0;
  36970 }
  36971 
  36972 static void bc_reader_free(BCReaderState *s)
  36973 {
  36974     int i;
  36975     if (s->idx_to_atom) {
  36976         for(i = 0; i < s->idx_to_atom_count; i++) {
  36977             JS_FreeAtom(s->ctx, s->idx_to_atom[i]);
  36978         }
  36979         js_free(s->ctx, s->idx_to_atom);
  36980     }
  36981     js_free(s->ctx, s->objects);
  36982 }
  36983 
  36984 JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len,
  36985                        int flags)
  36986 {
  36987     BCReaderState ss, *s = &ss;
  36988     JSValue obj;
  36989 
  36990     ctx->binary_object_count += 1;
  36991     ctx->binary_object_size += buf_len;
  36992 
  36993     memset(s, 0, sizeof(*s));
  36994     s->ctx = ctx;
  36995     s->buf_start = buf;
  36996     s->buf_end = buf + buf_len;
  36997     s->ptr = buf;
  36998     s->allow_bytecode = ((flags & JS_READ_OBJ_BYTECODE) != 0);
  36999     s->is_rom_data = ((flags & JS_READ_OBJ_ROM_DATA) != 0);
  37000     s->allow_sab = ((flags & JS_READ_OBJ_SAB) != 0);
  37001     s->allow_reference = ((flags & JS_READ_OBJ_REFERENCE) != 0);
  37002     if (s->allow_bytecode)
  37003         s->first_atom = JS_ATOM_END;
  37004     else
  37005         s->first_atom = 1;
  37006     if (JS_ReadObjectAtoms(s)) {
  37007         obj = JS_EXCEPTION;
  37008     } else {
  37009         obj = JS_ReadObjectRec(s);
  37010     }
  37011     bc_reader_free(s);
  37012     return obj;
  37013 }
  37014 
  37015 /*******************************************************************/
  37016 /* runtime functions & objects */
  37017 
  37018 static JSValue js_string_constructor(JSContext *ctx, JSValueConst this_val,
  37019                                      int argc, JSValueConst *argv);
  37020 static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst this_val,
  37021                                       int argc, JSValueConst *argv);
  37022 static JSValue js_number_constructor(JSContext *ctx, JSValueConst this_val,
  37023                                      int argc, JSValueConst *argv);
  37024 
  37025 static int check_function(JSContext *ctx, JSValueConst obj)
  37026 {
  37027     if (likely(JS_IsFunction(ctx, obj)))
  37028         return 0;
  37029     JS_ThrowTypeError(ctx, "not a function");
  37030     return -1;
  37031 }
  37032 
  37033 static int check_exception_free(JSContext *ctx, JSValue obj)
  37034 {
  37035     JS_FreeValue(ctx, obj);
  37036     return JS_IsException(obj);
  37037 }
  37038 
  37039 static JSAtom find_atom(JSContext *ctx, const char *name)
  37040 {
  37041     JSAtom atom;
  37042     int len;
  37043 
  37044     if (*name == '[') {
  37045         name++;
  37046         len = strlen(name) - 1;
  37047         /* We assume 8 bit non null strings, which is the case for these
  37048            symbols */
  37049         for(atom = JS_ATOM_Symbol_toPrimitive; atom < JS_ATOM_END; atom++) {
  37050             JSAtomStruct *p = ctx->rt->atom_array[atom];
  37051             JSString *str = p;
  37052             if (str->len == len && !memcmp(str->u.str8, name, len))
  37053                 return JS_DupAtom(ctx, atom);
  37054         }
  37055         abort();
  37056     } else {
  37057         atom = JS_NewAtom(ctx, name);
  37058     }
  37059     return atom;
  37060 }
  37061 
  37062 static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p,
  37063                                                JSAtom atom, void *opaque)
  37064 {
  37065     const JSCFunctionListEntry *e = opaque;
  37066     JSValue val;
  37067 
  37068     switch(e->def_type) {
  37069     case JS_DEF_CFUNC:
  37070         val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic,
  37071                                e->name, e->u.func.length, e->u.func.cproto, e->magic);
  37072         break;
  37073     case JS_DEF_PROP_STRING:
  37074         val = JS_NewAtomString(ctx, e->u.str);
  37075         break;
  37076     case JS_DEF_OBJECT:
  37077         val = JS_NewObject(ctx);
  37078         JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len);
  37079         break;
  37080     default:
  37081         abort();
  37082     }
  37083     return val;
  37084 }
  37085 
  37086 static int JS_InstantiateFunctionListItem(JSContext *ctx, JSValueConst obj,
  37087                                           JSAtom atom,
  37088                                           const JSCFunctionListEntry *e)
  37089 {
  37090     JSValue val;
  37091     int prop_flags = e->prop_flags;
  37092 
  37093     switch(e->def_type) {
  37094     case JS_DEF_ALIAS: /* using autoinit for aliases is not safe */
  37095         {
  37096             JSAtom atom1 = find_atom(ctx, e->u.alias.name);
  37097             switch (e->u.alias.base) {
  37098             case -1:
  37099                 val = JS_GetProperty(ctx, obj, atom1);
  37100                 break;
  37101             case 0:
  37102                 val = JS_GetProperty(ctx, ctx->global_obj, atom1);
  37103                 break;
  37104             case 1:
  37105                 val = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], atom1);
  37106                 break;
  37107             default:
  37108                 abort();
  37109             }
  37110             JS_FreeAtom(ctx, atom1);
  37111             if (atom == JS_ATOM_Symbol_toPrimitive) {
  37112                 /* Symbol.toPrimitive functions are not writable */
  37113                 prop_flags = JS_PROP_CONFIGURABLE;
  37114             } else if (atom == JS_ATOM_Symbol_hasInstance) {
  37115                 /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */
  37116                 prop_flags = 0;
  37117             }
  37118         }
  37119         break;
  37120     case JS_DEF_CFUNC:
  37121         if (atom == JS_ATOM_Symbol_toPrimitive) {
  37122             /* Symbol.toPrimitive functions are not writable */
  37123             prop_flags = JS_PROP_CONFIGURABLE;
  37124         } else if (atom == JS_ATOM_Symbol_hasInstance) {
  37125             /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */
  37126             prop_flags = 0;
  37127         }
  37128         JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP,
  37129                                   (void *)e, prop_flags);
  37130         return 0;
  37131     case JS_DEF_CGETSET: /* XXX: use autoinit again ? */
  37132     case JS_DEF_CGETSET_MAGIC:
  37133         {
  37134             JSValue getter, setter;
  37135             char buf[64];
  37136 
  37137             getter = JS_UNDEFINED;
  37138             if (e->u.getset.get.generic) {
  37139                 snprintf(buf, sizeof(buf), "get %s", e->name);
  37140                 getter = JS_NewCFunction2(ctx, e->u.getset.get.generic,
  37141                                           buf, 0, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_getter_magic : JS_CFUNC_getter,
  37142                                           e->magic);
  37143             }
  37144             setter = JS_UNDEFINED;
  37145             if (e->u.getset.set.generic) {
  37146                 snprintf(buf, sizeof(buf), "set %s", e->name);
  37147                 setter = JS_NewCFunction2(ctx, e->u.getset.set.generic,
  37148                                           buf, 1, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_setter_magic : JS_CFUNC_setter,
  37149                                           e->magic);
  37150             }
  37151             JS_DefinePropertyGetSet(ctx, obj, atom, getter, setter, prop_flags);
  37152             return 0;
  37153         }
  37154         break;
  37155     case JS_DEF_PROP_INT32:
  37156         val = JS_NewInt32(ctx, e->u.i32);
  37157         break;
  37158     case JS_DEF_PROP_INT64:
  37159         val = JS_NewInt64(ctx, e->u.i64);
  37160         break;
  37161     case JS_DEF_PROP_DOUBLE:
  37162         val = __JS_NewFloat64(ctx, e->u.f64);
  37163         break;
  37164     case JS_DEF_PROP_UNDEFINED:
  37165         val = JS_UNDEFINED;
  37166         break;
  37167     case JS_DEF_PROP_STRING:
  37168     case JS_DEF_OBJECT:
  37169         JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP,
  37170                                   (void *)e, prop_flags);
  37171         return 0;
  37172     default:
  37173         abort();
  37174     }
  37175     JS_DefinePropertyValue(ctx, obj, atom, val, prop_flags);
  37176     return 0;
  37177 }
  37178 
  37179 void JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj,
  37180                                 const JSCFunctionListEntry *tab, int len)
  37181 {
  37182     int i;
  37183 
  37184     for (i = 0; i < len; i++) {
  37185         const JSCFunctionListEntry *e = &tab[i];
  37186         JSAtom atom = find_atom(ctx, e->name);
  37187         JS_InstantiateFunctionListItem(ctx, obj, atom, e);
  37188         JS_FreeAtom(ctx, atom);
  37189     }
  37190 }
  37191 
  37192 int JS_AddModuleExportList(JSContext *ctx, JSModuleDef *m,
  37193                            const JSCFunctionListEntry *tab, int len)
  37194 {
  37195     int i;
  37196     for(i = 0; i < len; i++) {
  37197         if (JS_AddModuleExport(ctx, m, tab[i].name))
  37198             return -1;
  37199     }
  37200     return 0;
  37201 }
  37202 
  37203 int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m,
  37204                            const JSCFunctionListEntry *tab, int len)
  37205 {
  37206     int i;
  37207     JSValue val;
  37208 
  37209     for(i = 0; i < len; i++) {
  37210         const JSCFunctionListEntry *e = &tab[i];
  37211         switch(e->def_type) {
  37212         case JS_DEF_CFUNC:
  37213             val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic,
  37214                                    e->name, e->u.func.length, e->u.func.cproto, e->magic);
  37215             break;
  37216         case JS_DEF_PROP_STRING:
  37217             val = JS_NewString(ctx, e->u.str);
  37218             break;
  37219         case JS_DEF_PROP_INT32:
  37220             val = JS_NewInt32(ctx, e->u.i32);
  37221             break;
  37222         case JS_DEF_PROP_INT64:
  37223             val = JS_NewInt64(ctx, e->u.i64);
  37224             break;
  37225         case JS_DEF_PROP_DOUBLE:
  37226             val = __JS_NewFloat64(ctx, e->u.f64);
  37227             break;
  37228         case JS_DEF_OBJECT:
  37229             val = JS_NewObject(ctx);
  37230             JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len);
  37231             break;
  37232         default:
  37233             abort();
  37234         }
  37235         if (JS_SetModuleExport(ctx, m, e->name, val))
  37236             return -1;
  37237     }
  37238     return 0;
  37239 }
  37240 
  37241 /* Note: 'func_obj' is not necessarily a constructor */
  37242 static void JS_SetConstructor2(JSContext *ctx,
  37243                                JSValueConst func_obj,
  37244                                JSValueConst proto,
  37245                                int proto_flags, int ctor_flags)
  37246 {
  37247     JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype,
  37248                            JS_DupValue(ctx, proto), proto_flags);
  37249     JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor,
  37250                            JS_DupValue(ctx, func_obj),
  37251                            ctor_flags);
  37252     set_cycle_flag(ctx, func_obj);
  37253     set_cycle_flag(ctx, proto);
  37254 }
  37255 
  37256 void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj,
  37257                        JSValueConst proto)
  37258 {
  37259     JS_SetConstructor2(ctx, func_obj, proto,
  37260                        0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
  37261 }
  37262 
  37263 static void JS_NewGlobalCConstructor2(JSContext *ctx,
  37264                                       JSValue func_obj,
  37265                                       const char *name,
  37266                                       JSValueConst proto)
  37267 {
  37268     JS_DefinePropertyValueStr(ctx, ctx->global_obj, name,
  37269                            JS_DupValue(ctx, func_obj),
  37270                            JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
  37271     JS_SetConstructor(ctx, func_obj, proto);
  37272     JS_FreeValue(ctx, func_obj);
  37273 }
  37274 
  37275 static JSValueConst JS_NewGlobalCConstructor(JSContext *ctx, const char *name,
  37276                                              JSCFunction *func, int length,
  37277                                              JSValueConst proto)
  37278 {
  37279     JSValue func_obj;
  37280     func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor_or_func, 0);
  37281     JS_NewGlobalCConstructor2(ctx, func_obj, name, proto);
  37282     return func_obj;
  37283 }
  37284 
  37285 static JSValueConst JS_NewGlobalCConstructorOnly(JSContext *ctx, const char *name,
  37286                                                  JSCFunction *func, int length,
  37287                                                  JSValueConst proto)
  37288 {
  37289     JSValue func_obj;
  37290     func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor, 0);
  37291     JS_NewGlobalCConstructor2(ctx, func_obj, name, proto);
  37292     return func_obj;
  37293 }
  37294 
  37295 static JSValue js_global_eval(JSContext *ctx, JSValueConst this_val,
  37296                               int argc, JSValueConst *argv)
  37297 {
  37298     return JS_EvalObject(ctx, ctx->global_obj, argv[0], JS_EVAL_TYPE_INDIRECT, -1);
  37299 }
  37300 
  37301 static JSValue js_global_isNaN(JSContext *ctx, JSValueConst this_val,
  37302                                int argc, JSValueConst *argv)
  37303 {
  37304     double d;
  37305 
  37306     /* XXX: does this work for bigfloat? */
  37307     if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))
  37308         return JS_EXCEPTION;
  37309     return JS_NewBool(ctx, isnan(d));
  37310 }
  37311 
  37312 static JSValue js_global_isFinite(JSContext *ctx, JSValueConst this_val,
  37313                                   int argc, JSValueConst *argv)
  37314 {
  37315     double d;
  37316     if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))
  37317         return JS_EXCEPTION;
  37318     return JS_NewBool(ctx, isfinite(d));
  37319 }
  37320 
  37321 /* Object class */
  37322 
  37323 static JSValue JS_ToObject(JSContext *ctx, JSValueConst val)
  37324 {
  37325     int tag = JS_VALUE_GET_NORM_TAG(val);
  37326     JSValue obj;
  37327 
  37328     switch(tag) {
  37329     default:
  37330     case JS_TAG_NULL:
  37331     case JS_TAG_UNDEFINED:
  37332         return JS_ThrowTypeError(ctx, "cannot convert to object");
  37333     case JS_TAG_OBJECT:
  37334     case JS_TAG_EXCEPTION:
  37335         return JS_DupValue(ctx, val);
  37336     case JS_TAG_BIG_INT:
  37337         obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT);
  37338         goto set_value;
  37339 #ifdef CONFIG_BIGNUM
  37340     case JS_TAG_BIG_FLOAT:
  37341         obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_FLOAT);
  37342         goto set_value;
  37343     case JS_TAG_BIG_DECIMAL:
  37344         obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_DECIMAL);
  37345         goto set_value;
  37346 #endif
  37347     case JS_TAG_INT:
  37348     case JS_TAG_FLOAT64:
  37349         obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER);
  37350         goto set_value;
  37351     case JS_TAG_STRING:
  37352         /* XXX: should call the string constructor */
  37353         {
  37354             JSString *p1 = JS_VALUE_GET_STRING(val);
  37355             obj = JS_NewObjectClass(ctx, JS_CLASS_STRING);
  37356             JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0);
  37357         }
  37358         goto set_value;
  37359     case JS_TAG_BOOL:
  37360         obj = JS_NewObjectClass(ctx, JS_CLASS_BOOLEAN);
  37361         goto set_value;
  37362     case JS_TAG_SYMBOL:
  37363         obj = JS_NewObjectClass(ctx, JS_CLASS_SYMBOL);
  37364     set_value:
  37365         if (!JS_IsException(obj))
  37366             JS_SetObjectData(ctx, obj, JS_DupValue(ctx, val));
  37367         return obj;
  37368     }
  37369 }
  37370 
  37371 static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val)
  37372 {
  37373     JSValue obj = JS_ToObject(ctx, val);
  37374     JS_FreeValue(ctx, val);
  37375     return obj;
  37376 }
  37377 
  37378 static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d,
  37379                           JSValueConst desc)
  37380 {
  37381     JSValue val, getter, setter;
  37382     int flags;
  37383 
  37384     if (!JS_IsObject(desc)) {
  37385         JS_ThrowTypeErrorNotAnObject(ctx);
  37386         return -1;
  37387     }
  37388     flags = 0;
  37389     val = JS_UNDEFINED;
  37390     getter = JS_UNDEFINED;
  37391     setter = JS_UNDEFINED;
  37392     if (JS_HasProperty(ctx, desc, JS_ATOM_configurable)) {
  37393         JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_configurable);
  37394         if (JS_IsException(prop))
  37395             goto fail;
  37396         flags |= JS_PROP_HAS_CONFIGURABLE;
  37397         if (JS_ToBoolFree(ctx, prop))
  37398             flags |= JS_PROP_CONFIGURABLE;
  37399     }
  37400     if (JS_HasProperty(ctx, desc, JS_ATOM_writable)) {
  37401         JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_writable);
  37402         if (JS_IsException(prop))
  37403             goto fail;
  37404         flags |= JS_PROP_HAS_WRITABLE;
  37405         if (JS_ToBoolFree(ctx, prop))
  37406             flags |= JS_PROP_WRITABLE;
  37407     }
  37408     if (JS_HasProperty(ctx, desc, JS_ATOM_enumerable)) {
  37409         JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_enumerable);
  37410         if (JS_IsException(prop))
  37411             goto fail;
  37412         flags |= JS_PROP_HAS_ENUMERABLE;
  37413         if (JS_ToBoolFree(ctx, prop))
  37414             flags |= JS_PROP_ENUMERABLE;
  37415     }
  37416     if (JS_HasProperty(ctx, desc, JS_ATOM_value)) {
  37417         flags |= JS_PROP_HAS_VALUE;
  37418         val = JS_GetProperty(ctx, desc, JS_ATOM_value);
  37419         if (JS_IsException(val))
  37420             goto fail;
  37421     }
  37422     if (JS_HasProperty(ctx, desc, JS_ATOM_get)) {
  37423         flags |= JS_PROP_HAS_GET;
  37424         getter = JS_GetProperty(ctx, desc, JS_ATOM_get);
  37425         if (JS_IsException(getter) ||
  37426             !(JS_IsUndefined(getter) || JS_IsFunction(ctx, getter))) {
  37427             JS_ThrowTypeError(ctx, "invalid getter");
  37428             goto fail;
  37429         }
  37430     }
  37431     if (JS_HasProperty(ctx, desc, JS_ATOM_set)) {
  37432         flags |= JS_PROP_HAS_SET;
  37433         setter = JS_GetProperty(ctx, desc, JS_ATOM_set);
  37434         if (JS_IsException(setter) ||
  37435             !(JS_IsUndefined(setter) || JS_IsFunction(ctx, setter))) {
  37436             JS_ThrowTypeError(ctx, "invalid setter");
  37437             goto fail;
  37438         }
  37439     }
  37440     if ((flags & (JS_PROP_HAS_SET | JS_PROP_HAS_GET)) &&
  37441         (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE))) {
  37442         JS_ThrowTypeError(ctx, "cannot have setter/getter and value or writable");
  37443         goto fail;
  37444     }
  37445     d->flags = flags;
  37446     d->value = val;
  37447     d->getter = getter;
  37448     d->setter = setter;
  37449     return 0;
  37450  fail:
  37451     JS_FreeValue(ctx, val);
  37452     JS_FreeValue(ctx, getter);
  37453     JS_FreeValue(ctx, setter);
  37454     return -1;
  37455 }
  37456 
  37457 static __exception int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj,
  37458                                              JSAtom prop, JSValueConst desc,
  37459                                              int flags)
  37460 {
  37461     JSPropertyDescriptor d;
  37462     int ret;
  37463 
  37464     if (js_obj_to_desc(ctx, &d, desc) < 0)
  37465         return -1;
  37466 
  37467     ret = JS_DefineProperty(ctx, obj, prop,
  37468                             d.value, d.getter, d.setter, d.flags | flags);
  37469     js_free_desc(ctx, &d);
  37470     return ret;
  37471 }
  37472 
  37473 static __exception int JS_ObjectDefineProperties(JSContext *ctx,
  37474                                                  JSValueConst obj,
  37475                                                  JSValueConst properties)
  37476 {
  37477     JSValue props, desc;
  37478     JSObject *p;
  37479     JSPropertyEnum *atoms;
  37480     uint32_t len, i;
  37481     int ret = -1;
  37482 
  37483     if (!JS_IsObject(obj)) {
  37484         JS_ThrowTypeErrorNotAnObject(ctx);
  37485         return -1;
  37486     }
  37487     desc = JS_UNDEFINED;
  37488     props = JS_ToObject(ctx, properties);
  37489     if (JS_IsException(props))
  37490         return -1;
  37491     p = JS_VALUE_GET_OBJ(props);
  37492     if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK) < 0)
  37493         goto exception;
  37494     for(i = 0; i < len; i++) {
  37495         JS_FreeValue(ctx, desc);
  37496         desc = JS_GetProperty(ctx, props, atoms[i].atom);
  37497         if (JS_IsException(desc))
  37498             goto exception;
  37499         if (JS_DefinePropertyDesc(ctx, obj, atoms[i].atom, desc, JS_PROP_THROW) < 0)
  37500             goto exception;
  37501     }
  37502     ret = 0;
  37503 
  37504 exception:
  37505     js_free_prop_enum(ctx, atoms, len);
  37506     JS_FreeValue(ctx, props);
  37507     JS_FreeValue(ctx, desc);
  37508     return ret;
  37509 }
  37510 
  37511 static JSValue js_object_constructor(JSContext *ctx, JSValueConst new_target,
  37512                                      int argc, JSValueConst *argv)
  37513 {
  37514     JSValue ret;
  37515     if (!JS_IsUndefined(new_target) &&
  37516         JS_VALUE_GET_OBJ(new_target) !=
  37517         JS_VALUE_GET_OBJ(JS_GetActiveFunction(ctx))) {
  37518         ret = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);
  37519     } else {
  37520         int tag = JS_VALUE_GET_NORM_TAG(argv[0]);
  37521         switch(tag) {
  37522         case JS_TAG_NULL:
  37523         case JS_TAG_UNDEFINED:
  37524             ret = JS_NewObject(ctx);
  37525             break;
  37526         default:
  37527             ret = JS_ToObject(ctx, argv[0]);
  37528             break;
  37529         }
  37530     }
  37531     return ret;
  37532 }
  37533 
  37534 static JSValue js_object_create(JSContext *ctx, JSValueConst this_val,
  37535                                 int argc, JSValueConst *argv)
  37536 {
  37537     JSValueConst proto, props;
  37538     JSValue obj;
  37539 
  37540     proto = argv[0];
  37541     if (!JS_IsObject(proto) && !JS_IsNull(proto))
  37542         return JS_ThrowTypeError(ctx, "not a prototype");
  37543     obj = JS_NewObjectProto(ctx, proto);
  37544     if (JS_IsException(obj))
  37545         return JS_EXCEPTION;
  37546     props = argv[1];
  37547     if (!JS_IsUndefined(props)) {
  37548         if (JS_ObjectDefineProperties(ctx, obj, props)) {
  37549             JS_FreeValue(ctx, obj);
  37550             return JS_EXCEPTION;
  37551         }
  37552     }
  37553     return obj;
  37554 }
  37555 
  37556 static JSValue js_object_getPrototypeOf(JSContext *ctx, JSValueConst this_val,
  37557                                         int argc, JSValueConst *argv, int magic)
  37558 {
  37559     JSValueConst val;
  37560 
  37561     val = argv[0];
  37562     if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) {
  37563         /* ES6 feature non compatible with ES5.1: primitive types are
  37564            accepted */
  37565         if (magic || JS_VALUE_GET_TAG(val) == JS_TAG_NULL ||
  37566             JS_VALUE_GET_TAG(val) == JS_TAG_UNDEFINED)
  37567             return JS_ThrowTypeErrorNotAnObject(ctx);
  37568     }
  37569     return JS_GetPrototype(ctx, val);
  37570 }
  37571 
  37572 static JSValue js_object_setPrototypeOf(JSContext *ctx, JSValueConst this_val,
  37573                                         int argc, JSValueConst *argv)
  37574 {
  37575     JSValueConst obj;
  37576     obj = argv[0];
  37577     if (JS_SetPrototypeInternal(ctx, obj, argv[1], TRUE) < 0)
  37578         return JS_EXCEPTION;
  37579     return JS_DupValue(ctx, obj);
  37580 }
  37581 
  37582 /* magic = 1 if called as Reflect.defineProperty */
  37583 static JSValue js_object_defineProperty(JSContext *ctx, JSValueConst this_val,
  37584                                         int argc, JSValueConst *argv, int magic)
  37585 {
  37586     JSValueConst obj, prop, desc;
  37587     int ret, flags;
  37588     JSAtom atom;
  37589 
  37590     obj = argv[0];
  37591     prop = argv[1];
  37592     desc = argv[2];
  37593 
  37594     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
  37595         return JS_ThrowTypeErrorNotAnObject(ctx);
  37596     atom = JS_ValueToAtom(ctx, prop);
  37597     if (unlikely(atom == JS_ATOM_NULL))
  37598         return JS_EXCEPTION;
  37599     flags = 0;
  37600     if (!magic)
  37601         flags |= JS_PROP_THROW;
  37602     ret = JS_DefinePropertyDesc(ctx, obj, atom, desc, flags);
  37603     JS_FreeAtom(ctx, atom);
  37604     if (ret < 0) {
  37605         return JS_EXCEPTION;
  37606     } else if (magic) {
  37607         return JS_NewBool(ctx, ret);
  37608     } else {
  37609         return JS_DupValue(ctx, obj);
  37610     }
  37611 }
  37612 
  37613 static JSValue js_object_defineProperties(JSContext *ctx, JSValueConst this_val,
  37614                                           int argc, JSValueConst *argv)
  37615 {
  37616     // defineProperties(obj, properties)
  37617     JSValueConst obj = argv[0];
  37618 
  37619     if (JS_ObjectDefineProperties(ctx, obj, argv[1]))
  37620         return JS_EXCEPTION;
  37621     else
  37622         return JS_DupValue(ctx, obj);
  37623 }
  37624 
  37625 /* magic = 1 if called as __defineSetter__ */
  37626 static JSValue js_object___defineGetter__(JSContext *ctx, JSValueConst this_val,
  37627                                           int argc, JSValueConst *argv, int magic)
  37628 {
  37629     JSValue obj;
  37630     JSValueConst prop, value, get, set;
  37631     int ret, flags;
  37632     JSAtom atom;
  37633 
  37634     prop = argv[0];
  37635     value = argv[1];
  37636 
  37637     obj = JS_ToObject(ctx, this_val);
  37638     if (JS_IsException(obj))
  37639         return JS_EXCEPTION;
  37640 
  37641     if (check_function(ctx, value)) {
  37642         JS_FreeValue(ctx, obj);
  37643         return JS_EXCEPTION;
  37644     }
  37645     atom = JS_ValueToAtom(ctx, prop);
  37646     if (unlikely(atom == JS_ATOM_NULL)) {
  37647         JS_FreeValue(ctx, obj);
  37648         return JS_EXCEPTION;
  37649     }
  37650     flags = JS_PROP_THROW |
  37651         JS_PROP_HAS_ENUMERABLE | JS_PROP_ENUMERABLE |
  37652         JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE;
  37653     if (magic) {
  37654         get = JS_UNDEFINED;
  37655         set = value;
  37656         flags |= JS_PROP_HAS_SET;
  37657     } else {
  37658         get = value;
  37659         set = JS_UNDEFINED;
  37660         flags |= JS_PROP_HAS_GET;
  37661     }
  37662     ret = JS_DefineProperty(ctx, obj, atom, JS_UNDEFINED, get, set, flags);
  37663     JS_FreeValue(ctx, obj);
  37664     JS_FreeAtom(ctx, atom);
  37665     if (ret < 0) {
  37666         return JS_EXCEPTION;
  37667     } else {
  37668         return JS_UNDEFINED;
  37669     }
  37670 }
  37671 
  37672 static JSValue js_object_getOwnPropertyDescriptor(JSContext *ctx, JSValueConst this_val,
  37673                                                   int argc, JSValueConst *argv, int magic)
  37674 {
  37675     JSValueConst prop;
  37676     JSAtom atom;
  37677     JSValue ret, obj;
  37678     JSPropertyDescriptor desc;
  37679     int res, flags;
  37680 
  37681     if (magic) {
  37682         /* Reflect.getOwnPropertyDescriptor case */
  37683         if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT)
  37684             return JS_ThrowTypeErrorNotAnObject(ctx);
  37685         obj = JS_DupValue(ctx, argv[0]);
  37686     } else {
  37687         obj = JS_ToObject(ctx, argv[0]);
  37688         if (JS_IsException(obj))
  37689             return obj;
  37690     }
  37691     prop = argv[1];
  37692     atom = JS_ValueToAtom(ctx, prop);
  37693     if (unlikely(atom == JS_ATOM_NULL))
  37694         goto exception;
  37695     ret = JS_UNDEFINED;
  37696     if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
  37697         res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), atom);
  37698         if (res < 0)
  37699             goto exception;
  37700         if (res) {
  37701             ret = JS_NewObject(ctx);
  37702             if (JS_IsException(ret))
  37703                 goto exception1;
  37704             flags = JS_PROP_C_W_E | JS_PROP_THROW;
  37705             if (desc.flags & JS_PROP_GETSET) {
  37706                 if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, desc.getter), flags) < 0
  37707                 ||  JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, desc.setter), flags) < 0)
  37708                     goto exception1;
  37709             } else {
  37710                 if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, desc.value), flags) < 0
  37711                 ||  JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable,
  37712                                            JS_NewBool(ctx, desc.flags & JS_PROP_WRITABLE), flags) < 0)
  37713                     goto exception1;
  37714             }
  37715             if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable,
  37716                                        JS_NewBool(ctx, desc.flags & JS_PROP_ENUMERABLE), flags) < 0
  37717             ||  JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable,
  37718                                        JS_NewBool(ctx, desc.flags & JS_PROP_CONFIGURABLE), flags) < 0)
  37719                 goto exception1;
  37720             js_free_desc(ctx, &desc);
  37721         }
  37722     }
  37723     JS_FreeAtom(ctx, atom);
  37724     JS_FreeValue(ctx, obj);
  37725     return ret;
  37726 
  37727 exception1:
  37728     js_free_desc(ctx, &desc);
  37729     JS_FreeValue(ctx, ret);
  37730 exception:
  37731     JS_FreeAtom(ctx, atom);
  37732     JS_FreeValue(ctx, obj);
  37733     return JS_EXCEPTION;
  37734 }
  37735 
  37736 static JSValue js_object_getOwnPropertyDescriptors(JSContext *ctx, JSValueConst this_val,
  37737                                                    int argc, JSValueConst *argv)
  37738 {
  37739     //getOwnPropertyDescriptors(obj)
  37740     JSValue obj, r;
  37741     JSObject *p;
  37742     JSPropertyEnum *props;
  37743     uint32_t len, i;
  37744 
  37745     r = JS_UNDEFINED;
  37746     obj = JS_ToObject(ctx, argv[0]);
  37747     if (JS_IsException(obj))
  37748         return JS_EXCEPTION;
  37749 
  37750     p = JS_VALUE_GET_OBJ(obj);
  37751     if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p,
  37752                                JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK))
  37753         goto exception;
  37754     r = JS_NewObject(ctx);
  37755     if (JS_IsException(r))
  37756         goto exception;
  37757     for(i = 0; i < len; i++) {
  37758         JSValue atomValue, desc;
  37759         JSValueConst args[2];
  37760 
  37761         atomValue = JS_AtomToValue(ctx, props[i].atom);
  37762         if (JS_IsException(atomValue))
  37763             goto exception;
  37764         args[0] = obj;
  37765         args[1] = atomValue;
  37766         desc = js_object_getOwnPropertyDescriptor(ctx, JS_UNDEFINED, 2, args, 0);
  37767         JS_FreeValue(ctx, atomValue);
  37768         if (JS_IsException(desc))
  37769             goto exception;
  37770         if (!JS_IsUndefined(desc)) {
  37771             if (JS_DefinePropertyValue(ctx, r, props[i].atom, desc,
  37772                                        JS_PROP_C_W_E | JS_PROP_THROW) < 0)
  37773                 goto exception;
  37774         }
  37775     }
  37776     js_free_prop_enum(ctx, props, len);
  37777     JS_FreeValue(ctx, obj);
  37778     return r;
  37779 
  37780 exception:
  37781     js_free_prop_enum(ctx, props, len);
  37782     JS_FreeValue(ctx, obj);
  37783     JS_FreeValue(ctx, r);
  37784     return JS_EXCEPTION;
  37785 }
  37786 
  37787 static JSValue JS_GetOwnPropertyNames2(JSContext *ctx, JSValueConst obj1,
  37788                                        int flags, int kind)
  37789 {
  37790     JSValue obj, r, val, key, value;
  37791     JSObject *p;
  37792     JSPropertyEnum *atoms;
  37793     uint32_t len, i, j;
  37794 
  37795     r = JS_UNDEFINED;
  37796     val = JS_UNDEFINED;
  37797     obj = JS_ToObject(ctx, obj1);
  37798     if (JS_IsException(obj))
  37799         return JS_EXCEPTION;
  37800     p = JS_VALUE_GET_OBJ(obj);
  37801     if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, flags & ~JS_GPN_ENUM_ONLY))
  37802         goto exception;
  37803     r = JS_NewArray(ctx);
  37804     if (JS_IsException(r))
  37805         goto exception;
  37806     for(j = i = 0; i < len; i++) {
  37807         JSAtom atom = atoms[i].atom;
  37808         if (flags & JS_GPN_ENUM_ONLY) {
  37809             JSPropertyDescriptor desc;
  37810             int res;
  37811 
  37812             /* Check if property is still enumerable */
  37813             res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);
  37814             if (res < 0)
  37815                 goto exception;
  37816             if (!res)
  37817                 continue;
  37818             js_free_desc(ctx, &desc);
  37819             if (!(desc.flags & JS_PROP_ENUMERABLE))
  37820                 continue;
  37821         }
  37822         switch(kind) {
  37823         default:
  37824         case JS_ITERATOR_KIND_KEY:
  37825             val = JS_AtomToValue(ctx, atom);
  37826             if (JS_IsException(val))
  37827                 goto exception;
  37828             break;
  37829         case JS_ITERATOR_KIND_VALUE:
  37830             val = JS_GetProperty(ctx, obj, atom);
  37831             if (JS_IsException(val))
  37832                 goto exception;
  37833             break;
  37834         case JS_ITERATOR_KIND_KEY_AND_VALUE:
  37835             val = JS_NewArray(ctx);
  37836             if (JS_IsException(val))
  37837                 goto exception;
  37838             key = JS_AtomToValue(ctx, atom);
  37839             if (JS_IsException(key))
  37840                 goto exception1;
  37841             if (JS_CreateDataPropertyUint32(ctx, val, 0, key, JS_PROP_THROW) < 0)
  37842                 goto exception1;
  37843             value = JS_GetProperty(ctx, obj, atom);
  37844             if (JS_IsException(value))
  37845                 goto exception1;
  37846             if (JS_CreateDataPropertyUint32(ctx, val, 1, value, JS_PROP_THROW) < 0)
  37847                 goto exception1;
  37848             break;
  37849         }
  37850         if (JS_CreateDataPropertyUint32(ctx, r, j++, val, 0) < 0)
  37851             goto exception;
  37852     }
  37853     goto done;
  37854 
  37855 exception1:
  37856     JS_FreeValue(ctx, val);
  37857 exception:
  37858     JS_FreeValue(ctx, r);
  37859     r = JS_EXCEPTION;
  37860 done:
  37861     js_free_prop_enum(ctx, atoms, len);
  37862     JS_FreeValue(ctx, obj);
  37863     return r;
  37864 }
  37865 
  37866 static JSValue js_object_getOwnPropertyNames(JSContext *ctx, JSValueConst this_val,
  37867                                              int argc, JSValueConst *argv)
  37868 {
  37869     return JS_GetOwnPropertyNames2(ctx, argv[0],
  37870                                    JS_GPN_STRING_MASK, JS_ITERATOR_KIND_KEY);
  37871 }
  37872 
  37873 static JSValue js_object_getOwnPropertySymbols(JSContext *ctx, JSValueConst this_val,
  37874                                              int argc, JSValueConst *argv)
  37875 {
  37876     return JS_GetOwnPropertyNames2(ctx, argv[0],
  37877                                    JS_GPN_SYMBOL_MASK, JS_ITERATOR_KIND_KEY);
  37878 }
  37879 
  37880 static JSValue js_object_keys(JSContext *ctx, JSValueConst this_val,
  37881                               int argc, JSValueConst *argv, int kind)
  37882 {
  37883     return JS_GetOwnPropertyNames2(ctx, argv[0],
  37884                                    JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK, kind);
  37885 }
  37886 
  37887 static JSValue js_object_isExtensible(JSContext *ctx, JSValueConst this_val,
  37888                                       int argc, JSValueConst *argv, int reflect)
  37889 {
  37890     JSValueConst obj;
  37891     int ret;
  37892 
  37893     obj = argv[0];
  37894     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
  37895         if (reflect)
  37896             return JS_ThrowTypeErrorNotAnObject(ctx);
  37897         else
  37898             return JS_FALSE;
  37899     }
  37900     ret = JS_IsExtensible(ctx, obj);
  37901     if (ret < 0)
  37902         return JS_EXCEPTION;
  37903     else
  37904         return JS_NewBool(ctx, ret);
  37905 }
  37906 
  37907 static JSValue js_object_preventExtensions(JSContext *ctx, JSValueConst this_val,
  37908                                            int argc, JSValueConst *argv, int reflect)
  37909 {
  37910     JSValueConst obj;
  37911     int ret;
  37912 
  37913     obj = argv[0];
  37914     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
  37915         if (reflect)
  37916             return JS_ThrowTypeErrorNotAnObject(ctx);
  37917         else
  37918             return JS_DupValue(ctx, obj);
  37919     }
  37920     ret = JS_PreventExtensions(ctx, obj);
  37921     if (ret < 0)
  37922         return JS_EXCEPTION;
  37923     if (reflect) {
  37924         return JS_NewBool(ctx, ret);
  37925     } else {
  37926         if (!ret)
  37927             return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false");
  37928         return JS_DupValue(ctx, obj);
  37929     }
  37930 }
  37931 
  37932 static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValueConst this_val,
  37933                                         int argc, JSValueConst *argv)
  37934 {
  37935     JSValue obj;
  37936     JSAtom atom;
  37937     JSObject *p;
  37938     BOOL ret;
  37939 
  37940     atom = JS_ValueToAtom(ctx, argv[0]); /* must be done first */
  37941     if (unlikely(atom == JS_ATOM_NULL))
  37942         return JS_EXCEPTION;
  37943     obj = JS_ToObject(ctx, this_val);
  37944     if (JS_IsException(obj)) {
  37945         JS_FreeAtom(ctx, atom);
  37946         return obj;
  37947     }
  37948     p = JS_VALUE_GET_OBJ(obj);
  37949     ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom);
  37950     JS_FreeAtom(ctx, atom);
  37951     JS_FreeValue(ctx, obj);
  37952     if (ret < 0)
  37953         return JS_EXCEPTION;
  37954     else
  37955         return JS_NewBool(ctx, ret);
  37956 }
  37957 
  37958 static JSValue js_object_hasOwn(JSContext *ctx, JSValueConst this_val,
  37959                                 int argc, JSValueConst *argv)
  37960 {
  37961     JSValue obj;
  37962     JSAtom atom;
  37963     JSObject *p;
  37964     BOOL ret;
  37965 
  37966     obj = JS_ToObject(ctx, argv[0]);
  37967     if (JS_IsException(obj))
  37968         return obj;
  37969     atom = JS_ValueToAtom(ctx, argv[1]);
  37970     if (unlikely(atom == JS_ATOM_NULL)) {
  37971         JS_FreeValue(ctx, obj);
  37972         return JS_EXCEPTION;
  37973     }
  37974     p = JS_VALUE_GET_OBJ(obj);
  37975     ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom);
  37976     JS_FreeAtom(ctx, atom);
  37977     JS_FreeValue(ctx, obj);
  37978     if (ret < 0)
  37979         return JS_EXCEPTION;
  37980     else
  37981         return JS_NewBool(ctx, ret);
  37982 }
  37983 
  37984 static JSValue js_object_valueOf(JSContext *ctx, JSValueConst this_val,
  37985                                  int argc, JSValueConst *argv)
  37986 {
  37987     return JS_ToObject(ctx, this_val);
  37988 }
  37989 
  37990 static JSValue js_object_toString(JSContext *ctx, JSValueConst this_val,
  37991                                   int argc, JSValueConst *argv)
  37992 {
  37993     JSValue obj, tag;
  37994     int is_array;
  37995     JSAtom atom;
  37996     JSObject *p;
  37997 
  37998     if (JS_IsNull(this_val)) {
  37999         tag = JS_NewString(ctx, "Null");
  38000     } else if (JS_IsUndefined(this_val)) {
  38001         tag = JS_NewString(ctx, "Undefined");
  38002     } else {
  38003         obj = JS_ToObject(ctx, this_val);
  38004         if (JS_IsException(obj))
  38005             return obj;
  38006         is_array = JS_IsArray(ctx, obj);
  38007         if (is_array < 0) {
  38008             JS_FreeValue(ctx, obj);
  38009             return JS_EXCEPTION;
  38010         }
  38011         if (is_array) {
  38012             atom = JS_ATOM_Array;
  38013         } else if (JS_IsFunction(ctx, obj)) {
  38014             atom = JS_ATOM_Function;
  38015         } else {
  38016             p = JS_VALUE_GET_OBJ(obj);
  38017             switch(p->class_id) {
  38018             case JS_CLASS_STRING:
  38019             case JS_CLASS_ARGUMENTS:
  38020             case JS_CLASS_MAPPED_ARGUMENTS:
  38021             case JS_CLASS_ERROR:
  38022             case JS_CLASS_BOOLEAN:
  38023             case JS_CLASS_NUMBER:
  38024             case JS_CLASS_DATE:
  38025             case JS_CLASS_REGEXP:
  38026                 atom = ctx->rt->class_array[p->class_id].class_name;
  38027                 break;
  38028             default:
  38029                 atom = JS_ATOM_Object;
  38030                 break;
  38031             }
  38032         }
  38033         tag = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_toStringTag);
  38034         JS_FreeValue(ctx, obj);
  38035         if (JS_IsException(tag))
  38036             return JS_EXCEPTION;
  38037         if (!JS_IsString(tag)) {
  38038             JS_FreeValue(ctx, tag);
  38039             tag = JS_AtomToString(ctx, atom);
  38040         }
  38041     }
  38042     return JS_ConcatString3(ctx, "[object ", tag, "]");
  38043 }
  38044 
  38045 static JSValue js_object_toLocaleString(JSContext *ctx, JSValueConst this_val,
  38046                                         int argc, JSValueConst *argv)
  38047 {
  38048     return JS_Invoke(ctx, this_val, JS_ATOM_toString, 0, NULL);
  38049 }
  38050 
  38051 static JSValue js_object_assign(JSContext *ctx, JSValueConst this_val,
  38052                                 int argc, JSValueConst *argv)
  38053 {
  38054     // Object.assign(obj, source1)
  38055     JSValue obj, s;
  38056     int i;
  38057 
  38058     s = JS_UNDEFINED;
  38059     obj = JS_ToObject(ctx, argv[0]);
  38060     if (JS_IsException(obj))
  38061         goto exception;
  38062     for (i = 1; i < argc; i++) {
  38063         if (!JS_IsNull(argv[i]) && !JS_IsUndefined(argv[i])) {
  38064             s = JS_ToObject(ctx, argv[i]);
  38065             if (JS_IsException(s))
  38066                 goto exception;
  38067             if (JS_CopyDataProperties(ctx, obj, s, JS_UNDEFINED, TRUE))
  38068                 goto exception;
  38069             JS_FreeValue(ctx, s);
  38070         }
  38071     }
  38072     return obj;
  38073 exception:
  38074     JS_FreeValue(ctx, obj);
  38075     JS_FreeValue(ctx, s);
  38076     return JS_EXCEPTION;
  38077 }
  38078 
  38079 static JSValue js_object_seal(JSContext *ctx, JSValueConst this_val,
  38080                               int argc, JSValueConst *argv, int freeze_flag)
  38081 {
  38082     JSValueConst obj = argv[0];
  38083     JSObject *p;
  38084     JSPropertyEnum *props;
  38085     uint32_t len, i;
  38086     int flags, desc_flags, res;
  38087 
  38088     if (!JS_IsObject(obj))
  38089         return JS_DupValue(ctx, obj);
  38090 
  38091     res = JS_PreventExtensions(ctx, obj);
  38092     if (res < 0)
  38093         return JS_EXCEPTION;
  38094     if (!res) {
  38095         return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false");
  38096     }
  38097 
  38098     p = JS_VALUE_GET_OBJ(obj);
  38099     flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK;
  38100     if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags))
  38101         return JS_EXCEPTION;
  38102 
  38103     for(i = 0; i < len; i++) {
  38104         JSPropertyDescriptor desc;
  38105         JSAtom prop = props[i].atom;
  38106 
  38107         desc_flags = JS_PROP_THROW | JS_PROP_HAS_CONFIGURABLE;
  38108         if (freeze_flag) {
  38109             res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
  38110             if (res < 0)
  38111                 goto exception;
  38112             if (res) {
  38113                 if (desc.flags & JS_PROP_WRITABLE)
  38114                     desc_flags |= JS_PROP_HAS_WRITABLE;
  38115                 js_free_desc(ctx, &desc);
  38116             }
  38117         }
  38118         if (JS_DefineProperty(ctx, obj, prop, JS_UNDEFINED,
  38119                               JS_UNDEFINED, JS_UNDEFINED, desc_flags) < 0)
  38120             goto exception;
  38121     }
  38122     js_free_prop_enum(ctx, props, len);
  38123     return JS_DupValue(ctx, obj);
  38124 
  38125  exception:
  38126     js_free_prop_enum(ctx, props, len);
  38127     return JS_EXCEPTION;
  38128 }
  38129 
  38130 static JSValue js_object_isSealed(JSContext *ctx, JSValueConst this_val,
  38131                                   int argc, JSValueConst *argv, int is_frozen)
  38132 {
  38133     JSValueConst obj = argv[0];
  38134     JSObject *p;
  38135     JSPropertyEnum *props;
  38136     uint32_t len, i;
  38137     int flags, res;
  38138 
  38139     if (!JS_IsObject(obj))
  38140         return JS_TRUE;
  38141 
  38142     p = JS_VALUE_GET_OBJ(obj);
  38143     flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK;
  38144     if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags))
  38145         return JS_EXCEPTION;
  38146 
  38147     for(i = 0; i < len; i++) {
  38148         JSPropertyDescriptor desc;
  38149         JSAtom prop = props[i].atom;
  38150 
  38151         res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
  38152         if (res < 0)
  38153             goto exception;
  38154         if (res) {
  38155             js_free_desc(ctx, &desc);
  38156             if ((desc.flags & JS_PROP_CONFIGURABLE)
  38157             ||  (is_frozen && (desc.flags & JS_PROP_WRITABLE))) {
  38158                 res = FALSE;
  38159                 goto done;
  38160             }
  38161         }
  38162     }
  38163     res = JS_IsExtensible(ctx, obj);
  38164     if (res < 0)
  38165         return JS_EXCEPTION;
  38166     res ^= 1;
  38167 done:
  38168     js_free_prop_enum(ctx, props, len);
  38169     return JS_NewBool(ctx, res);
  38170 
  38171 exception:
  38172     js_free_prop_enum(ctx, props, len);
  38173     return JS_EXCEPTION;
  38174 }
  38175 
  38176 static JSValue js_object_fromEntries(JSContext *ctx, JSValueConst this_val,
  38177                                      int argc, JSValueConst *argv)
  38178 {
  38179     JSValue obj, iter, next_method = JS_UNDEFINED;
  38180     JSValueConst iterable;
  38181     BOOL done;
  38182 
  38183     /*  RequireObjectCoercible() not necessary because it is tested in
  38184         JS_GetIterator() by JS_GetProperty() */
  38185     iterable = argv[0];
  38186 
  38187     obj = JS_NewObject(ctx);
  38188     if (JS_IsException(obj))
  38189         return obj;
  38190 
  38191     iter = JS_GetIterator(ctx, iterable, FALSE);
  38192     if (JS_IsException(iter))
  38193         goto fail;
  38194     next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
  38195     if (JS_IsException(next_method))
  38196         goto fail;
  38197 
  38198     for(;;) {
  38199         JSValue key, value, item;
  38200         item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
  38201         if (JS_IsException(item))
  38202             goto fail;
  38203         if (done) {
  38204             JS_FreeValue(ctx, item);
  38205             break;
  38206         }
  38207 
  38208         key = JS_UNDEFINED;
  38209         value = JS_UNDEFINED;
  38210         if (!JS_IsObject(item)) {
  38211             JS_ThrowTypeErrorNotAnObject(ctx);
  38212             goto fail1;
  38213         }
  38214         key = JS_GetPropertyUint32(ctx, item, 0);
  38215         if (JS_IsException(key))
  38216             goto fail1;
  38217         value = JS_GetPropertyUint32(ctx, item, 1);
  38218         if (JS_IsException(value)) {
  38219             JS_FreeValue(ctx, key);
  38220             goto fail1;
  38221         }
  38222         if (JS_DefinePropertyValueValue(ctx, obj, key, value,
  38223                                         JS_PROP_C_W_E | JS_PROP_THROW) < 0) {
  38224         fail1:
  38225             JS_FreeValue(ctx, item);
  38226             goto fail;
  38227         }
  38228         JS_FreeValue(ctx, item);
  38229     }
  38230     JS_FreeValue(ctx, next_method);
  38231     JS_FreeValue(ctx, iter);
  38232     return obj;
  38233  fail:
  38234     if (JS_IsObject(iter)) {
  38235         /* close the iterator object, preserving pending exception */
  38236         JS_IteratorClose(ctx, iter, TRUE);
  38237     }
  38238     JS_FreeValue(ctx, next_method);
  38239     JS_FreeValue(ctx, iter);
  38240     JS_FreeValue(ctx, obj);
  38241     return JS_EXCEPTION;
  38242 }
  38243 
  38244 #if 0
  38245 /* Note: corresponds to ECMA spec: CreateDataPropertyOrThrow() */
  38246 static JSValue js_object___setOwnProperty(JSContext *ctx, JSValueConst this_val,
  38247                                           int argc, JSValueConst *argv)
  38248 {
  38249     int ret;
  38250     ret = JS_DefinePropertyValueValue(ctx, argv[0], JS_DupValue(ctx, argv[1]),
  38251                                       JS_DupValue(ctx, argv[2]),
  38252                                       JS_PROP_C_W_E | JS_PROP_THROW);
  38253     if (ret < 0)
  38254         return JS_EXCEPTION;
  38255     else
  38256         return JS_NewBool(ctx, ret);
  38257 }
  38258 
  38259 static JSValue js_object___toObject(JSContext *ctx, JSValueConst this_val,
  38260                                     int argc, JSValueConst *argv)
  38261 {
  38262     return JS_ToObject(ctx, argv[0]);
  38263 }
  38264 
  38265 static JSValue js_object___toPrimitive(JSContext *ctx, JSValueConst this_val,
  38266                                        int argc, JSValueConst *argv)
  38267 {
  38268     int hint = HINT_NONE;
  38269 
  38270     if (JS_VALUE_GET_TAG(argv[1]) == JS_TAG_INT)
  38271         hint = JS_VALUE_GET_INT(argv[1]);
  38272 
  38273     return JS_ToPrimitive(ctx, argv[0], hint);
  38274 }
  38275 #endif
  38276 
  38277 /* return an empty string if not an object */
  38278 static JSValue js_object___getClass(JSContext *ctx, JSValueConst this_val,
  38279                                     int argc, JSValueConst *argv)
  38280 {
  38281     JSAtom atom;
  38282     JSObject *p;
  38283     uint32_t tag;
  38284     int class_id;
  38285 
  38286     tag = JS_VALUE_GET_NORM_TAG(argv[0]);
  38287     if (tag == JS_TAG_OBJECT) {
  38288         p = JS_VALUE_GET_OBJ(argv[0]);
  38289         class_id = p->class_id;
  38290         if (class_id == JS_CLASS_PROXY && JS_IsFunction(ctx, argv[0]))
  38291             class_id = JS_CLASS_BYTECODE_FUNCTION;
  38292         atom = ctx->rt->class_array[class_id].class_name;
  38293     } else {
  38294         atom = JS_ATOM_empty_string;
  38295     }
  38296     return JS_AtomToString(ctx, atom);
  38297 }
  38298 
  38299 static JSValue js_object_is(JSContext *ctx, JSValueConst this_val,
  38300                             int argc, JSValueConst *argv)
  38301 {
  38302     return JS_NewBool(ctx, js_same_value(ctx, argv[0], argv[1]));
  38303 }
  38304 
  38305 #if 0
  38306 static JSValue js_object___getObjectData(JSContext *ctx, JSValueConst this_val,
  38307                                          int argc, JSValueConst *argv)
  38308 {
  38309     return JS_GetObjectData(ctx, argv[0]);
  38310 }
  38311 
  38312 static JSValue js_object___setObjectData(JSContext *ctx, JSValueConst this_val,
  38313                                          int argc, JSValueConst *argv)
  38314 {
  38315     if (JS_SetObjectData(ctx, argv[0], JS_DupValue(ctx, argv[1])))
  38316         return JS_EXCEPTION;
  38317     return JS_DupValue(ctx, argv[1]);
  38318 }
  38319 
  38320 static JSValue js_object___toPropertyKey(JSContext *ctx, JSValueConst this_val,
  38321                                          int argc, JSValueConst *argv)
  38322 {
  38323     return JS_ToPropertyKey(ctx, argv[0]);
  38324 }
  38325 
  38326 static JSValue js_object___isObject(JSContext *ctx, JSValueConst this_val,
  38327                                     int argc, JSValueConst *argv)
  38328 {
  38329     return JS_NewBool(ctx, JS_IsObject(argv[0]));
  38330 }
  38331 
  38332 static JSValue js_object___isSameValueZero(JSContext *ctx, JSValueConst this_val,
  38333                                            int argc, JSValueConst *argv)
  38334 {
  38335     return JS_NewBool(ctx, js_same_value_zero(ctx, argv[0], argv[1]));
  38336 }
  38337 
  38338 static JSValue js_object___isConstructor(JSContext *ctx, JSValueConst this_val,
  38339                                          int argc, JSValueConst *argv)
  38340 {
  38341     return JS_NewBool(ctx, JS_IsConstructor(ctx, argv[0]));
  38342 }
  38343 #endif
  38344 
  38345 static JSValue JS_SpeciesConstructor(JSContext *ctx, JSValueConst obj,
  38346                                      JSValueConst defaultConstructor)
  38347 {
  38348     JSValue ctor, species;
  38349 
  38350     if (!JS_IsObject(obj))
  38351         return JS_ThrowTypeErrorNotAnObject(ctx);
  38352     ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor);
  38353     if (JS_IsException(ctor))
  38354         return ctor;
  38355     if (JS_IsUndefined(ctor))
  38356         return JS_DupValue(ctx, defaultConstructor);
  38357     if (!JS_IsObject(ctor)) {
  38358         JS_FreeValue(ctx, ctor);
  38359         return JS_ThrowTypeErrorNotAnObject(ctx);
  38360     }
  38361     species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species);
  38362     JS_FreeValue(ctx, ctor);
  38363     if (JS_IsException(species))
  38364         return species;
  38365     if (JS_IsUndefined(species) || JS_IsNull(species))
  38366         return JS_DupValue(ctx, defaultConstructor);
  38367     if (!JS_IsConstructor(ctx, species)) {
  38368         JS_FreeValue(ctx, species);
  38369         return JS_ThrowTypeError(ctx, "not a constructor");
  38370     }
  38371     return species;
  38372 }
  38373 
  38374 #if 0
  38375 static JSValue js_object___speciesConstructor(JSContext *ctx, JSValueConst this_val,
  38376                                               int argc, JSValueConst *argv)
  38377 {
  38378     return JS_SpeciesConstructor(ctx, argv[0], argv[1]);
  38379 }
  38380 #endif
  38381 
  38382 static JSValue js_object_get___proto__(JSContext *ctx, JSValueConst this_val)
  38383 {
  38384     JSValue val, ret;
  38385 
  38386     val = JS_ToObject(ctx, this_val);
  38387     if (JS_IsException(val))
  38388         return val;
  38389     ret = JS_GetPrototype(ctx, val);
  38390     JS_FreeValue(ctx, val);
  38391     return ret;
  38392 }
  38393 
  38394 static JSValue js_object_set___proto__(JSContext *ctx, JSValueConst this_val,
  38395                                        JSValueConst proto)
  38396 {
  38397     if (JS_IsUndefined(this_val) || JS_IsNull(this_val))
  38398         return JS_ThrowTypeErrorNotAnObject(ctx);
  38399     if (!JS_IsObject(proto) && !JS_IsNull(proto))
  38400         return JS_UNDEFINED;
  38401     if (JS_SetPrototypeInternal(ctx, this_val, proto, TRUE) < 0)
  38402         return JS_EXCEPTION;
  38403     else
  38404         return JS_UNDEFINED;
  38405 }
  38406 
  38407 static JSValue js_object_isPrototypeOf(JSContext *ctx, JSValueConst this_val,
  38408                                        int argc, JSValueConst *argv)
  38409 {
  38410     JSValue obj, v1;
  38411     JSValueConst v;
  38412     int res;
  38413 
  38414     v = argv[0];
  38415     if (!JS_IsObject(v))
  38416         return JS_FALSE;
  38417     obj = JS_ToObject(ctx, this_val);
  38418     if (JS_IsException(obj))
  38419         return JS_EXCEPTION;
  38420     v1 = JS_DupValue(ctx, v);
  38421     for(;;) {
  38422         v1 = JS_GetPrototypeFree(ctx, v1);
  38423         if (JS_IsException(v1))
  38424             goto exception;
  38425         if (JS_IsNull(v1)) {
  38426             res = FALSE;
  38427             break;
  38428         }
  38429         if (JS_VALUE_GET_OBJ(obj) == JS_VALUE_GET_OBJ(v1)) {
  38430             res = TRUE;
  38431             break;
  38432         }
  38433         /* avoid infinite loop (possible with proxies) */
  38434         if (js_poll_interrupts(ctx))
  38435             goto exception;
  38436     }
  38437     JS_FreeValue(ctx, v1);
  38438     JS_FreeValue(ctx, obj);
  38439     return JS_NewBool(ctx, res);
  38440 
  38441 exception:
  38442     JS_FreeValue(ctx, v1);
  38443     JS_FreeValue(ctx, obj);
  38444     return JS_EXCEPTION;
  38445 }
  38446 
  38447 static JSValue js_object_propertyIsEnumerable(JSContext *ctx, JSValueConst this_val,
  38448                                               int argc, JSValueConst *argv)
  38449 {
  38450     JSValue obj, res = JS_EXCEPTION;
  38451     JSAtom prop = JS_ATOM_NULL;
  38452     JSPropertyDescriptor desc;
  38453     int has_prop;
  38454 
  38455     obj = JS_ToObject(ctx, this_val);
  38456     if (JS_IsException(obj))
  38457         goto exception;
  38458     prop = JS_ValueToAtom(ctx, argv[0]);
  38459     if (unlikely(prop == JS_ATOM_NULL))
  38460         goto exception;
  38461 
  38462     has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop);
  38463     if (has_prop < 0)
  38464         goto exception;
  38465     if (has_prop) {
  38466         res = JS_NewBool(ctx, desc.flags & JS_PROP_ENUMERABLE);
  38467         js_free_desc(ctx, &desc);
  38468     } else {
  38469         res = JS_FALSE;
  38470     }
  38471 
  38472 exception:
  38473     JS_FreeAtom(ctx, prop);
  38474     JS_FreeValue(ctx, obj);
  38475     return res;
  38476 }
  38477 
  38478 static JSValue js_object___lookupGetter__(JSContext *ctx, JSValueConst this_val,
  38479                                           int argc, JSValueConst *argv, int setter)
  38480 {
  38481     JSValue obj, res = JS_EXCEPTION;
  38482     JSAtom prop = JS_ATOM_NULL;
  38483     JSPropertyDescriptor desc;
  38484     int has_prop;
  38485 
  38486     obj = JS_ToObject(ctx, this_val);
  38487     if (JS_IsException(obj))
  38488         goto exception;
  38489     prop = JS_ValueToAtom(ctx, argv[0]);
  38490     if (unlikely(prop == JS_ATOM_NULL))
  38491         goto exception;
  38492 
  38493     for (;;) {
  38494         has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop);
  38495         if (has_prop < 0)
  38496             goto exception;
  38497         if (has_prop) {
  38498             if (desc.flags & JS_PROP_GETSET)
  38499                 res = JS_DupValue(ctx, setter ? desc.setter : desc.getter);
  38500             else
  38501                 res = JS_UNDEFINED;
  38502             js_free_desc(ctx, &desc);
  38503             break;
  38504         }
  38505         obj = JS_GetPrototypeFree(ctx, obj);
  38506         if (JS_IsException(obj))
  38507             goto exception;
  38508         if (JS_IsNull(obj)) {
  38509             res = JS_UNDEFINED;
  38510             break;
  38511         }
  38512         /* avoid infinite loop (possible with proxies) */
  38513         if (js_poll_interrupts(ctx))
  38514             goto exception;
  38515     }
  38516 
  38517 exception:
  38518     JS_FreeAtom(ctx, prop);
  38519     JS_FreeValue(ctx, obj);
  38520     return res;
  38521 }
  38522 
  38523 static const JSCFunctionListEntry js_object_funcs[] = {
  38524     JS_CFUNC_DEF("create", 2, js_object_create ),
  38525     JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 0 ),
  38526     JS_CFUNC_DEF("setPrototypeOf", 2, js_object_setPrototypeOf ),
  38527     JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 0 ),
  38528     JS_CFUNC_DEF("defineProperties", 2, js_object_defineProperties ),
  38529     JS_CFUNC_DEF("getOwnPropertyNames", 1, js_object_getOwnPropertyNames ),
  38530     JS_CFUNC_DEF("getOwnPropertySymbols", 1, js_object_getOwnPropertySymbols ),
  38531     JS_CFUNC_MAGIC_DEF("groupBy", 2, js_object_groupBy, 0 ),
  38532     JS_CFUNC_MAGIC_DEF("keys", 1, js_object_keys, JS_ITERATOR_KIND_KEY ),
  38533     JS_CFUNC_MAGIC_DEF("values", 1, js_object_keys, JS_ITERATOR_KIND_VALUE ),
  38534     JS_CFUNC_MAGIC_DEF("entries", 1, js_object_keys, JS_ITERATOR_KIND_KEY_AND_VALUE ),
  38535     JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 0 ),
  38536     JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 0 ),
  38537     JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 0 ),
  38538     JS_CFUNC_DEF("getOwnPropertyDescriptors", 1, js_object_getOwnPropertyDescriptors ),
  38539     JS_CFUNC_DEF("is", 2, js_object_is ),
  38540     JS_CFUNC_DEF("assign", 2, js_object_assign ),
  38541     JS_CFUNC_MAGIC_DEF("seal", 1, js_object_seal, 0 ),
  38542     JS_CFUNC_MAGIC_DEF("freeze", 1, js_object_seal, 1 ),
  38543     JS_CFUNC_MAGIC_DEF("isSealed", 1, js_object_isSealed, 0 ),
  38544     JS_CFUNC_MAGIC_DEF("isFrozen", 1, js_object_isSealed, 1 ),
  38545     JS_CFUNC_DEF("__getClass", 1, js_object___getClass ),
  38546     //JS_CFUNC_DEF("__isObject", 1, js_object___isObject ),
  38547     //JS_CFUNC_DEF("__isConstructor", 1, js_object___isConstructor ),
  38548     //JS_CFUNC_DEF("__toObject", 1, js_object___toObject ),
  38549     //JS_CFUNC_DEF("__setOwnProperty", 3, js_object___setOwnProperty ),
  38550     //JS_CFUNC_DEF("__toPrimitive", 2, js_object___toPrimitive ),
  38551     //JS_CFUNC_DEF("__toPropertyKey", 1, js_object___toPropertyKey ),
  38552     //JS_CFUNC_DEF("__speciesConstructor", 2, js_object___speciesConstructor ),
  38553     //JS_CFUNC_DEF("__isSameValueZero", 2, js_object___isSameValueZero ),
  38554     //JS_CFUNC_DEF("__getObjectData", 1, js_object___getObjectData ),
  38555     //JS_CFUNC_DEF("__setObjectData", 2, js_object___setObjectData ),
  38556     JS_CFUNC_DEF("fromEntries", 1, js_object_fromEntries ),
  38557     JS_CFUNC_DEF("hasOwn", 2, js_object_hasOwn ),
  38558 };
  38559 
  38560 static const JSCFunctionListEntry js_object_proto_funcs[] = {
  38561     JS_CFUNC_DEF("toString", 0, js_object_toString ),
  38562     JS_CFUNC_DEF("toLocaleString", 0, js_object_toLocaleString ),
  38563     JS_CFUNC_DEF("valueOf", 0, js_object_valueOf ),
  38564     JS_CFUNC_DEF("hasOwnProperty", 1, js_object_hasOwnProperty ),
  38565     JS_CFUNC_DEF("isPrototypeOf", 1, js_object_isPrototypeOf ),
  38566     JS_CFUNC_DEF("propertyIsEnumerable", 1, js_object_propertyIsEnumerable ),
  38567     JS_CGETSET_DEF("__proto__", js_object_get___proto__, js_object_set___proto__ ),
  38568     JS_CFUNC_MAGIC_DEF("__defineGetter__", 2, js_object___defineGetter__, 0 ),
  38569     JS_CFUNC_MAGIC_DEF("__defineSetter__", 2, js_object___defineGetter__, 1 ),
  38570     JS_CFUNC_MAGIC_DEF("__lookupGetter__", 1, js_object___lookupGetter__, 0 ),
  38571     JS_CFUNC_MAGIC_DEF("__lookupSetter__", 1, js_object___lookupGetter__, 1 ),
  38572 };
  38573 
  38574 /* Function class */
  38575 
  38576 static JSValue js_function_proto(JSContext *ctx, JSValueConst this_val,
  38577                                  int argc, JSValueConst *argv)
  38578 {
  38579     return JS_UNDEFINED;
  38580 }
  38581 
  38582 /* XXX: add a specific eval mode so that Function("}), ({") is rejected */
  38583 static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target,
  38584                                        int argc, JSValueConst *argv, int magic)
  38585 {
  38586     JSFunctionKindEnum func_kind = magic;
  38587     int i, n, ret;
  38588     JSValue s, proto, obj = JS_UNDEFINED;
  38589     StringBuffer b_s, *b = &b_s;
  38590 
  38591     string_buffer_init(ctx, b, 0);
  38592     string_buffer_putc8(b, '(');
  38593 
  38594     if (func_kind == JS_FUNC_ASYNC || func_kind == JS_FUNC_ASYNC_GENERATOR) {
  38595         string_buffer_puts8(b, "async ");
  38596     }
  38597     string_buffer_puts8(b, "function");
  38598 
  38599     if (func_kind == JS_FUNC_GENERATOR || func_kind == JS_FUNC_ASYNC_GENERATOR) {
  38600         string_buffer_putc8(b, '*');
  38601     }
  38602     string_buffer_puts8(b, " anonymous(");
  38603 
  38604     n = argc - 1;
  38605     for(i = 0; i < n; i++) {
  38606         if (i != 0) {
  38607             string_buffer_putc8(b, ',');
  38608         }
  38609         if (string_buffer_concat_value(b, argv[i]))
  38610             goto fail;
  38611     }
  38612     string_buffer_puts8(b, "\n) {\n");
  38613     if (n >= 0) {
  38614         if (string_buffer_concat_value(b, argv[n]))
  38615             goto fail;
  38616     }
  38617     string_buffer_puts8(b, "\n})");
  38618     s = string_buffer_end(b);
  38619     if (JS_IsException(s))
  38620         goto fail1;
  38621 
  38622     obj = JS_EvalObject(ctx, ctx->global_obj, s, JS_EVAL_TYPE_INDIRECT, -1);
  38623     JS_FreeValue(ctx, s);
  38624     if (JS_IsException(obj))
  38625         goto fail1;
  38626     if (!JS_IsUndefined(new_target)) {
  38627         /* set the prototype */
  38628         proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype);
  38629         if (JS_IsException(proto))
  38630             goto fail1;
  38631         if (!JS_IsObject(proto)) {
  38632             JSContext *realm;
  38633             JS_FreeValue(ctx, proto);
  38634             realm = JS_GetFunctionRealm(ctx, new_target);
  38635             if (!realm)
  38636                 goto fail1;
  38637             proto = JS_DupValue(ctx, realm->class_proto[func_kind_to_class_id[func_kind]]);
  38638         }
  38639         ret = JS_SetPrototypeInternal(ctx, obj, proto, TRUE);
  38640         JS_FreeValue(ctx, proto);
  38641         if (ret < 0)
  38642             goto fail1;
  38643     }
  38644     return obj;
  38645 
  38646  fail:
  38647     string_buffer_free(b);
  38648  fail1:
  38649     JS_FreeValue(ctx, obj);
  38650     return JS_EXCEPTION;
  38651 }
  38652 
  38653 static __exception int js_get_length32(JSContext *ctx, uint32_t *pres,
  38654                                        JSValueConst obj)
  38655 {
  38656     JSValue len_val;
  38657     len_val = JS_GetProperty(ctx, obj, JS_ATOM_length);
  38658     if (JS_IsException(len_val)) {
  38659         *pres = 0;
  38660         return -1;
  38661     }
  38662     return JS_ToUint32Free(ctx, pres, len_val);
  38663 }
  38664 
  38665 static __exception int js_get_length64(JSContext *ctx, int64_t *pres,
  38666                                        JSValueConst obj)
  38667 {
  38668     JSValue len_val;
  38669     len_val = JS_GetProperty(ctx, obj, JS_ATOM_length);
  38670     if (JS_IsException(len_val)) {
  38671         *pres = 0;
  38672         return -1;
  38673     }
  38674     return JS_ToLengthFree(ctx, pres, len_val);
  38675 }
  38676 
  38677 static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len)
  38678 {
  38679     uint32_t i;
  38680     for(i = 0; i < len; i++) {
  38681         JS_FreeValue(ctx, tab[i]);
  38682     }
  38683     js_free(ctx, tab);
  38684 }
  38685 
  38686 /* XXX: should use ValueArray */
  38687 static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen,
  38688                                JSValueConst array_arg)
  38689 {
  38690     uint32_t len, i;
  38691     JSValue *tab, ret;
  38692     JSObject *p;
  38693 
  38694     if (JS_VALUE_GET_TAG(array_arg) != JS_TAG_OBJECT) {
  38695         JS_ThrowTypeError(ctx, "not a object");
  38696         return NULL;
  38697     }
  38698     if (js_get_length32(ctx, &len, array_arg))
  38699         return NULL;
  38700     if (len > JS_MAX_LOCAL_VARS) {
  38701         // XXX: check for stack overflow?
  38702         JS_ThrowRangeError(ctx, "too many arguments in function call (only %d allowed)",
  38703                            JS_MAX_LOCAL_VARS);
  38704         return NULL;
  38705     }
  38706     /* avoid allocating 0 bytes */
  38707     tab = js_mallocz(ctx, sizeof(tab[0]) * max_uint32(1, len));
  38708     if (!tab)
  38709         return NULL;
  38710     p = JS_VALUE_GET_OBJ(array_arg);
  38711     if ((p->class_id == JS_CLASS_ARRAY || p->class_id == JS_CLASS_ARGUMENTS) &&
  38712         p->fast_array &&
  38713         len == p->u.array.count) {
  38714         for(i = 0; i < len; i++) {
  38715             tab[i] = JS_DupValue(ctx, p->u.array.u.values[i]);
  38716         }
  38717     } else {
  38718         for(i = 0; i < len; i++) {
  38719             ret = JS_GetPropertyUint32(ctx, array_arg, i);
  38720             if (JS_IsException(ret)) {
  38721                 free_arg_list(ctx, tab, i);
  38722                 return NULL;
  38723             }
  38724             tab[i] = ret;
  38725         }
  38726     }
  38727     *plen = len;
  38728     return tab;
  38729 }
  38730 
  38731 /* magic value: 0 = normal apply, 1 = apply for constructor, 2 =
  38732    Reflect.apply */
  38733 static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val,
  38734                                  int argc, JSValueConst *argv, int magic)
  38735 {
  38736     JSValueConst this_arg, array_arg;
  38737     uint32_t len;
  38738     JSValue *tab, ret;
  38739 
  38740     if (check_function(ctx, this_val))
  38741         return JS_EXCEPTION;
  38742     this_arg = argv[0];
  38743     array_arg = argv[1];
  38744     if ((JS_VALUE_GET_TAG(array_arg) == JS_TAG_UNDEFINED ||
  38745          JS_VALUE_GET_TAG(array_arg) == JS_TAG_NULL) && magic != 2) {
  38746         return JS_Call(ctx, this_val, this_arg, 0, NULL);
  38747     }
  38748     tab = build_arg_list(ctx, &len, array_arg);
  38749     if (!tab)
  38750         return JS_EXCEPTION;
  38751     if (magic & 1) {
  38752         ret = JS_CallConstructor2(ctx, this_val, this_arg, len, (JSValueConst *)tab);
  38753     } else {
  38754         ret = JS_Call(ctx, this_val, this_arg, len, (JSValueConst *)tab);
  38755     }
  38756     free_arg_list(ctx, tab, len);
  38757     return ret;
  38758 }
  38759 
  38760 static JSValue js_function_call(JSContext *ctx, JSValueConst this_val,
  38761                                 int argc, JSValueConst *argv)
  38762 {
  38763     if (argc <= 0) {
  38764         return JS_Call(ctx, this_val, JS_UNDEFINED, 0, NULL);
  38765     } else {
  38766         return JS_Call(ctx, this_val, argv[0], argc - 1, argv + 1);
  38767     }
  38768 }
  38769 
  38770 static JSValue js_function_bind(JSContext *ctx, JSValueConst this_val,
  38771                                 int argc, JSValueConst *argv)
  38772 {
  38773     JSBoundFunction *bf;
  38774     JSValue func_obj, name1, len_val;
  38775     JSObject *p;
  38776     int arg_count, i, ret;
  38777 
  38778     if (check_function(ctx, this_val))
  38779         return JS_EXCEPTION;
  38780 
  38781     func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
  38782                                  JS_CLASS_BOUND_FUNCTION);
  38783     if (JS_IsException(func_obj))
  38784         return JS_EXCEPTION;
  38785     p = JS_VALUE_GET_OBJ(func_obj);
  38786     p->is_constructor = JS_IsConstructor(ctx, this_val);
  38787     arg_count = max_int(0, argc - 1);
  38788     bf = js_malloc(ctx, sizeof(*bf) + arg_count * sizeof(JSValue));
  38789     if (!bf)
  38790         goto exception;
  38791     bf->func_obj = JS_DupValue(ctx, this_val);
  38792     bf->this_val = JS_DupValue(ctx, argv[0]);
  38793     bf->argc = arg_count;
  38794     for(i = 0; i < arg_count; i++) {
  38795         bf->argv[i] = JS_DupValue(ctx, argv[i + 1]);
  38796     }
  38797     p->u.bound_function = bf;
  38798 
  38799     /* XXX: the spec could be simpler by only using GetOwnProperty */
  38800     ret = JS_GetOwnProperty(ctx, NULL, this_val, JS_ATOM_length);
  38801     if (ret < 0)
  38802         goto exception;
  38803     if (!ret) {
  38804         len_val = JS_NewInt32(ctx, 0);
  38805     } else {
  38806         len_val = JS_GetProperty(ctx, this_val, JS_ATOM_length);
  38807         if (JS_IsException(len_val))
  38808             goto exception;
  38809         if (JS_VALUE_GET_TAG(len_val) == JS_TAG_INT) {
  38810             /* most common case */
  38811             int len1 = JS_VALUE_GET_INT(len_val);
  38812             if (len1 <= arg_count)
  38813                 len1 = 0;
  38814             else
  38815                 len1 -= arg_count;
  38816             len_val = JS_NewInt32(ctx, len1);
  38817         } else if (JS_VALUE_GET_NORM_TAG(len_val) == JS_TAG_FLOAT64) {
  38818             double d = JS_VALUE_GET_FLOAT64(len_val);
  38819             if (isnan(d)) {
  38820                 d = 0.0;
  38821             } else {
  38822                 d = trunc(d);
  38823                 if (d <= (double)arg_count)
  38824                     d = 0.0;
  38825                 else
  38826                     d -= (double)arg_count; /* also converts -0 to +0 */
  38827             }
  38828             len_val = JS_NewFloat64(ctx, d);
  38829         } else {
  38830             JS_FreeValue(ctx, len_val);
  38831             len_val = JS_NewInt32(ctx, 0);
  38832         }
  38833     }
  38834     JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length,
  38835                            len_val, JS_PROP_CONFIGURABLE);
  38836 
  38837     name1 = JS_GetProperty(ctx, this_val, JS_ATOM_name);
  38838     if (JS_IsException(name1))
  38839         goto exception;
  38840     if (!JS_IsString(name1)) {
  38841         JS_FreeValue(ctx, name1);
  38842         name1 = JS_AtomToString(ctx, JS_ATOM_empty_string);
  38843     }
  38844     name1 = JS_ConcatString3(ctx, "bound ", name1, "");
  38845     if (JS_IsException(name1))
  38846         goto exception;
  38847     JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name1,
  38848                            JS_PROP_CONFIGURABLE);
  38849     return func_obj;
  38850  exception:
  38851     JS_FreeValue(ctx, func_obj);
  38852     return JS_EXCEPTION;
  38853 }
  38854 
  38855 static JSValue js_function_toString(JSContext *ctx, JSValueConst this_val,
  38856                                     int argc, JSValueConst *argv)
  38857 {
  38858     JSObject *p;
  38859     JSFunctionKindEnum func_kind = JS_FUNC_NORMAL;
  38860 
  38861     if (check_function(ctx, this_val))
  38862         return JS_EXCEPTION;
  38863 
  38864     p = JS_VALUE_GET_OBJ(this_val);
  38865     if (js_class_has_bytecode(p->class_id)) {
  38866         JSFunctionBytecode *b = p->u.func.function_bytecode;
  38867         if (b->has_debug && b->debug.source) {
  38868             return JS_NewStringLen(ctx, b->debug.source, b->debug.source_len);
  38869         }
  38870         func_kind = b->func_kind;
  38871     }
  38872     {
  38873         JSValue name;
  38874         const char *pref, *suff;
  38875 
  38876         switch(func_kind) {
  38877         default:
  38878         case JS_FUNC_NORMAL:
  38879             pref = "function ";
  38880             break;
  38881         case JS_FUNC_GENERATOR:
  38882             pref = "function *";
  38883             break;
  38884         case JS_FUNC_ASYNC:
  38885             pref = "async function ";
  38886             break;
  38887         case JS_FUNC_ASYNC_GENERATOR:
  38888             pref = "async function *";
  38889             break;
  38890         }
  38891         suff = "() {\n    [native code]\n}";
  38892         name = JS_GetProperty(ctx, this_val, JS_ATOM_name);
  38893         if (JS_IsUndefined(name))
  38894             name = JS_AtomToString(ctx, JS_ATOM_empty_string);
  38895         return JS_ConcatString3(ctx, pref, name, suff);
  38896     }
  38897 }
  38898 
  38899 static JSValue js_function_hasInstance(JSContext *ctx, JSValueConst this_val,
  38900                                        int argc, JSValueConst *argv)
  38901 {
  38902     int ret;
  38903     ret = JS_OrdinaryIsInstanceOf(ctx, argv[0], this_val);
  38904     if (ret < 0)
  38905         return JS_EXCEPTION;
  38906     else
  38907         return JS_NewBool(ctx, ret);
  38908 }
  38909 
  38910 static const JSCFunctionListEntry js_function_proto_funcs[] = {
  38911     JS_CFUNC_DEF("call", 1, js_function_call ),
  38912     JS_CFUNC_MAGIC_DEF("apply", 2, js_function_apply, 0 ),
  38913     JS_CFUNC_DEF("bind", 1, js_function_bind ),
  38914     JS_CFUNC_DEF("toString", 0, js_function_toString ),
  38915     JS_CFUNC_DEF("[Symbol.hasInstance]", 1, js_function_hasInstance ),
  38916     JS_CGETSET_DEF("fileName", js_function_proto_fileName, NULL ),
  38917     JS_CGETSET_DEF("lineNumber", js_function_proto_lineNumber, NULL ),
  38918 };
  38919 
  38920 /* Error class */
  38921 
  38922 static JSValue iterator_to_array(JSContext *ctx, JSValueConst items)
  38923 {
  38924     JSValue iter, next_method = JS_UNDEFINED;
  38925     JSValue v, r = JS_UNDEFINED;
  38926     int64_t k;
  38927     BOOL done;
  38928 
  38929     iter = JS_GetIterator(ctx, items, FALSE);
  38930     if (JS_IsException(iter))
  38931         goto exception;
  38932     next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
  38933     if (JS_IsException(next_method))
  38934         goto exception;
  38935     r = JS_NewArray(ctx);
  38936     if (JS_IsException(r))
  38937         goto exception;
  38938     for (k = 0;; k++) {
  38939         v = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
  38940         if (JS_IsException(v))
  38941             goto exception_close;
  38942         if (done)
  38943             break;
  38944         if (JS_DefinePropertyValueInt64(ctx, r, k, v,
  38945                                         JS_PROP_C_W_E | JS_PROP_THROW) < 0)
  38946             goto exception_close;
  38947     }
  38948  done:
  38949     JS_FreeValue(ctx, next_method);
  38950     JS_FreeValue(ctx, iter);
  38951     return r;
  38952  exception_close:
  38953     JS_IteratorClose(ctx, iter, TRUE);
  38954  exception:
  38955     JS_FreeValue(ctx, r);
  38956     r = JS_EXCEPTION;
  38957     goto done;
  38958 }
  38959 
  38960 static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target,
  38961                                     int argc, JSValueConst *argv, int magic)
  38962 {
  38963     JSValue obj, msg, proto;
  38964     JSValueConst message, options;
  38965     int arg_index;
  38966 
  38967     if (JS_IsUndefined(new_target))
  38968         new_target = JS_GetActiveFunction(ctx);
  38969     proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype);
  38970     if (JS_IsException(proto))
  38971         return proto;
  38972     if (!JS_IsObject(proto)) {
  38973         JSContext *realm;
  38974         JSValueConst proto1;
  38975 
  38976         JS_FreeValue(ctx, proto);
  38977         realm = JS_GetFunctionRealm(ctx, new_target);
  38978         if (!realm)
  38979             return JS_EXCEPTION;
  38980         if (magic < 0) {
  38981             proto1 = realm->class_proto[JS_CLASS_ERROR];
  38982         } else {
  38983             proto1 = realm->native_error_proto[magic];
  38984         }
  38985         proto = JS_DupValue(ctx, proto1);
  38986     }
  38987     obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_ERROR);
  38988     JS_FreeValue(ctx, proto);
  38989     if (JS_IsException(obj))
  38990         return obj;
  38991     arg_index = (magic == JS_AGGREGATE_ERROR);
  38992 
  38993     message = argv[arg_index++];
  38994     if (!JS_IsUndefined(message)) {
  38995         msg = JS_ToString(ctx, message);
  38996         if (unlikely(JS_IsException(msg)))
  38997             goto exception;
  38998         JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, msg,
  38999                                JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
  39000     }
  39001 
  39002     if (arg_index < argc) {
  39003         options = argv[arg_index];
  39004         if (JS_IsObject(options)) {
  39005             int present = JS_HasProperty(ctx, options, JS_ATOM_cause);
  39006             if (present < 0)
  39007                 goto exception;
  39008             if (present) {
  39009                 JSValue cause = JS_GetProperty(ctx, options, JS_ATOM_cause);
  39010                 if (JS_IsException(cause))
  39011                     goto exception;
  39012                 JS_DefinePropertyValue(ctx, obj, JS_ATOM_cause, cause,
  39013                                        JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
  39014             }
  39015         }
  39016     }
  39017 
  39018     if (magic == JS_AGGREGATE_ERROR) {
  39019         JSValue error_list = iterator_to_array(ctx, argv[0]);
  39020         if (JS_IsException(error_list))
  39021             goto exception;
  39022         JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, error_list,
  39023                                JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
  39024     }
  39025 
  39026     /* skip the Error() function in the backtrace */
  39027     build_backtrace(ctx, obj, NULL, 0, JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL);
  39028     return obj;
  39029  exception:
  39030     JS_FreeValue(ctx, obj);
  39031     return JS_EXCEPTION;
  39032 }
  39033 
  39034 static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val,
  39035                                  int argc, JSValueConst *argv)
  39036 {
  39037     JSValue name, msg;
  39038 
  39039     if (!JS_IsObject(this_val))
  39040         return JS_ThrowTypeErrorNotAnObject(ctx);
  39041     name = JS_GetProperty(ctx, this_val, JS_ATOM_name);
  39042     if (JS_IsUndefined(name))
  39043         name = JS_AtomToString(ctx, JS_ATOM_Error);
  39044     else
  39045         name = JS_ToStringFree(ctx, name);
  39046     if (JS_IsException(name))
  39047         return JS_EXCEPTION;
  39048 
  39049     msg = JS_GetProperty(ctx, this_val, JS_ATOM_message);
  39050     if (JS_IsUndefined(msg))
  39051         msg = JS_AtomToString(ctx, JS_ATOM_empty_string);
  39052     else
  39053         msg = JS_ToStringFree(ctx, msg);
  39054     if (JS_IsException(msg)) {
  39055         JS_FreeValue(ctx, name);
  39056         return JS_EXCEPTION;
  39057     }
  39058     if (!JS_IsEmptyString(name) && !JS_IsEmptyString(msg))
  39059         name = JS_ConcatString3(ctx, "", name, ": ");
  39060     return JS_ConcatString(ctx, name, msg);
  39061 }
  39062 
  39063 static const JSCFunctionListEntry js_error_proto_funcs[] = {
  39064     JS_CFUNC_DEF("toString", 0, js_error_toString ),
  39065     JS_PROP_STRING_DEF("name", "Error", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
  39066     JS_PROP_STRING_DEF("message", "", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
  39067 };
  39068 
  39069 /* AggregateError */
  39070 
  39071 /* used by C code. */
  39072 static JSValue js_aggregate_error_constructor(JSContext *ctx,
  39073                                               JSValueConst errors)
  39074 {
  39075     JSValue obj;
  39076 
  39077     obj = JS_NewObjectProtoClass(ctx,
  39078                                  ctx->native_error_proto[JS_AGGREGATE_ERROR],
  39079                                  JS_CLASS_ERROR);
  39080     if (JS_IsException(obj))
  39081         return obj;
  39082     JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, JS_DupValue(ctx, errors),
  39083                            JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
  39084     return obj;
  39085 }
  39086 
  39087 /* Array */
  39088 
  39089 static int JS_CopySubArray(JSContext *ctx,
  39090                            JSValueConst obj, int64_t to_pos,
  39091                            int64_t from_pos, int64_t count, int dir)
  39092 {
  39093     JSObject *p;
  39094     int64_t i, from, to, len;
  39095     JSValue val;
  39096     int fromPresent;
  39097 
  39098     p = NULL;
  39099     if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
  39100         p = JS_VALUE_GET_OBJ(obj);
  39101         if (p->class_id != JS_CLASS_ARRAY || !p->fast_array) {
  39102             p = NULL;
  39103         }
  39104     }
  39105 
  39106     for (i = 0; i < count; ) {
  39107         if (dir < 0) {
  39108             from = from_pos + count - i - 1;
  39109             to = to_pos + count - i - 1;
  39110         } else {
  39111             from = from_pos + i;
  39112             to = to_pos + i;
  39113         }
  39114         if (p && p->fast_array &&
  39115             from >= 0 && from < (len = p->u.array.count)  &&
  39116             to >= 0 && to < len) {
  39117             int64_t l, j;
  39118             /* Fast path for fast arrays. Since we don't look at the
  39119                prototype chain, we can optimize only the cases where
  39120                all the elements are present in the array. */
  39121             l = count - i;
  39122             if (dir < 0) {
  39123                 l = min_int64(l, from + 1);
  39124                 l = min_int64(l, to + 1);
  39125                 for(j = 0; j < l; j++) {
  39126                     set_value(ctx, &p->u.array.u.values[to - j],
  39127                               JS_DupValue(ctx, p->u.array.u.values[from - j]));
  39128                 }
  39129             } else {
  39130                 l = min_int64(l, len - from);
  39131                 l = min_int64(l, len - to);
  39132                 for(j = 0; j < l; j++) {
  39133                     set_value(ctx, &p->u.array.u.values[to + j],
  39134                               JS_DupValue(ctx, p->u.array.u.values[from + j]));
  39135                 }
  39136             }
  39137             i += l;
  39138         } else {
  39139             fromPresent = JS_TryGetPropertyInt64(ctx, obj, from, &val);
  39140             if (fromPresent < 0)
  39141                 goto exception;
  39142 
  39143             if (fromPresent) {
  39144                 if (JS_SetPropertyInt64(ctx, obj, to, val) < 0)
  39145                     goto exception;
  39146             } else {
  39147                 if (JS_DeletePropertyInt64(ctx, obj, to, JS_PROP_THROW) < 0)
  39148                     goto exception;
  39149             }
  39150             i++;
  39151         }
  39152     }
  39153     return 0;
  39154 
  39155  exception:
  39156     return -1;
  39157 }
  39158 
  39159 static JSValue js_array_constructor(JSContext *ctx, JSValueConst new_target,
  39160                                     int argc, JSValueConst *argv)
  39161 {
  39162     JSValue obj;
  39163     int i;
  39164 
  39165     obj = js_create_from_ctor(ctx, new_target, JS_CLASS_ARRAY);
  39166     if (JS_IsException(obj))
  39167         return obj;
  39168     if (argc == 1 && JS_IsNumber(argv[0])) {
  39169         uint32_t len;
  39170         if (JS_ToArrayLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]), TRUE))
  39171             goto fail;
  39172         if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, len)) < 0)
  39173             goto fail;
  39174     } else {
  39175         for(i = 0; i < argc; i++) {
  39176             if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0)
  39177                 goto fail;
  39178         }
  39179     }
  39180     return obj;
  39181 fail:
  39182     JS_FreeValue(ctx, obj);
  39183     return JS_EXCEPTION;
  39184 }
  39185 
  39186 static JSValue js_array_from(JSContext *ctx, JSValueConst this_val,
  39187                              int argc, JSValueConst *argv)
  39188 {
  39189     // from(items, mapfn = void 0, this_arg = void 0)
  39190     JSValueConst items = argv[0], mapfn, this_arg;
  39191     JSValueConst args[2];
  39192     JSValue stack[2];
  39193     JSValue iter, r, v, v2, arrayLike;
  39194     int64_t k, len;
  39195     int done, mapping;
  39196 
  39197     mapping = FALSE;
  39198     mapfn = JS_UNDEFINED;
  39199     this_arg = JS_UNDEFINED;
  39200     r = JS_UNDEFINED;
  39201     arrayLike = JS_UNDEFINED;
  39202     stack[0] = JS_UNDEFINED;
  39203     stack[1] = JS_UNDEFINED;
  39204 
  39205     if (argc > 1) {
  39206         mapfn = argv[1];
  39207         if (!JS_IsUndefined(mapfn)) {
  39208             if (check_function(ctx, mapfn))
  39209                 goto exception;
  39210             mapping = 1;
  39211             if (argc > 2)
  39212                 this_arg = argv[2];
  39213         }
  39214     }
  39215     iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator);
  39216     if (JS_IsException(iter))
  39217         goto exception;
  39218     if (!JS_IsUndefined(iter)) {
  39219         JS_FreeValue(ctx, iter);
  39220         if (JS_IsConstructor(ctx, this_val))
  39221             r = JS_CallConstructor(ctx, this_val, 0, NULL);
  39222         else
  39223             r = JS_NewArray(ctx);
  39224         if (JS_IsException(r))
  39225             goto exception;
  39226         stack[0] = JS_DupValue(ctx, items);
  39227         if (js_for_of_start(ctx, &stack[1], FALSE))
  39228             goto exception;
  39229         for (k = 0;; k++) {
  39230             v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done);
  39231             if (JS_IsException(v))
  39232                 goto exception_close;
  39233             if (done)
  39234                 break;
  39235             if (mapping) {
  39236                 args[0] = v;
  39237                 args[1] = JS_NewInt32(ctx, k);
  39238                 v2 = JS_Call(ctx, mapfn, this_arg, 2, args);
  39239                 JS_FreeValue(ctx, v);
  39240                 v = v2;
  39241                 if (JS_IsException(v))
  39242                     goto exception_close;
  39243             }
  39244             if (JS_DefinePropertyValueInt64(ctx, r, k, v,
  39245                                             JS_PROP_C_W_E | JS_PROP_THROW) < 0)
  39246                 goto exception_close;
  39247         }
  39248     } else {
  39249         arrayLike = JS_ToObject(ctx, items);
  39250         if (JS_IsException(arrayLike))
  39251             goto exception;
  39252         if (js_get_length64(ctx, &len, arrayLike) < 0)
  39253             goto exception;
  39254         v = JS_NewInt64(ctx, len);
  39255         args[0] = v;
  39256         if (JS_IsConstructor(ctx, this_val)) {
  39257             r = JS_CallConstructor(ctx, this_val, 1, args);
  39258         } else {
  39259             r = js_array_constructor(ctx, JS_UNDEFINED, 1, args);
  39260         }
  39261         JS_FreeValue(ctx, v);
  39262         if (JS_IsException(r))
  39263             goto exception;
  39264         for(k = 0; k < len; k++) {
  39265             v = JS_GetPropertyInt64(ctx, arrayLike, k);
  39266             if (JS_IsException(v))
  39267                 goto exception;
  39268             if (mapping) {
  39269                 args[0] = v;
  39270                 args[1] = JS_NewInt32(ctx, k);
  39271                 v2 = JS_Call(ctx, mapfn, this_arg, 2, args);
  39272                 JS_FreeValue(ctx, v);
  39273                 v = v2;
  39274                 if (JS_IsException(v))
  39275                     goto exception;
  39276             }
  39277             if (JS_DefinePropertyValueInt64(ctx, r, k, v,
  39278                                             JS_PROP_C_W_E | JS_PROP_THROW) < 0)
  39279                 goto exception;
  39280         }
  39281     }
  39282     if (JS_SetProperty(ctx, r, JS_ATOM_length, JS_NewUint32(ctx, k)) < 0)
  39283         goto exception;
  39284     goto done;
  39285 
  39286  exception_close:
  39287     if (!JS_IsUndefined(stack[0]))
  39288         JS_IteratorClose(ctx, stack[0], TRUE);
  39289  exception:
  39290     JS_FreeValue(ctx, r);
  39291     r = JS_EXCEPTION;
  39292  done:
  39293     JS_FreeValue(ctx, arrayLike);
  39294     JS_FreeValue(ctx, stack[0]);
  39295     JS_FreeValue(ctx, stack[1]);
  39296     return r;
  39297 }
  39298 
  39299 static JSValue js_array_of(JSContext *ctx, JSValueConst this_val,
  39300                            int argc, JSValueConst *argv)
  39301 {
  39302     JSValue obj, args[1];
  39303     int i;
  39304 
  39305     if (JS_IsConstructor(ctx, this_val)) {
  39306         args[0] = JS_NewInt32(ctx, argc);
  39307         obj = JS_CallConstructor(ctx, this_val, 1, (JSValueConst *)args);
  39308     } else {
  39309         obj = JS_NewArray(ctx);
  39310     }
  39311     if (JS_IsException(obj))
  39312         return JS_EXCEPTION;
  39313     for(i = 0; i < argc; i++) {
  39314         if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i]),
  39315                                         JS_PROP_THROW) < 0) {
  39316             goto fail;
  39317         }
  39318     }
  39319     if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, argc)) < 0) {
  39320     fail:
  39321         JS_FreeValue(ctx, obj);
  39322         return JS_EXCEPTION;
  39323     }
  39324     return obj;
  39325 }
  39326 
  39327 static JSValue js_array_isArray(JSContext *ctx, JSValueConst this_val,
  39328                                 int argc, JSValueConst *argv)
  39329 {
  39330     int ret;
  39331     ret = JS_IsArray(ctx, argv[0]);
  39332     if (ret < 0)
  39333         return JS_EXCEPTION;
  39334     else
  39335         return JS_NewBool(ctx, ret);
  39336 }
  39337 
  39338 static JSValue js_get_this(JSContext *ctx,
  39339                            JSValueConst this_val)
  39340 {
  39341     return JS_DupValue(ctx, this_val);
  39342 }
  39343 
  39344 static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValueConst obj,
  39345                                      JSValueConst len_val)
  39346 {
  39347     JSValue ctor, ret, species;
  39348     int res;
  39349     JSContext *realm;
  39350 
  39351     res = JS_IsArray(ctx, obj);
  39352     if (res < 0)
  39353         return JS_EXCEPTION;
  39354     if (!res)
  39355         return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val);
  39356     ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor);
  39357     if (JS_IsException(ctor))
  39358         return ctor;
  39359     if (JS_IsConstructor(ctx, ctor)) {
  39360         /* legacy web compatibility */
  39361         realm = JS_GetFunctionRealm(ctx, ctor);
  39362         if (!realm) {
  39363             JS_FreeValue(ctx, ctor);
  39364             return JS_EXCEPTION;
  39365         }
  39366         if (realm != ctx &&
  39367             js_same_value(ctx, ctor, realm->array_ctor)) {
  39368             JS_FreeValue(ctx, ctor);
  39369             ctor = JS_UNDEFINED;
  39370         }
  39371     }
  39372     if (JS_IsObject(ctor)) {
  39373         species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species);
  39374         JS_FreeValue(ctx, ctor);
  39375         if (JS_IsException(species))
  39376             return species;
  39377         ctor = species;
  39378         if (JS_IsNull(ctor))
  39379             ctor = JS_UNDEFINED;
  39380     }
  39381     if (JS_IsUndefined(ctor)) {
  39382         return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val);
  39383     } else {
  39384         ret = JS_CallConstructor(ctx, ctor, 1, &len_val);
  39385         JS_FreeValue(ctx, ctor);
  39386         return ret;
  39387     }
  39388 }
  39389 
  39390 static const JSCFunctionListEntry js_array_funcs[] = {
  39391     JS_CFUNC_DEF("isArray", 1, js_array_isArray ),
  39392     JS_CFUNC_DEF("from", 1, js_array_from ),
  39393     JS_CFUNC_DEF("of", 0, js_array_of ),
  39394     JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
  39395 };
  39396 
  39397 static int JS_isConcatSpreadable(JSContext *ctx, JSValueConst obj)
  39398 {
  39399     JSValue val;
  39400 
  39401     if (!JS_IsObject(obj))
  39402         return FALSE;
  39403     val = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_isConcatSpreadable);
  39404     if (JS_IsException(val))
  39405         return -1;
  39406     if (!JS_IsUndefined(val))
  39407         return JS_ToBoolFree(ctx, val);
  39408     return JS_IsArray(ctx, obj);
  39409 }
  39410 
  39411 static JSValue js_array_at(JSContext *ctx, JSValueConst this_val,
  39412                            int argc, JSValueConst *argv)
  39413 {
  39414     JSValue obj, ret;
  39415     int64_t len, idx;
  39416     JSValue *arrp;
  39417     uint32_t count;
  39418 
  39419     obj = JS_ToObject(ctx, this_val);
  39420     if (js_get_length64(ctx, &len, obj))
  39421         goto exception;
  39422 
  39423     if (JS_ToInt64Sat(ctx, &idx, argv[0]))
  39424         goto exception;
  39425 
  39426     if (idx < 0)
  39427         idx = len + idx;
  39428     if (idx < 0 || idx >= len) {
  39429         ret = JS_UNDEFINED;
  39430     } else if (js_get_fast_array(ctx, obj, &arrp, &count) && idx < count) {
  39431         ret = JS_DupValue(ctx, arrp[idx]);
  39432     } else {
  39433         int present = JS_TryGetPropertyInt64(ctx, obj, idx, &ret);
  39434         if (present < 0)
  39435             goto exception;
  39436         if (!present)
  39437             ret = JS_UNDEFINED;
  39438     }
  39439     JS_FreeValue(ctx, obj);
  39440     return ret;
  39441  exception:
  39442     JS_FreeValue(ctx, obj);
  39443     return JS_EXCEPTION;
  39444 }
  39445 
  39446 static JSValue js_array_with(JSContext *ctx, JSValueConst this_val,
  39447                              int argc, JSValueConst *argv)
  39448 {
  39449     JSValue arr, obj, ret, *arrp, *pval;
  39450     JSObject *p;
  39451     int64_t i, len, idx;
  39452     uint32_t count32;
  39453 
  39454     ret = JS_EXCEPTION;
  39455     arr = JS_UNDEFINED;
  39456     obj = JS_ToObject(ctx, this_val);
  39457     if (js_get_length64(ctx, &len, obj))
  39458         goto exception;
  39459 
  39460     if (JS_ToInt64Sat(ctx, &idx, argv[0]))
  39461         goto exception;
  39462 
  39463     if (idx < 0)
  39464         idx = len + idx;
  39465 
  39466     if (idx < 0 || idx >= len) {
  39467         JS_ThrowRangeError(ctx, "invalid array index: %" PRId64, idx);
  39468         goto exception;
  39469     }
  39470 
  39471     arr = js_allocate_fast_array(ctx, len);
  39472     if (JS_IsException(arr))
  39473         goto exception;
  39474 
  39475     p = JS_VALUE_GET_OBJ(arr);
  39476     i = 0;
  39477     pval = p->u.array.u.values;
  39478     if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
  39479         for (; i < idx; i++, pval++)
  39480             *pval = JS_DupValue(ctx, arrp[i]);
  39481         *pval = JS_DupValue(ctx, argv[1]);
  39482         for (i++, pval++; i < len; i++, pval++)
  39483             *pval = JS_DupValue(ctx, arrp[i]);
  39484     } else {
  39485         for (; i < idx; i++, pval++)
  39486             if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval))
  39487                 goto fill_and_fail;
  39488         *pval = JS_DupValue(ctx, argv[1]);
  39489         for (i++, pval++; i < len; i++, pval++) {
  39490             if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) {
  39491             fill_and_fail:
  39492                 for (; i < len; i++, pval++)
  39493                     *pval = JS_UNDEFINED;
  39494                 goto exception;
  39495             }
  39496         }
  39497     }
  39498 
  39499     if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, len)) < 0)
  39500         goto exception;
  39501 
  39502     ret = arr;
  39503     arr = JS_UNDEFINED;
  39504 
  39505 exception:
  39506     JS_FreeValue(ctx, arr);
  39507     JS_FreeValue(ctx, obj);
  39508     return ret;
  39509 }
  39510 
  39511 static JSValue js_array_concat(JSContext *ctx, JSValueConst this_val,
  39512                                int argc, JSValueConst *argv)
  39513 {
  39514     JSValue obj, arr, val;
  39515     JSValueConst e;
  39516     int64_t len, k, n;
  39517     int i, res;
  39518 
  39519     arr = JS_UNDEFINED;
  39520     obj = JS_ToObject(ctx, this_val);
  39521     if (JS_IsException(obj))
  39522         goto exception;
  39523 
  39524     arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));
  39525     if (JS_IsException(arr))
  39526         goto exception;
  39527     n = 0;
  39528     for (i = -1; i < argc; i++) {
  39529         if (i < 0)
  39530             e = obj;
  39531         else
  39532             e = argv[i];
  39533 
  39534         res = JS_isConcatSpreadable(ctx, e);
  39535         if (res < 0)
  39536             goto exception;
  39537         if (res) {
  39538             if (js_get_length64(ctx, &len, e))
  39539                 goto exception;
  39540             if (n + len > MAX_SAFE_INTEGER) {
  39541                 JS_ThrowTypeError(ctx, "Array loo long");
  39542                 goto exception;
  39543             }
  39544             for (k = 0; k < len; k++, n++) {
  39545                 res = JS_TryGetPropertyInt64(ctx, e, k, &val);
  39546                 if (res < 0)
  39547                     goto exception;
  39548                 if (res) {
  39549                     if (JS_DefinePropertyValueInt64(ctx, arr, n, val,
  39550                                                     JS_PROP_C_W_E | JS_PROP_THROW) < 0)
  39551                         goto exception;
  39552                 }
  39553             }
  39554         } else {
  39555             if (n >= MAX_SAFE_INTEGER) {
  39556                 JS_ThrowTypeError(ctx, "Array loo long");
  39557                 goto exception;
  39558             }
  39559             if (JS_DefinePropertyValueInt64(ctx, arr, n, JS_DupValue(ctx, e),
  39560                                             JS_PROP_C_W_E | JS_PROP_THROW) < 0)
  39561                 goto exception;
  39562             n++;
  39563         }
  39564     }
  39565     if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0)
  39566         goto exception;
  39567 
  39568     JS_FreeValue(ctx, obj);
  39569     return arr;
  39570 
  39571 exception:
  39572     JS_FreeValue(ctx, arr);
  39573     JS_FreeValue(ctx, obj);
  39574     return JS_EXCEPTION;
  39575 }
  39576 
  39577 #define special_every    0
  39578 #define special_some     1
  39579 #define special_forEach  2
  39580 #define special_map      3
  39581 #define special_filter   4
  39582 #define special_TA       8
  39583 
  39584 static int js_typed_array_get_length_internal(JSContext *ctx, JSValueConst obj);
  39585 
  39586 static JSValue js_typed_array___speciesCreate(JSContext *ctx,
  39587                                               JSValueConst this_val,
  39588                                               int argc, JSValueConst *argv);
  39589 
  39590 static JSValue js_array_every(JSContext *ctx, JSValueConst this_val,
  39591                               int argc, JSValueConst *argv, int special)
  39592 {
  39593     JSValue obj, val, index_val, res, ret;
  39594     JSValueConst args[3];
  39595     JSValueConst func, this_arg;
  39596     int64_t len, k, n;
  39597     int present;
  39598 
  39599     ret = JS_UNDEFINED;
  39600     val = JS_UNDEFINED;
  39601     if (special & special_TA) {
  39602         obj = JS_DupValue(ctx, this_val);
  39603         len = js_typed_array_get_length_internal(ctx, obj);
  39604         if (len < 0)
  39605             goto exception;
  39606     } else {
  39607         obj = JS_ToObject(ctx, this_val);
  39608         if (js_get_length64(ctx, &len, obj))
  39609             goto exception;
  39610     }
  39611     func = argv[0];
  39612     this_arg = JS_UNDEFINED;
  39613     if (argc > 1)
  39614         this_arg = argv[1];
  39615 
  39616     if (check_function(ctx, func))
  39617         goto exception;
  39618 
  39619     switch (special) {
  39620     case special_every:
  39621     case special_every | special_TA:
  39622         ret = JS_TRUE;
  39623         break;
  39624     case special_some:
  39625     case special_some | special_TA:
  39626         ret = JS_FALSE;
  39627         break;
  39628     case special_map:
  39629         /* XXX: JS_ArraySpeciesCreate should take int64_t */
  39630         ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt64(ctx, len));
  39631         if (JS_IsException(ret))
  39632             goto exception;
  39633         break;
  39634     case special_filter:
  39635         ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));
  39636         if (JS_IsException(ret))
  39637             goto exception;
  39638         break;
  39639     case special_map | special_TA:
  39640         args[0] = obj;
  39641         args[1] = JS_NewInt32(ctx, len);
  39642         ret = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);
  39643         if (JS_IsException(ret))
  39644             goto exception;
  39645         break;
  39646     case special_filter | special_TA:
  39647         ret = JS_NewArray(ctx);
  39648         if (JS_IsException(ret))
  39649             goto exception;
  39650         break;
  39651     }
  39652     n = 0;
  39653 
  39654     for(k = 0; k < len; k++) {
  39655         if (special & special_TA) {
  39656             val = JS_GetPropertyInt64(ctx, obj, k);
  39657             if (JS_IsException(val))
  39658                 goto exception;
  39659             present = TRUE;
  39660         } else {
  39661             present = JS_TryGetPropertyInt64(ctx, obj, k, &val);
  39662             if (present < 0)
  39663                 goto exception;
  39664         }
  39665         if (present) {
  39666             index_val = JS_NewInt64(ctx, k);
  39667             if (JS_IsException(index_val))
  39668                 goto exception;
  39669             args[0] = val;
  39670             args[1] = index_val;
  39671             args[2] = obj;
  39672             res = JS_Call(ctx, func, this_arg, 3, args);
  39673             JS_FreeValue(ctx, index_val);
  39674             if (JS_IsException(res))
  39675                 goto exception;
  39676             switch (special) {
  39677             case special_every:
  39678             case special_every | special_TA:
  39679                 if (!JS_ToBoolFree(ctx, res)) {
  39680                     ret = JS_FALSE;
  39681                     goto done;
  39682                 }
  39683                 break;
  39684             case special_some:
  39685             case special_some | special_TA:
  39686                 if (JS_ToBoolFree(ctx, res)) {
  39687                     ret = JS_TRUE;
  39688                     goto done;
  39689                 }
  39690                 break;
  39691             case special_map:
  39692                 if (JS_DefinePropertyValueInt64(ctx, ret, k, res,
  39693                                                 JS_PROP_C_W_E | JS_PROP_THROW) < 0)
  39694                     goto exception;
  39695                 break;
  39696             case special_map | special_TA:
  39697                 if (JS_SetPropertyValue(ctx, ret, JS_NewInt32(ctx, k), res, JS_PROP_THROW) < 0)
  39698                     goto exception;
  39699                 break;
  39700             case special_filter:
  39701             case special_filter | special_TA:
  39702                 if (JS_ToBoolFree(ctx, res)) {
  39703                     if (JS_DefinePropertyValueInt64(ctx, ret, n++, JS_DupValue(ctx, val),
  39704                                                     JS_PROP_C_W_E | JS_PROP_THROW) < 0)
  39705                         goto exception;
  39706                 }
  39707                 break;
  39708             default:
  39709                 JS_FreeValue(ctx, res);
  39710                 break;
  39711             }
  39712             JS_FreeValue(ctx, val);
  39713             val = JS_UNDEFINED;
  39714         }
  39715     }
  39716 done:
  39717     if (special == (special_filter | special_TA)) {
  39718         JSValue arr;
  39719         args[0] = obj;
  39720         args[1] = JS_NewInt32(ctx, n);
  39721         arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);
  39722         if (JS_IsException(arr))
  39723             goto exception;
  39724         args[0] = ret;
  39725         res = JS_Invoke(ctx, arr, JS_ATOM_set, 1, args);
  39726         if (check_exception_free(ctx, res))
  39727             goto exception;
  39728         JS_FreeValue(ctx, ret);
  39729         ret = arr;
  39730     }
  39731     JS_FreeValue(ctx, val);
  39732     JS_FreeValue(ctx, obj);
  39733     return ret;
  39734 
  39735 exception:
  39736     JS_FreeValue(ctx, ret);
  39737     JS_FreeValue(ctx, val);
  39738     JS_FreeValue(ctx, obj);
  39739     return JS_EXCEPTION;
  39740 }
  39741 
  39742 #define special_reduce       0
  39743 #define special_reduceRight  1
  39744 
  39745 static JSValue js_array_reduce(JSContext *ctx, JSValueConst this_val,
  39746                                int argc, JSValueConst *argv, int special)
  39747 {
  39748     JSValue obj, val, index_val, acc, acc1;
  39749     JSValueConst args[4];
  39750     JSValueConst func;
  39751     int64_t len, k, k1;
  39752     int present;
  39753 
  39754     acc = JS_UNDEFINED;
  39755     val = JS_UNDEFINED;
  39756     if (special & special_TA) {
  39757         obj = JS_DupValue(ctx, this_val);
  39758         len = js_typed_array_get_length_internal(ctx, obj);
  39759         if (len < 0)
  39760             goto exception;
  39761     } else {
  39762         obj = JS_ToObject(ctx, this_val);
  39763         if (js_get_length64(ctx, &len, obj))
  39764             goto exception;
  39765     }
  39766     func = argv[0];
  39767 
  39768     if (check_function(ctx, func))
  39769         goto exception;
  39770 
  39771     k = 0;
  39772     if (argc > 1) {
  39773         acc = JS_DupValue(ctx, argv[1]);
  39774     } else {
  39775         for(;;) {
  39776             if (k >= len) {
  39777                 JS_ThrowTypeError(ctx, "empty array");
  39778                 goto exception;
  39779             }
  39780             k1 = (special & special_reduceRight) ? len - k - 1 : k;
  39781             k++;
  39782             if (special & special_TA) {
  39783                 acc = JS_GetPropertyInt64(ctx, obj, k1);
  39784                 if (JS_IsException(acc))
  39785                     goto exception;
  39786                 break;
  39787             } else {
  39788                 present = JS_TryGetPropertyInt64(ctx, obj, k1, &acc);
  39789                 if (present < 0)
  39790                     goto exception;
  39791                 if (present)
  39792                     break;
  39793             }
  39794         }
  39795     }
  39796     for (; k < len; k++) {
  39797         k1 = (special & special_reduceRight) ? len - k - 1 : k;
  39798         if (special & special_TA) {
  39799             val = JS_GetPropertyInt64(ctx, obj, k1);
  39800             if (JS_IsException(val))
  39801                 goto exception;
  39802             present = TRUE;
  39803         } else {
  39804             present = JS_TryGetPropertyInt64(ctx, obj, k1, &val);
  39805             if (present < 0)
  39806                 goto exception;
  39807         }
  39808         if (present) {
  39809             index_val = JS_NewInt64(ctx, k1);
  39810             if (JS_IsException(index_val))
  39811                 goto exception;
  39812             args[0] = acc;
  39813             args[1] = val;
  39814             args[2] = index_val;
  39815             args[3] = obj;
  39816             acc1 = JS_Call(ctx, func, JS_UNDEFINED, 4, args);
  39817             JS_FreeValue(ctx, index_val);
  39818             JS_FreeValue(ctx, val);
  39819             val = JS_UNDEFINED;
  39820             if (JS_IsException(acc1))
  39821                 goto exception;
  39822             JS_FreeValue(ctx, acc);
  39823             acc = acc1;
  39824         }
  39825     }
  39826     JS_FreeValue(ctx, obj);
  39827     return acc;
  39828 
  39829 exception:
  39830     JS_FreeValue(ctx, acc);
  39831     JS_FreeValue(ctx, val);
  39832     JS_FreeValue(ctx, obj);
  39833     return JS_EXCEPTION;
  39834 }
  39835 
  39836 static JSValue js_array_fill(JSContext *ctx, JSValueConst this_val,
  39837                              int argc, JSValueConst *argv)
  39838 {
  39839     JSValue obj;
  39840     int64_t len, start, end;
  39841 
  39842     obj = JS_ToObject(ctx, this_val);
  39843     if (js_get_length64(ctx, &len, obj))
  39844         goto exception;
  39845 
  39846     start = 0;
  39847     if (argc > 1 && !JS_IsUndefined(argv[1])) {
  39848         if (JS_ToInt64Clamp(ctx, &start, argv[1], 0, len, len))
  39849             goto exception;
  39850     }
  39851 
  39852     end = len;
  39853     if (argc > 2 && !JS_IsUndefined(argv[2])) {
  39854         if (JS_ToInt64Clamp(ctx, &end, argv[2], 0, len, len))
  39855             goto exception;
  39856     }
  39857 
  39858     /* XXX: should special case fast arrays */
  39859     while (start < end) {
  39860         if (JS_SetPropertyInt64(ctx, obj, start,
  39861                                 JS_DupValue(ctx, argv[0])) < 0)
  39862             goto exception;
  39863         start++;
  39864     }
  39865     return obj;
  39866 
  39867  exception:
  39868     JS_FreeValue(ctx, obj);
  39869     return JS_EXCEPTION;
  39870 }
  39871 
  39872 static JSValue js_array_includes(JSContext *ctx, JSValueConst this_val,
  39873                                  int argc, JSValueConst *argv)
  39874 {
  39875     JSValue obj, val;
  39876     int64_t len, n;
  39877     JSValue *arrp;
  39878     uint32_t count;
  39879     int res;
  39880 
  39881     obj = JS_ToObject(ctx, this_val);
  39882     if (js_get_length64(ctx, &len, obj))
  39883         goto exception;
  39884 
  39885     res = FALSE;
  39886     if (len > 0) {
  39887         n = 0;
  39888         if (argc > 1) {
  39889             if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len))
  39890                 goto exception;
  39891         }
  39892         if (js_get_fast_array(ctx, obj, &arrp, &count)) {
  39893             for (; n < count; n++) {
  39894                 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]),
  39895                                   JS_DupValue(ctx, arrp[n]),
  39896                                   JS_EQ_SAME_VALUE_ZERO)) {
  39897                     res = TRUE;
  39898                     goto done;
  39899                 }
  39900             }
  39901         }
  39902         for (; n < len; n++) {
  39903             val = JS_GetPropertyInt64(ctx, obj, n);
  39904             if (JS_IsException(val))
  39905                 goto exception;
  39906             if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val,
  39907                               JS_EQ_SAME_VALUE_ZERO)) {
  39908                 res = TRUE;
  39909                 break;
  39910             }
  39911         }
  39912     }
  39913  done:
  39914     JS_FreeValue(ctx, obj);
  39915     return JS_NewBool(ctx, res);
  39916 
  39917  exception:
  39918     JS_FreeValue(ctx, obj);
  39919     return JS_EXCEPTION;
  39920 }
  39921 
  39922 static JSValue js_array_indexOf(JSContext *ctx, JSValueConst this_val,
  39923                                 int argc, JSValueConst *argv)
  39924 {
  39925     JSValue obj, val;
  39926     int64_t len, n, res;
  39927     JSValue *arrp;
  39928     uint32_t count;
  39929 
  39930     obj = JS_ToObject(ctx, this_val);
  39931     if (js_get_length64(ctx, &len, obj))
  39932         goto exception;
  39933 
  39934     res = -1;
  39935     if (len > 0) {
  39936         n = 0;
  39937         if (argc > 1) {
  39938             if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len))
  39939                 goto exception;
  39940         }
  39941         if (js_get_fast_array(ctx, obj, &arrp, &count)) {
  39942             for (; n < count; n++) {
  39943                 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]),
  39944                                   JS_DupValue(ctx, arrp[n]), JS_EQ_STRICT)) {
  39945                     res = n;
  39946                     goto done;
  39947                 }
  39948             }
  39949         }
  39950         for (; n < len; n++) {
  39951             int present = JS_TryGetPropertyInt64(ctx, obj, n, &val);
  39952             if (present < 0)
  39953                 goto exception;
  39954             if (present) {
  39955                 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) {
  39956                     res = n;
  39957                     break;
  39958                 }
  39959             }
  39960         }
  39961     }
  39962  done:
  39963     JS_FreeValue(ctx, obj);
  39964     return JS_NewInt64(ctx, res);
  39965 
  39966  exception:
  39967     JS_FreeValue(ctx, obj);
  39968     return JS_EXCEPTION;
  39969 }
  39970 
  39971 static JSValue js_array_lastIndexOf(JSContext *ctx, JSValueConst this_val,
  39972                                     int argc, JSValueConst *argv)
  39973 {
  39974     JSValue obj, val;
  39975     int64_t len, n, res;
  39976     int present;
  39977 
  39978     obj = JS_ToObject(ctx, this_val);
  39979     if (js_get_length64(ctx, &len, obj))
  39980         goto exception;
  39981 
  39982     res = -1;
  39983     if (len > 0) {
  39984         n = len - 1;
  39985         if (argc > 1) {
  39986             if (JS_ToInt64Clamp(ctx, &n, argv[1], -1, len - 1, len))
  39987                 goto exception;
  39988         }
  39989         /* XXX: should special case fast arrays */
  39990         for (; n >= 0; n--) {
  39991             present = JS_TryGetPropertyInt64(ctx, obj, n, &val);
  39992             if (present < 0)
  39993                 goto exception;
  39994             if (present) {
  39995                 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) {
  39996                     res = n;
  39997                     break;
  39998                 }
  39999             }
  40000         }
  40001     }
  40002     JS_FreeValue(ctx, obj);
  40003     return JS_NewInt64(ctx, res);
  40004 
  40005  exception:
  40006     JS_FreeValue(ctx, obj);
  40007     return JS_EXCEPTION;
  40008 }
  40009 
  40010 enum {
  40011     ArrayFind,
  40012     ArrayFindIndex,
  40013     ArrayFindLast,
  40014     ArrayFindLastIndex,
  40015 };
  40016 
  40017 static JSValue js_array_find(JSContext *ctx, JSValueConst this_val,
  40018                              int argc, JSValueConst *argv, int mode)
  40019 {
  40020     JSValueConst func, this_arg;
  40021     JSValueConst args[3];
  40022     JSValue obj, val, index_val, res;
  40023     int64_t len, k, end;
  40024     int dir;
  40025 
  40026     index_val = JS_UNDEFINED;
  40027     val = JS_UNDEFINED;
  40028     obj = JS_ToObject(ctx, this_val);
  40029     if (js_get_length64(ctx, &len, obj))
  40030         goto exception;
  40031 
  40032     func = argv[0];
  40033     if (check_function(ctx, func))
  40034         goto exception;
  40035 
  40036     this_arg = JS_UNDEFINED;
  40037     if (argc > 1)
  40038         this_arg = argv[1];
  40039 
  40040     k = 0;
  40041     dir = 1;
  40042     end = len;
  40043     if (mode == ArrayFindLast || mode == ArrayFindLastIndex) {
  40044         k = len - 1;
  40045         dir = -1;
  40046         end = -1;
  40047     }
  40048 
  40049     // TODO(bnoordhuis) add fast path for fast arrays
  40050     for(; k != end; k += dir) {
  40051         index_val = JS_NewInt64(ctx, k);
  40052         if (JS_IsException(index_val))
  40053             goto exception;
  40054         val = JS_GetPropertyValue(ctx, obj, index_val);
  40055         if (JS_IsException(val))
  40056             goto exception;
  40057         args[0] = val;
  40058         args[1] = index_val;
  40059         args[2] = this_val;
  40060         res = JS_Call(ctx, func, this_arg, 3, args);
  40061         if (JS_IsException(res))
  40062             goto exception;
  40063         if (JS_ToBoolFree(ctx, res)) {
  40064             if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) {
  40065                 JS_FreeValue(ctx, val);
  40066                 JS_FreeValue(ctx, obj);
  40067                 return index_val;
  40068             } else {
  40069                 JS_FreeValue(ctx, index_val);
  40070                 JS_FreeValue(ctx, obj);
  40071                 return val;
  40072             }
  40073         }
  40074         JS_FreeValue(ctx, val);
  40075         JS_FreeValue(ctx, index_val);
  40076     }
  40077     JS_FreeValue(ctx, obj);
  40078     if (mode == ArrayFindIndex || mode == ArrayFindLastIndex)
  40079         return JS_NewInt32(ctx, -1);
  40080     else
  40081         return JS_UNDEFINED;
  40082 
  40083 exception:
  40084     JS_FreeValue(ctx, index_val);
  40085     JS_FreeValue(ctx, val);
  40086     JS_FreeValue(ctx, obj);
  40087     return JS_EXCEPTION;
  40088 }
  40089 
  40090 static JSValue js_array_toString(JSContext *ctx, JSValueConst this_val,
  40091                                  int argc, JSValueConst *argv)
  40092 {
  40093     JSValue obj, method, ret;
  40094 
  40095     obj = JS_ToObject(ctx, this_val);
  40096     if (JS_IsException(obj))
  40097         return JS_EXCEPTION;
  40098     method = JS_GetProperty(ctx, obj, JS_ATOM_join);
  40099     if (JS_IsException(method)) {
  40100         ret = JS_EXCEPTION;
  40101     } else
  40102     if (!JS_IsFunction(ctx, method)) {
  40103         /* Use intrinsic Object.prototype.toString */
  40104         JS_FreeValue(ctx, method);
  40105         ret = js_object_toString(ctx, obj, 0, NULL);
  40106     } else {
  40107         ret = JS_CallFree(ctx, method, obj, 0, NULL);
  40108     }
  40109     JS_FreeValue(ctx, obj);
  40110     return ret;
  40111 }
  40112 
  40113 static JSValue js_array_join(JSContext *ctx, JSValueConst this_val,
  40114                              int argc, JSValueConst *argv, int toLocaleString)
  40115 {
  40116     JSValue obj, sep = JS_UNDEFINED, el;
  40117     StringBuffer b_s, *b = &b_s;
  40118     JSString *p = NULL;
  40119     int64_t i, n;
  40120     int c;
  40121 
  40122     obj = JS_ToObject(ctx, this_val);
  40123     if (js_get_length64(ctx, &n, obj))
  40124         goto exception;
  40125 
  40126     c = ',';    /* default separator */
  40127     if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) {
  40128         sep = JS_ToString(ctx, argv[0]);
  40129         if (JS_IsException(sep))
  40130             goto exception;
  40131         p = JS_VALUE_GET_STRING(sep);
  40132         if (p->len == 1 && !p->is_wide_char)
  40133             c = p->u.str8[0];
  40134         else
  40135             c = -1;
  40136     }
  40137     string_buffer_init(ctx, b, 0);
  40138 
  40139     for(i = 0; i < n; i++) {
  40140         if (i > 0) {
  40141             if (c >= 0) {
  40142                 string_buffer_putc8(b, c);
  40143             } else {
  40144                 string_buffer_concat(b, p, 0, p->len);
  40145             }
  40146         }
  40147         el = JS_GetPropertyUint32(ctx, obj, i);
  40148         if (JS_IsException(el))
  40149             goto fail;
  40150         if (!JS_IsNull(el) && !JS_IsUndefined(el)) {
  40151             if (toLocaleString) {
  40152                 el = JS_ToLocaleStringFree(ctx, el);
  40153             }
  40154             if (string_buffer_concat_value_free(b, el))
  40155                 goto fail;
  40156         }
  40157     }
  40158     JS_FreeValue(ctx, sep);
  40159     JS_FreeValue(ctx, obj);
  40160     return string_buffer_end(b);
  40161 
  40162 fail:
  40163     string_buffer_free(b);
  40164     JS_FreeValue(ctx, sep);
  40165 exception:
  40166     JS_FreeValue(ctx, obj);
  40167     return JS_EXCEPTION;
  40168 }
  40169 
  40170 static JSValue js_array_pop(JSContext *ctx, JSValueConst this_val,
  40171                             int argc, JSValueConst *argv, int shift)
  40172 {
  40173     JSValue obj, res = JS_UNDEFINED;
  40174     int64_t len, newLen;
  40175     JSValue *arrp;
  40176     uint32_t count32;
  40177 
  40178     obj = JS_ToObject(ctx, this_val);
  40179     if (js_get_length64(ctx, &len, obj))
  40180         goto exception;
  40181     newLen = 0;
  40182     if (len > 0) {
  40183         newLen = len - 1;
  40184         /* Special case fast arrays */
  40185         if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
  40186             JSObject *p = JS_VALUE_GET_OBJ(obj);
  40187             if (shift) {
  40188                 res = arrp[0];
  40189                 memmove(arrp, arrp + 1, (count32 - 1) * sizeof(*arrp));
  40190                 p->u.array.count--;
  40191             } else {
  40192                 res = arrp[count32 - 1];
  40193                 p->u.array.count--;
  40194             }
  40195         } else {
  40196             if (shift) {
  40197                 res = JS_GetPropertyInt64(ctx, obj, 0);
  40198                 if (JS_IsException(res))
  40199                     goto exception;
  40200                 if (JS_CopySubArray(ctx, obj, 0, 1, len - 1, +1))
  40201                     goto exception;
  40202             } else {
  40203                 res = JS_GetPropertyInt64(ctx, obj, newLen);
  40204                 if (JS_IsException(res))
  40205                     goto exception;
  40206             }
  40207             if (JS_DeletePropertyInt64(ctx, obj, newLen, JS_PROP_THROW) < 0)
  40208                 goto exception;
  40209         }
  40210     }
  40211     if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0)
  40212         goto exception;
  40213 
  40214     JS_FreeValue(ctx, obj);
  40215     return res;
  40216 
  40217  exception:
  40218     JS_FreeValue(ctx, res);
  40219     JS_FreeValue(ctx, obj);
  40220     return JS_EXCEPTION;
  40221 }
  40222 
  40223 int qjs_array_append_new(JSContext *ctx, JSValue this_val, JSValue item)
  40224 {
  40225     JSValue obj;
  40226     int64_t len, from, newLen;
  40227 
  40228     obj = JS_ToObject(ctx, this_val);
  40229     if (js_get_length64(ctx, &len, obj))
  40230         goto exception;
  40231     newLen = len + 1;
  40232     if (newLen > MAX_SAFE_INTEGER) {
  40233         JS_ThrowTypeError(ctx, "Array loo long");
  40234         goto exception;
  40235     }
  40236     from = len;
  40237     if (JS_SetPropertyInt64(ctx, obj, from, item) < 0)
  40238         goto exception;
  40239     if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0)
  40240         goto exception;
  40241 
  40242     JS_FreeValue(ctx, obj);
  40243     return 0;
  40244 
  40245  exception:
  40246     JS_FreeValue(ctx, obj);
  40247     return -1;
  40248 }
  40249 
  40250 static JSValue js_array_push(JSContext *ctx, JSValueConst this_val,
  40251                              int argc, JSValueConst *argv, int unshift)
  40252 {
  40253     JSValue obj;
  40254     int i;
  40255     int64_t len, from, newLen;
  40256 
  40257     obj = JS_ToObject(ctx, this_val);
  40258     if (js_get_length64(ctx, &len, obj))
  40259         goto exception;
  40260     newLen = len + argc;
  40261     if (newLen > MAX_SAFE_INTEGER) {
  40262         JS_ThrowTypeError(ctx, "Array loo long");
  40263         goto exception;
  40264     }
  40265     from = len;
  40266     if (unshift && argc > 0) {
  40267         if (JS_CopySubArray(ctx, obj, argc, 0, len, -1))
  40268             goto exception;
  40269         from = 0;
  40270     }
  40271     for(i = 0; i < argc; i++) {
  40272         if (JS_SetPropertyInt64(ctx, obj, from + i,
  40273                                 JS_DupValue(ctx, argv[i])) < 0)
  40274             goto exception;
  40275     }
  40276     if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0)
  40277         goto exception;
  40278 
  40279     JS_FreeValue(ctx, obj);
  40280     return JS_NewInt64(ctx, newLen);
  40281 
  40282  exception:
  40283     JS_FreeValue(ctx, obj);
  40284     return JS_EXCEPTION;
  40285 }
  40286 
  40287 static JSValue js_array_reverse(JSContext *ctx, JSValueConst this_val,
  40288                                 int argc, JSValueConst *argv)
  40289 {
  40290     JSValue obj, lval, hval;
  40291     JSValue *arrp;
  40292     int64_t len, l, h;
  40293     int l_present, h_present;
  40294     uint32_t count32;
  40295 
  40296     lval = JS_UNDEFINED;
  40297     obj = JS_ToObject(ctx, this_val);
  40298     if (js_get_length64(ctx, &len, obj))
  40299         goto exception;
  40300 
  40301     /* Special case fast arrays */
  40302     if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
  40303         uint32_t ll, hh;
  40304 
  40305         if (count32 > 1) {
  40306             for (ll = 0, hh = count32 - 1; ll < hh; ll++, hh--) {
  40307                 lval = arrp[ll];
  40308                 arrp[ll] = arrp[hh];
  40309                 arrp[hh] = lval;
  40310             }
  40311         }
  40312         return obj;
  40313     }
  40314 
  40315     for (l = 0, h = len - 1; l < h; l++, h--) {
  40316         l_present = JS_TryGetPropertyInt64(ctx, obj, l, &lval);
  40317         if (l_present < 0)
  40318             goto exception;
  40319         h_present = JS_TryGetPropertyInt64(ctx, obj, h, &hval);
  40320         if (h_present < 0)
  40321             goto exception;
  40322         if (h_present) {
  40323             if (JS_SetPropertyInt64(ctx, obj, l, hval) < 0)
  40324                 goto exception;
  40325 
  40326             if (l_present) {
  40327                 if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) {
  40328                     lval = JS_UNDEFINED;
  40329                     goto exception;
  40330                 }
  40331                 lval = JS_UNDEFINED;
  40332             } else {
  40333                 if (JS_DeletePropertyInt64(ctx, obj, h, JS_PROP_THROW) < 0)
  40334                     goto exception;
  40335             }
  40336         } else {
  40337             if (l_present) {
  40338                 if (JS_DeletePropertyInt64(ctx, obj, l, JS_PROP_THROW) < 0)
  40339                     goto exception;
  40340                 if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) {
  40341                     lval = JS_UNDEFINED;
  40342                     goto exception;
  40343                 }
  40344                 lval = JS_UNDEFINED;
  40345             }
  40346         }
  40347     }
  40348     return obj;
  40349 
  40350  exception:
  40351     JS_FreeValue(ctx, lval);
  40352     JS_FreeValue(ctx, obj);
  40353     return JS_EXCEPTION;
  40354 }
  40355 
  40356 // Note: a.toReversed() is a.slice().reverse() with the twist that a.slice()
  40357 // leaves holes in sparse arrays intact whereas a.toReversed() replaces them
  40358 // with undefined, thus in effect creating a dense array.
  40359 // Does not use Array[@@species], always returns a base Array.
  40360 static JSValue js_array_toReversed(JSContext *ctx, JSValueConst this_val,
  40361                                    int argc, JSValueConst *argv)
  40362 {
  40363     JSValue arr, obj, ret, *arrp, *pval;
  40364     JSObject *p;
  40365     int64_t i, len;
  40366     uint32_t count32;
  40367 
  40368     ret = JS_EXCEPTION;
  40369     arr = JS_UNDEFINED;
  40370     obj = JS_ToObject(ctx, this_val);
  40371     if (js_get_length64(ctx, &len, obj))
  40372         goto exception;
  40373 
  40374     arr = js_allocate_fast_array(ctx, len);
  40375     if (JS_IsException(arr))
  40376         goto exception;
  40377 
  40378     if (len > 0) {
  40379         p = JS_VALUE_GET_OBJ(arr);
  40380 
  40381         i = len - 1;
  40382         pval = p->u.array.u.values;
  40383         if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
  40384             for (; i >= 0; i--, pval++)
  40385                 *pval = JS_DupValue(ctx, arrp[i]);
  40386         } else {
  40387             // Query order is observable; test262 expects descending order.
  40388             for (; i >= 0; i--, pval++) {
  40389                 if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) {
  40390                     // Exception; initialize remaining elements.
  40391                     for (; i >= 0; i--, pval++)
  40392                         *pval = JS_UNDEFINED;
  40393                     goto exception;
  40394                 }
  40395             }
  40396         }
  40397 
  40398         if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, len)) < 0)
  40399             goto exception;
  40400     }
  40401 
  40402     ret = arr;
  40403     arr = JS_UNDEFINED;
  40404 
  40405 exception:
  40406     JS_FreeValue(ctx, arr);
  40407     JS_FreeValue(ctx, obj);
  40408     return ret;
  40409 }
  40410 
  40411 static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val,
  40412                               int argc, JSValueConst *argv, int splice)
  40413 {
  40414     JSValue obj, arr, val, len_val;
  40415     int64_t len, start, k, final, n, count, del_count, new_len;
  40416     int kPresent;
  40417     JSValue *arrp;
  40418     uint32_t count32, i, item_count;
  40419 
  40420     arr = JS_UNDEFINED;
  40421     obj = JS_ToObject(ctx, this_val);
  40422     if (js_get_length64(ctx, &len, obj))
  40423         goto exception;
  40424 
  40425     if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len))
  40426         goto exception;
  40427 
  40428     if (splice) {
  40429         if (argc == 0) {
  40430             item_count = 0;
  40431             del_count = 0;
  40432         } else
  40433         if (argc == 1) {
  40434             item_count = 0;
  40435             del_count = len - start;
  40436         } else {
  40437             item_count = argc - 2;
  40438             if (JS_ToInt64Clamp(ctx, &del_count, argv[1], 0, len - start, 0))
  40439                 goto exception;
  40440         }
  40441         if (len + item_count - del_count > MAX_SAFE_INTEGER) {
  40442             JS_ThrowTypeError(ctx, "Array loo long");
  40443             goto exception;
  40444         }
  40445         count = del_count;
  40446     } else {
  40447         item_count = 0; /* avoid warning */
  40448         final = len;
  40449         if (!JS_IsUndefined(argv[1])) {
  40450             if (JS_ToInt64Clamp(ctx, &final, argv[1], 0, len, len))
  40451                 goto exception;
  40452         }
  40453         count = max_int64(final - start, 0);
  40454     }
  40455     len_val = JS_NewInt64(ctx, count);
  40456     arr = JS_ArraySpeciesCreate(ctx, obj, len_val);
  40457     JS_FreeValue(ctx, len_val);
  40458     if (JS_IsException(arr))
  40459         goto exception;
  40460 
  40461     k = start;
  40462     final = start + count;
  40463     n = 0;
  40464     /* The fast array test on arr ensures that
  40465        JS_CreateDataPropertyUint32() won't modify obj in case arr is
  40466        an exotic object */
  40467     /* Special case fast arrays */
  40468     if (js_get_fast_array(ctx, obj, &arrp, &count32) &&
  40469         js_is_fast_array(ctx, arr)) {
  40470         /* XXX: should share code with fast array constructor */
  40471         for (; k < final && k < count32; k++, n++) {
  40472             if (JS_CreateDataPropertyUint32(ctx, arr, n, JS_DupValue(ctx, arrp[k]), JS_PROP_THROW) < 0)
  40473                 goto exception;
  40474         }
  40475     }
  40476     /* Copy the remaining elements if any (handle case of inherited properties) */
  40477     for (; k < final; k++, n++) {
  40478         kPresent = JS_TryGetPropertyInt64(ctx, obj, k, &val);
  40479         if (kPresent < 0)
  40480             goto exception;
  40481         if (kPresent) {
  40482             if (JS_CreateDataPropertyUint32(ctx, arr, n, val, JS_PROP_THROW) < 0)
  40483                 goto exception;
  40484         }
  40485     }
  40486     if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0)
  40487         goto exception;
  40488 
  40489     if (splice) {
  40490         new_len = len + item_count - del_count;
  40491         if (item_count != del_count) {
  40492             if (JS_CopySubArray(ctx, obj, start + item_count,
  40493                                 start + del_count, len - (start + del_count),
  40494                                 item_count <= del_count ? +1 : -1) < 0)
  40495                 goto exception;
  40496 
  40497             for (k = len; k-- > new_len; ) {
  40498                 if (JS_DeletePropertyInt64(ctx, obj, k, JS_PROP_THROW) < 0)
  40499                     goto exception;
  40500             }
  40501         }
  40502         for (i = 0; i < item_count; i++) {
  40503             if (JS_SetPropertyInt64(ctx, obj, start + i, JS_DupValue(ctx, argv[i + 2])) < 0)
  40504                 goto exception;
  40505         }
  40506         if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, new_len)) < 0)
  40507             goto exception;
  40508     }
  40509     JS_FreeValue(ctx, obj);
  40510     return arr;
  40511 
  40512  exception:
  40513     JS_FreeValue(ctx, obj);
  40514     JS_FreeValue(ctx, arr);
  40515     return JS_EXCEPTION;
  40516 }
  40517 
  40518 static JSValue js_array_toSpliced(JSContext *ctx, JSValueConst this_val,
  40519                                   int argc, JSValueConst *argv)
  40520 {
  40521     JSValue arr, obj, ret, *arrp, *pval, *last;
  40522     JSObject *p;
  40523     int64_t i, j, len, newlen, start, add, del;
  40524     uint32_t count32;
  40525 
  40526     pval = NULL;
  40527     last = NULL;
  40528     ret = JS_EXCEPTION;
  40529     arr = JS_UNDEFINED;
  40530 
  40531     obj = JS_ToObject(ctx, this_val);
  40532     if (js_get_length64(ctx, &len, obj))
  40533         goto exception;
  40534 
  40535     start = 0;
  40536     if (argc > 0)
  40537         if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len))
  40538             goto exception;
  40539 
  40540     del = 0;
  40541     if (argc > 0)
  40542         del = len - start;
  40543     if (argc > 1)
  40544         if (JS_ToInt64Clamp(ctx, &del, argv[1], 0, del, 0))
  40545             goto exception;
  40546 
  40547     add = 0;
  40548     if (argc > 2)
  40549         add = argc - 2;
  40550 
  40551     newlen = len + add - del;
  40552     if (newlen > MAX_SAFE_INTEGER) {
  40553         JS_ThrowTypeError(ctx, "invalid array length");
  40554         goto exception;
  40555     }
  40556 
  40557     arr = js_allocate_fast_array(ctx, newlen);
  40558     if (JS_IsException(arr))
  40559         goto exception;
  40560 
  40561     if (newlen <= 0)
  40562         goto done;
  40563 
  40564     p = JS_VALUE_GET_OBJ(arr);
  40565     pval = &p->u.array.u.values[0];
  40566     last = &p->u.array.u.values[newlen];
  40567 
  40568     if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
  40569         for (i = 0; i < start; i++, pval++)
  40570             *pval = JS_DupValue(ctx, arrp[i]);
  40571         for (j = 0; j < add; j++, pval++)
  40572             *pval = JS_DupValue(ctx, argv[2 + j]);
  40573         for (i += del; i < len; i++, pval++)
  40574             *pval = JS_DupValue(ctx, arrp[i]);
  40575     } else {
  40576         for (i = 0; i < start; i++, pval++)
  40577             if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval))
  40578                 goto exception;
  40579         for (j = 0; j < add; j++, pval++)
  40580             *pval = JS_DupValue(ctx, argv[2 + j]);
  40581         for (i += del; i < len; i++, pval++)
  40582             if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval))
  40583                 goto exception;
  40584     }
  40585 
  40586     assert(pval == last);
  40587 
  40588     if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, newlen)) < 0)
  40589         goto exception;
  40590 
  40591 done:
  40592     ret = arr;
  40593     arr = JS_UNDEFINED;
  40594 
  40595 exception:
  40596     while (pval != last)
  40597         *pval++ = JS_UNDEFINED;
  40598 
  40599     JS_FreeValue(ctx, arr);
  40600     JS_FreeValue(ctx, obj);
  40601     return ret;
  40602 }
  40603 
  40604 static JSValue js_array_copyWithin(JSContext *ctx, JSValueConst this_val,
  40605                                    int argc, JSValueConst *argv)
  40606 {
  40607     JSValue obj;
  40608     int64_t len, from, to, final, count;
  40609 
  40610     obj = JS_ToObject(ctx, this_val);
  40611     if (js_get_length64(ctx, &len, obj))
  40612         goto exception;
  40613 
  40614     if (JS_ToInt64Clamp(ctx, &to, argv[0], 0, len, len))
  40615         goto exception;
  40616 
  40617     if (JS_ToInt64Clamp(ctx, &from, argv[1], 0, len, len))
  40618         goto exception;
  40619 
  40620     final = len;
  40621     if (argc > 2 && !JS_IsUndefined(argv[2])) {
  40622         if (JS_ToInt64Clamp(ctx, &final, argv[2], 0, len, len))
  40623             goto exception;
  40624     }
  40625 
  40626     count = min_int64(final - from, len - to);
  40627 
  40628     if (JS_CopySubArray(ctx, obj, to, from, count,
  40629                         (from < to && to < from + count) ? -1 : +1))
  40630         goto exception;
  40631 
  40632     return obj;
  40633 
  40634  exception:
  40635     JS_FreeValue(ctx, obj);
  40636     return JS_EXCEPTION;
  40637 }
  40638 
  40639 static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValueConst target,
  40640                                    JSValueConst source, int64_t sourceLen,
  40641                                    int64_t targetIndex, int depth,
  40642                                    JSValueConst mapperFunction,
  40643                                    JSValueConst thisArg)
  40644 {
  40645     JSValue element;
  40646     int64_t sourceIndex, elementLen;
  40647     int present, is_array;
  40648 
  40649     if (js_check_stack_overflow(ctx->rt, 0)) {
  40650         JS_ThrowStackOverflow(ctx);
  40651         return -1;
  40652     }
  40653 
  40654     for (sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) {
  40655         present = JS_TryGetPropertyInt64(ctx, source, sourceIndex, &element);
  40656         if (present < 0)
  40657             return -1;
  40658         if (!present)
  40659             continue;
  40660         if (!JS_IsUndefined(mapperFunction)) {
  40661             JSValueConst args[3] = { element, JS_NewInt64(ctx, sourceIndex), source };
  40662             element = JS_Call(ctx, mapperFunction, thisArg, 3, args);
  40663             JS_FreeValue(ctx, (JSValue)args[0]);
  40664             JS_FreeValue(ctx, (JSValue)args[1]);
  40665             if (JS_IsException(element))
  40666                 return -1;
  40667         }
  40668         if (depth > 0) {
  40669             is_array = JS_IsArray(ctx, element);
  40670             if (is_array < 0)
  40671                 goto fail;
  40672             if (is_array) {
  40673                 if (js_get_length64(ctx, &elementLen, element) < 0)
  40674                     goto fail;
  40675                 targetIndex = JS_FlattenIntoArray(ctx, target, element,
  40676                                                   elementLen, targetIndex,
  40677                                                   depth - 1,
  40678                                                   JS_UNDEFINED, JS_UNDEFINED);
  40679                 if (targetIndex < 0)
  40680                     goto fail;
  40681                 JS_FreeValue(ctx, element);
  40682                 continue;
  40683             }
  40684         }
  40685         if (targetIndex >= MAX_SAFE_INTEGER) {
  40686             JS_ThrowTypeError(ctx, "Array too long");
  40687             goto fail;
  40688         }
  40689         if (JS_DefinePropertyValueInt64(ctx, target, targetIndex, element,
  40690                                         JS_PROP_C_W_E | JS_PROP_THROW) < 0)
  40691             return -1;
  40692         targetIndex++;
  40693     }
  40694     return targetIndex;
  40695 
  40696 fail:
  40697     JS_FreeValue(ctx, element);
  40698     return -1;
  40699 }
  40700 
  40701 static JSValue js_array_flatten(JSContext *ctx, JSValueConst this_val,
  40702                                 int argc, JSValueConst *argv, int map)
  40703 {
  40704     JSValue obj, arr;
  40705     JSValueConst mapperFunction, thisArg;
  40706     int64_t sourceLen;
  40707     int depthNum;
  40708 
  40709     arr = JS_UNDEFINED;
  40710     obj = JS_ToObject(ctx, this_val);
  40711     if (js_get_length64(ctx, &sourceLen, obj))
  40712         goto exception;
  40713 
  40714     depthNum = 1;
  40715     mapperFunction = JS_UNDEFINED;
  40716     thisArg = JS_UNDEFINED;
  40717     if (map) {
  40718         mapperFunction = argv[0];
  40719         if (argc > 1) {
  40720             thisArg = argv[1];
  40721         }
  40722         if (check_function(ctx, mapperFunction))
  40723             goto exception;
  40724     } else {
  40725         if (argc > 0 && !JS_IsUndefined(argv[0])) {
  40726             if (JS_ToInt32Sat(ctx, &depthNum, argv[0]) < 0)
  40727                 goto exception;
  40728         }
  40729     }
  40730     arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));
  40731     if (JS_IsException(arr))
  40732         goto exception;
  40733     if (JS_FlattenIntoArray(ctx, arr, obj, sourceLen, 0, depthNum,
  40734                             mapperFunction, thisArg) < 0)
  40735         goto exception;
  40736     JS_FreeValue(ctx, obj);
  40737     return arr;
  40738 
  40739 exception:
  40740     JS_FreeValue(ctx, obj);
  40741     JS_FreeValue(ctx, arr);
  40742     return JS_EXCEPTION;
  40743 }
  40744 
  40745 /* Array sort */
  40746 
  40747 typedef struct ValueSlot {
  40748     JSValue val;
  40749     JSString *str;
  40750     int64_t pos;
  40751 } ValueSlot;
  40752 
  40753 struct array_sort_context {
  40754     JSContext *ctx;
  40755     int exception;
  40756     int has_method;
  40757     JSValueConst method;
  40758 };
  40759 
  40760 static int js_array_cmp_generic(const void *a, const void *b, void *opaque) {
  40761     struct array_sort_context *psc = opaque;
  40762     JSContext *ctx = psc->ctx;
  40763     JSValueConst argv[2];
  40764     JSValue res;
  40765     ValueSlot *ap = (ValueSlot *)(void *)a;
  40766     ValueSlot *bp = (ValueSlot *)(void *)b;
  40767     int cmp;
  40768 
  40769     if (psc->exception)
  40770         return 0;
  40771 
  40772     if (psc->has_method) {
  40773         /* custom sort function is specified as returning 0 for identical
  40774          * objects: avoid method call overhead.
  40775          */
  40776         if (!memcmp(&ap->val, &bp->val, sizeof(ap->val)))
  40777             goto cmp_same;
  40778         argv[0] = ap->val;
  40779         argv[1] = bp->val;
  40780         res = JS_Call(ctx, psc->method, JS_UNDEFINED, 2, argv);
  40781         if (JS_IsException(res))
  40782             goto exception;
  40783         if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) {
  40784             int val = JS_VALUE_GET_INT(res);
  40785             cmp = (val > 0) - (val < 0);
  40786         } else {
  40787             double val;
  40788             if (JS_ToFloat64Free(ctx, &val, res) < 0)
  40789                 goto exception;
  40790             cmp = (val > 0) - (val < 0);
  40791         }
  40792     } else {
  40793         /* Not supposed to bypass ToString even for identical objects as
  40794          * tested in test262/test/built-ins/Array/prototype/sort/bug_596_1.js
  40795          */
  40796         if (!ap->str) {
  40797             JSValue str = JS_ToString(ctx, ap->val);
  40798             if (JS_IsException(str))
  40799                 goto exception;
  40800             ap->str = JS_VALUE_GET_STRING(str);
  40801         }
  40802         if (!bp->str) {
  40803             JSValue str = JS_ToString(ctx, bp->val);
  40804             if (JS_IsException(str))
  40805                 goto exception;
  40806             bp->str = JS_VALUE_GET_STRING(str);
  40807         }
  40808         cmp = js_string_compare(ctx, ap->str, bp->str);
  40809     }
  40810     if (cmp != 0)
  40811         return cmp;
  40812 cmp_same:
  40813     /* make sort stable: compare array offsets */
  40814     return (ap->pos > bp->pos) - (ap->pos < bp->pos);
  40815 
  40816 exception:
  40817     psc->exception = 1;
  40818     return 0;
  40819 }
  40820 
  40821 static JSValue js_array_sort(JSContext *ctx, JSValueConst this_val,
  40822                              int argc, JSValueConst *argv)
  40823 {
  40824     struct array_sort_context asc = { ctx, 0, 0, argv[0] };
  40825     JSValue obj = JS_UNDEFINED;
  40826     ValueSlot *array = NULL;
  40827     size_t array_size = 0, pos = 0, n = 0;
  40828     int64_t i, len, undefined_count = 0;
  40829     int present;
  40830 
  40831     if (!JS_IsUndefined(asc.method)) {
  40832         if (check_function(ctx, asc.method))
  40833             goto exception;
  40834         asc.has_method = 1;
  40835     }
  40836     obj = JS_ToObject(ctx, this_val);
  40837     if (js_get_length64(ctx, &len, obj))
  40838         goto exception;
  40839 
  40840     /* XXX: should special case fast arrays */
  40841     for (i = 0; i < len; i++) {
  40842         if (pos >= array_size) {
  40843             size_t new_size, slack;
  40844             ValueSlot *new_array;
  40845             new_size = (array_size + (array_size >> 1) + 31) & ~15;
  40846             new_array = js_realloc2(ctx, array, new_size * sizeof(*array), &slack);
  40847             if (new_array == NULL)
  40848                 goto exception;
  40849             new_size += slack / sizeof(*new_array);
  40850             array = new_array;
  40851             array_size = new_size;
  40852         }
  40853         present = JS_TryGetPropertyInt64(ctx, obj, i, &array[pos].val);
  40854         if (present < 0)
  40855             goto exception;
  40856         if (present == 0)
  40857             continue;
  40858         if (JS_IsUndefined(array[pos].val)) {
  40859             undefined_count++;
  40860             continue;
  40861         }
  40862         array[pos].str = NULL;
  40863         array[pos].pos = i;
  40864         pos++;
  40865     }
  40866     rqsort(array, pos, sizeof(*array), js_array_cmp_generic, &asc);
  40867     if (asc.exception)
  40868         goto exception;
  40869 
  40870     /* XXX: should special case fast arrays */
  40871     while (n < pos) {
  40872         if (array[n].str)
  40873             JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str));
  40874         if (array[n].pos == n) {
  40875             JS_FreeValue(ctx, array[n].val);
  40876         } else {
  40877             if (JS_SetPropertyInt64(ctx, obj, n, array[n].val) < 0) {
  40878                 n++;
  40879                 goto exception;
  40880             }
  40881         }
  40882         n++;
  40883     }
  40884     js_free(ctx, array);
  40885     for (i = n; undefined_count-- > 0; i++) {
  40886         if (JS_SetPropertyInt64(ctx, obj, i, JS_UNDEFINED) < 0)
  40887             goto fail;
  40888     }
  40889     for (; i < len; i++) {
  40890         if (JS_DeletePropertyInt64(ctx, obj, i, JS_PROP_THROW) < 0)
  40891             goto fail;
  40892     }
  40893     return obj;
  40894 
  40895 exception:
  40896     for (; n < pos; n++) {
  40897         JS_FreeValue(ctx, array[n].val);
  40898         if (array[n].str)
  40899             JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str));
  40900     }
  40901     js_free(ctx, array);
  40902 fail:
  40903     JS_FreeValue(ctx, obj);
  40904     return JS_EXCEPTION;
  40905 }
  40906 
  40907 // Note: a.toSorted() is a.slice().sort() with the twist that a.slice()
  40908 // leaves holes in sparse arrays intact whereas a.toSorted() replaces them
  40909 // with undefined, thus in effect creating a dense array.
  40910 // Does not use Array[@@species], always returns a base Array.
  40911 static JSValue js_array_toSorted(JSContext *ctx, JSValueConst this_val,
  40912                                  int argc, JSValueConst *argv)
  40913 {
  40914     JSValue arr, obj, ret, *arrp, *pval;
  40915     JSObject *p;
  40916     int64_t i, len;
  40917     uint32_t count32;
  40918     int ok;
  40919 
  40920     ok = JS_IsUndefined(argv[0]) || JS_IsFunction(ctx, argv[0]);
  40921     if (!ok)
  40922         return JS_ThrowTypeError(ctx, "not a function");
  40923 
  40924     ret = JS_EXCEPTION;
  40925     arr = JS_UNDEFINED;
  40926     obj = JS_ToObject(ctx, this_val);
  40927     if (js_get_length64(ctx, &len, obj))
  40928         goto exception;
  40929 
  40930     arr = js_allocate_fast_array(ctx, len);
  40931     if (JS_IsException(arr))
  40932         goto exception;
  40933 
  40934     if (len > 0) {
  40935         p = JS_VALUE_GET_OBJ(arr);
  40936         i = 0;
  40937         pval = p->u.array.u.values;
  40938         if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
  40939             for (; i < len; i++, pval++)
  40940                 *pval = JS_DupValue(ctx, arrp[i]);
  40941         } else {
  40942             for (; i < len; i++, pval++) {
  40943                 if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) {
  40944                     for (; i < len; i++, pval++)
  40945                         *pval = JS_UNDEFINED;
  40946                     goto exception;
  40947                 }
  40948             }
  40949         }
  40950 
  40951         if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, len)) < 0)
  40952             goto exception;
  40953     }
  40954 
  40955     ret = js_array_sort(ctx, arr, argc, argv);
  40956     if (JS_IsException(ret))
  40957         goto exception;
  40958     JS_FreeValue(ctx, ret);
  40959 
  40960     ret = arr;
  40961     arr = JS_UNDEFINED;
  40962 
  40963 exception:
  40964     JS_FreeValue(ctx, arr);
  40965     JS_FreeValue(ctx, obj);
  40966     return ret;
  40967 }
  40968 
  40969 typedef struct JSArrayIteratorData {
  40970     JSValue obj;
  40971     JSIteratorKindEnum kind;
  40972     uint32_t idx;
  40973 } JSArrayIteratorData;
  40974 
  40975 static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val)
  40976 {
  40977     JSObject *p = JS_VALUE_GET_OBJ(val);
  40978     JSArrayIteratorData *it = p->u.array_iterator_data;
  40979     if (it) {
  40980         JS_FreeValueRT(rt, it->obj);
  40981         js_free_rt(rt, it);
  40982     }
  40983 }
  40984 
  40985 static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val,
  40986                                    JS_MarkFunc *mark_func)
  40987 {
  40988     JSObject *p = JS_VALUE_GET_OBJ(val);
  40989     JSArrayIteratorData *it = p->u.array_iterator_data;
  40990     if (it) {
  40991         JS_MarkValue(rt, it->obj, mark_func);
  40992     }
  40993 }
  40994 
  40995 static JSValue js_create_array(JSContext *ctx, int len, JSValueConst *tab)
  40996 {
  40997     JSValue obj;
  40998     int i;
  40999 
  41000     obj = JS_NewArray(ctx);
  41001     if (JS_IsException(obj))
  41002         return JS_EXCEPTION;
  41003     for(i = 0; i < len; i++) {
  41004         if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, tab[i]), 0) < 0) {
  41005             JS_FreeValue(ctx, obj);
  41006             return JS_EXCEPTION;
  41007         }
  41008     }
  41009     return obj;
  41010 }
  41011 
  41012 static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val,
  41013                                         int argc, JSValueConst *argv, int magic)
  41014 {
  41015     JSValue enum_obj, arr;
  41016     JSArrayIteratorData *it;
  41017     JSIteratorKindEnum kind;
  41018     int class_id;
  41019 
  41020     kind = magic & 3;
  41021     if (magic & 4) {
  41022         /* string iterator case */
  41023         arr = JS_ToStringCheckObject(ctx, this_val);
  41024         class_id = JS_CLASS_STRING_ITERATOR;
  41025     } else {
  41026         arr = JS_ToObject(ctx, this_val);
  41027         class_id = JS_CLASS_ARRAY_ITERATOR;
  41028     }
  41029     if (JS_IsException(arr))
  41030         goto fail;
  41031     enum_obj = JS_NewObjectClass(ctx, class_id);
  41032     if (JS_IsException(enum_obj))
  41033         goto fail;
  41034     it = js_malloc(ctx, sizeof(*it));
  41035     if (!it)
  41036         goto fail1;
  41037     it->obj = arr;
  41038     it->kind = kind;
  41039     it->idx = 0;
  41040     JS_SetOpaque(enum_obj, it);
  41041     return enum_obj;
  41042  fail1:
  41043     JS_FreeValue(ctx, enum_obj);
  41044  fail:
  41045     JS_FreeValue(ctx, arr);
  41046     return JS_EXCEPTION;
  41047 }
  41048 
  41049 static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val,
  41050                                       int argc, JSValueConst *argv,
  41051                                       BOOL *pdone, int magic)
  41052 {
  41053     JSArrayIteratorData *it;
  41054     uint32_t len, idx;
  41055     JSValue val, obj;
  41056     JSObject *p;
  41057 
  41058     it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_ITERATOR);
  41059     if (!it)
  41060         goto fail1;
  41061     if (JS_IsUndefined(it->obj))
  41062         goto done;
  41063     p = JS_VALUE_GET_OBJ(it->obj);
  41064     if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
  41065         p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
  41066         if (typed_array_is_detached(ctx, p)) {
  41067             JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  41068             goto fail1;
  41069         }
  41070         len = p->u.array.count;
  41071     } else {
  41072         if (js_get_length32(ctx, &len, it->obj)) {
  41073         fail1:
  41074             *pdone = FALSE;
  41075             return JS_EXCEPTION;
  41076         }
  41077     }
  41078     idx = it->idx;
  41079     if (idx >= len) {
  41080         JS_FreeValue(ctx, it->obj);
  41081         it->obj = JS_UNDEFINED;
  41082     done:
  41083         *pdone = TRUE;
  41084         return JS_UNDEFINED;
  41085     }
  41086     it->idx = idx + 1;
  41087     *pdone = FALSE;
  41088     if (it->kind == JS_ITERATOR_KIND_KEY) {
  41089         return JS_NewUint32(ctx, idx);
  41090     } else {
  41091         val = JS_GetPropertyUint32(ctx, it->obj, idx);
  41092         if (JS_IsException(val))
  41093             return JS_EXCEPTION;
  41094         if (it->kind == JS_ITERATOR_KIND_VALUE) {
  41095             return val;
  41096         } else {
  41097             JSValueConst args[2];
  41098             JSValue num;
  41099             num = JS_NewUint32(ctx, idx);
  41100             args[0] = num;
  41101             args[1] = val;
  41102             obj = js_create_array(ctx, 2, args);
  41103             JS_FreeValue(ctx, val);
  41104             JS_FreeValue(ctx, num);
  41105             return obj;
  41106         }
  41107     }
  41108 }
  41109 
  41110 static JSValue js_iterator_proto_iterator(JSContext *ctx, JSValueConst this_val,
  41111                                           int argc, JSValueConst *argv)
  41112 {
  41113     return JS_DupValue(ctx, this_val);
  41114 }
  41115 
  41116 static const JSCFunctionListEntry js_iterator_proto_funcs[] = {
  41117     JS_CFUNC_DEF("[Symbol.iterator]", 0, js_iterator_proto_iterator ),
  41118 };
  41119 
  41120 static const JSCFunctionListEntry js_array_proto_funcs[] = {
  41121     JS_CFUNC_DEF("at", 1, js_array_at ),
  41122     JS_CFUNC_DEF("with", 2, js_array_with ),
  41123     JS_CFUNC_DEF("concat", 1, js_array_concat ),
  41124     JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every ),
  41125     JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some ),
  41126     JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach ),
  41127     JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map ),
  41128     JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter ),
  41129     JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce ),
  41130     JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight ),
  41131     JS_CFUNC_DEF("fill", 1, js_array_fill ),
  41132     JS_CFUNC_MAGIC_DEF("find", 1, js_array_find, ArrayFind ),
  41133     JS_CFUNC_MAGIC_DEF("findIndex", 1, js_array_find, ArrayFindIndex ),
  41134     JS_CFUNC_MAGIC_DEF("findLast", 1, js_array_find, ArrayFindLast ),
  41135     JS_CFUNC_MAGIC_DEF("findLastIndex", 1, js_array_find, ArrayFindLastIndex ),
  41136     JS_CFUNC_DEF("indexOf", 1, js_array_indexOf ),
  41137     JS_CFUNC_DEF("lastIndexOf", 1, js_array_lastIndexOf ),
  41138     JS_CFUNC_DEF("includes", 1, js_array_includes ),
  41139     JS_CFUNC_MAGIC_DEF("join", 1, js_array_join, 0 ),
  41140     JS_CFUNC_DEF("toString", 0, js_array_toString ),
  41141     JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_array_join, 1 ),
  41142     JS_CFUNC_MAGIC_DEF("pop", 0, js_array_pop, 0 ),
  41143     JS_CFUNC_MAGIC_DEF("push", 1, js_array_push, 0 ),
  41144     JS_CFUNC_MAGIC_DEF("shift", 0, js_array_pop, 1 ),
  41145     JS_CFUNC_MAGIC_DEF("unshift", 1, js_array_push, 1 ),
  41146     JS_CFUNC_DEF("reverse", 0, js_array_reverse ),
  41147     JS_CFUNC_DEF("toReversed", 0, js_array_toReversed ),
  41148     JS_CFUNC_DEF("sort", 1, js_array_sort ),
  41149     JS_CFUNC_DEF("toSorted", 1, js_array_toSorted ),
  41150     JS_CFUNC_MAGIC_DEF("slice", 2, js_array_slice, 0 ),
  41151     JS_CFUNC_MAGIC_DEF("splice", 2, js_array_slice, 1 ),
  41152     JS_CFUNC_DEF("toSpliced", 2, js_array_toSpliced ),
  41153     JS_CFUNC_DEF("copyWithin", 2, js_array_copyWithin ),
  41154     JS_CFUNC_MAGIC_DEF("flatMap", 1, js_array_flatten, 1 ),
  41155     JS_CFUNC_MAGIC_DEF("flat", 0, js_array_flatten, 0 ),
  41156     JS_CFUNC_MAGIC_DEF("values", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE ),
  41157     JS_ALIAS_DEF("[Symbol.iterator]", "values" ),
  41158     JS_CFUNC_MAGIC_DEF("keys", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY ),
  41159     JS_CFUNC_MAGIC_DEF("entries", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ),
  41160 };
  41161 
  41162 static const JSCFunctionListEntry js_array_iterator_proto_funcs[] = {
  41163     JS_ITERATOR_NEXT_DEF("next", 0, js_array_iterator_next, 0 ),
  41164     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Array Iterator", JS_PROP_CONFIGURABLE ),
  41165 };
  41166 
  41167 /* Number */
  41168 
  41169 static JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target,
  41170                                      int argc, JSValueConst *argv)
  41171 {
  41172     JSValue val, obj;
  41173     if (argc == 0) {
  41174         val = JS_NewInt32(ctx, 0);
  41175     } else {
  41176         val = JS_ToNumeric(ctx, argv[0]);
  41177         if (JS_IsException(val))
  41178             return val;
  41179         switch(JS_VALUE_GET_TAG(val)) {
  41180         case JS_TAG_BIG_INT:
  41181 #ifdef CONFIG_BIGNUM
  41182         case JS_TAG_BIG_FLOAT:
  41183 #endif
  41184             {
  41185                 JSBigFloat *p = JS_VALUE_GET_PTR(val);
  41186                 double d;
  41187                 bf_get_float64(&p->num, &d, BF_RNDN);
  41188                 JS_FreeValue(ctx, val);
  41189                 val = __JS_NewFloat64(ctx, d);
  41190             }
  41191             break;
  41192 #ifdef CONFIG_BIGNUM
  41193         case JS_TAG_BIG_DECIMAL:
  41194             val = JS_ToStringFree(ctx, val);
  41195             if (JS_IsException(val))
  41196                 return val;
  41197             val = JS_ToNumberFree(ctx, val);
  41198             if (JS_IsException(val))
  41199                 return val;
  41200             break;
  41201 #endif
  41202         default:
  41203             break;
  41204         }
  41205     }
  41206     if (!JS_IsUndefined(new_target)) {
  41207         obj = js_create_from_ctor(ctx, new_target, JS_CLASS_NUMBER);
  41208         if (!JS_IsException(obj))
  41209             JS_SetObjectData(ctx, obj, val);
  41210         return obj;
  41211     } else {
  41212         return val;
  41213     }
  41214 }
  41215 
  41216 #if 0
  41217 static JSValue js_number___toInteger(JSContext *ctx, JSValueConst this_val,
  41218                                      int argc, JSValueConst *argv)
  41219 {
  41220     return JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[0]));
  41221 }
  41222 
  41223 static JSValue js_number___toLength(JSContext *ctx, JSValueConst this_val,
  41224                                     int argc, JSValueConst *argv)
  41225 {
  41226     int64_t v;
  41227     if (JS_ToLengthFree(ctx, &v, JS_DupValue(ctx, argv[0])))
  41228         return JS_EXCEPTION;
  41229     return JS_NewInt64(ctx, v);
  41230 }
  41231 #endif
  41232 
  41233 static JSValue js_number_isNaN(JSContext *ctx, JSValueConst this_val,
  41234                                int argc, JSValueConst *argv)
  41235 {
  41236     if (!JS_IsNumber(argv[0]))
  41237         return JS_FALSE;
  41238     return js_global_isNaN(ctx, this_val, argc, argv);
  41239 }
  41240 
  41241 static JSValue js_number_isFinite(JSContext *ctx, JSValueConst this_val,
  41242                                   int argc, JSValueConst *argv)
  41243 {
  41244     if (!JS_IsNumber(argv[0]))
  41245         return JS_FALSE;
  41246     return js_global_isFinite(ctx, this_val, argc, argv);
  41247 }
  41248 
  41249 static JSValue js_number_isInteger(JSContext *ctx, JSValueConst this_val,
  41250                                    int argc, JSValueConst *argv)
  41251 {
  41252     int ret;
  41253     ret = JS_NumberIsInteger(ctx, argv[0]);
  41254     if (ret < 0)
  41255         return JS_EXCEPTION;
  41256     else
  41257         return JS_NewBool(ctx, ret);
  41258 }
  41259 
  41260 static JSValue js_number_isSafeInteger(JSContext *ctx, JSValueConst this_val,
  41261                                        int argc, JSValueConst *argv)
  41262 {
  41263     double d;
  41264     if (!JS_IsNumber(argv[0]))
  41265         return JS_FALSE;
  41266     if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))
  41267         return JS_EXCEPTION;
  41268     return JS_NewBool(ctx, is_safe_integer(d));
  41269 }
  41270 
  41271 static const JSCFunctionListEntry js_number_funcs[] = {
  41272     /* global ParseInt and parseFloat should be defined already or delayed */
  41273     JS_ALIAS_BASE_DEF("parseInt", "parseInt", 0 ),
  41274     JS_ALIAS_BASE_DEF("parseFloat", "parseFloat", 0 ),
  41275     JS_CFUNC_DEF("isNaN", 1, js_number_isNaN ),
  41276     JS_CFUNC_DEF("isFinite", 1, js_number_isFinite ),
  41277     JS_CFUNC_DEF("isInteger", 1, js_number_isInteger ),
  41278     JS_CFUNC_DEF("isSafeInteger", 1, js_number_isSafeInteger ),
  41279     JS_PROP_DOUBLE_DEF("MAX_VALUE", 1.7976931348623157e+308, 0 ),
  41280     JS_PROP_DOUBLE_DEF("MIN_VALUE", 5e-324, 0 ),
  41281     JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),
  41282     JS_PROP_DOUBLE_DEF("NEGATIVE_INFINITY", -INFINITY, 0 ),
  41283     JS_PROP_DOUBLE_DEF("POSITIVE_INFINITY", INFINITY, 0 ),
  41284     JS_PROP_DOUBLE_DEF("EPSILON", 2.220446049250313e-16, 0 ), /* ES6 */
  41285     JS_PROP_DOUBLE_DEF("MAX_SAFE_INTEGER", 9007199254740991.0, 0 ), /* ES6 */
  41286     JS_PROP_DOUBLE_DEF("MIN_SAFE_INTEGER", -9007199254740991.0, 0 ), /* ES6 */
  41287     //JS_CFUNC_DEF("__toInteger", 1, js_number___toInteger ),
  41288     //JS_CFUNC_DEF("__toLength", 1, js_number___toLength ),
  41289 };
  41290 
  41291 static JSValue js_thisNumberValue(JSContext *ctx, JSValueConst this_val)
  41292 {
  41293     if (JS_IsNumber(this_val))
  41294         return JS_DupValue(ctx, this_val);
  41295 
  41296     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
  41297         JSObject *p = JS_VALUE_GET_OBJ(this_val);
  41298         if (p->class_id == JS_CLASS_NUMBER) {
  41299             if (JS_IsNumber(p->u.object_data))
  41300                 return JS_DupValue(ctx, p->u.object_data);
  41301         }
  41302     }
  41303     return JS_ThrowTypeError(ctx, "not a number");
  41304 }
  41305 
  41306 static JSValue js_number_valueOf(JSContext *ctx, JSValueConst this_val,
  41307                                  int argc, JSValueConst *argv)
  41308 {
  41309     return js_thisNumberValue(ctx, this_val);
  41310 }
  41311 
  41312 static int js_get_radix(JSContext *ctx, JSValueConst val)
  41313 {
  41314     int radix;
  41315     if (JS_ToInt32Sat(ctx, &radix, val))
  41316         return -1;
  41317     if (radix < 2 || radix > 36) {
  41318         JS_ThrowRangeError(ctx, "radix must be between 2 and 36");
  41319         return -1;
  41320     }
  41321     return radix;
  41322 }
  41323 
  41324 static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val,
  41325                                   int argc, JSValueConst *argv, int magic)
  41326 {
  41327     JSValue val;
  41328     int base;
  41329     double d;
  41330 
  41331     val = js_thisNumberValue(ctx, this_val);
  41332     if (JS_IsException(val))
  41333         return val;
  41334     if (magic || JS_IsUndefined(argv[0])) {
  41335         base = 10;
  41336     } else {
  41337         base = js_get_radix(ctx, argv[0]);
  41338         if (base < 0)
  41339             goto fail;
  41340     }
  41341     if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) {
  41342         char buf1[70], *ptr;
  41343         ptr = i64toa(buf1 + sizeof(buf1), JS_VALUE_GET_INT(val), base);
  41344         return JS_NewString(ctx, ptr);
  41345     }
  41346     if (JS_ToFloat64Free(ctx, &d, val))
  41347         return JS_EXCEPTION;
  41348     if (base != 10 && isfinite(d)) {
  41349         return js_dtoa_radix(ctx, d, base);
  41350     }
  41351     return js_dtoa(ctx, d, base, 0, JS_DTOA_VAR_FORMAT);
  41352  fail:
  41353     JS_FreeValue(ctx, val);
  41354     return JS_EXCEPTION;
  41355 }
  41356 
  41357 static JSValue js_number_toFixed(JSContext *ctx, JSValueConst this_val,
  41358                                  int argc, JSValueConst *argv)
  41359 {
  41360     JSValue val;
  41361     int f;
  41362     double d;
  41363 
  41364     val = js_thisNumberValue(ctx, this_val);
  41365     if (JS_IsException(val))
  41366         return val;
  41367     if (JS_ToFloat64Free(ctx, &d, val))
  41368         return JS_EXCEPTION;
  41369     if (JS_ToInt32Sat(ctx, &f, argv[0]))
  41370         return JS_EXCEPTION;
  41371     if (f < 0 || f > 100)
  41372         return JS_ThrowRangeError(ctx, "invalid number of digits");
  41373     if (fabs(d) >= 1e21) {
  41374         return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d));
  41375     } else {
  41376         return js_dtoa(ctx, d, 10, f, JS_DTOA_FRAC_FORMAT);
  41377     }
  41378 }
  41379 
  41380 static JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val,
  41381                                        int argc, JSValueConst *argv)
  41382 {
  41383     JSValue val;
  41384     int f, flags;
  41385     double d;
  41386 
  41387     val = js_thisNumberValue(ctx, this_val);
  41388     if (JS_IsException(val))
  41389         return val;
  41390     if (JS_ToFloat64Free(ctx, &d, val))
  41391         return JS_EXCEPTION;
  41392     if (JS_ToInt32Sat(ctx, &f, argv[0]))
  41393         return JS_EXCEPTION;
  41394     if (!isfinite(d)) {
  41395         return JS_ToStringFree(ctx,  __JS_NewFloat64(ctx, d));
  41396     }
  41397     if (JS_IsUndefined(argv[0])) {
  41398         flags = 0;
  41399         f = 0;
  41400     } else {
  41401         if (f < 0 || f > 100)
  41402             return JS_ThrowRangeError(ctx, "invalid number of digits");
  41403         f++;
  41404         flags = JS_DTOA_FIXED_FORMAT;
  41405     }
  41406     return js_dtoa(ctx, d, 10, f, flags | JS_DTOA_FORCE_EXP);
  41407 }
  41408 
  41409 static JSValue js_number_toPrecision(JSContext *ctx, JSValueConst this_val,
  41410                                      int argc, JSValueConst *argv)
  41411 {
  41412     JSValue val;
  41413     int p;
  41414     double d;
  41415 
  41416     val = js_thisNumberValue(ctx, this_val);
  41417     if (JS_IsException(val))
  41418         return val;
  41419     if (JS_ToFloat64Free(ctx, &d, val))
  41420         return JS_EXCEPTION;
  41421     if (JS_IsUndefined(argv[0]))
  41422         goto to_string;
  41423     if (JS_ToInt32Sat(ctx, &p, argv[0]))
  41424         return JS_EXCEPTION;
  41425     if (!isfinite(d)) {
  41426     to_string:
  41427         return JS_ToStringFree(ctx,  __JS_NewFloat64(ctx, d));
  41428     }
  41429     if (p < 1 || p > 100)
  41430         return JS_ThrowRangeError(ctx, "invalid number of digits");
  41431     return js_dtoa(ctx, d, 10, p, JS_DTOA_FIXED_FORMAT);
  41432 }
  41433 
  41434 static const JSCFunctionListEntry js_number_proto_funcs[] = {
  41435     JS_CFUNC_DEF("toExponential", 1, js_number_toExponential ),
  41436     JS_CFUNC_DEF("toFixed", 1, js_number_toFixed ),
  41437     JS_CFUNC_DEF("toPrecision", 1, js_number_toPrecision ),
  41438     JS_CFUNC_MAGIC_DEF("toString", 1, js_number_toString, 0 ),
  41439     JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_number_toString, 1 ),
  41440     JS_CFUNC_DEF("valueOf", 0, js_number_valueOf ),
  41441 };
  41442 
  41443 static JSValue js_parseInt(JSContext *ctx, JSValueConst this_val,
  41444                            int argc, JSValueConst *argv)
  41445 {
  41446     const char *str, *p;
  41447     int radix, flags;
  41448     JSValue ret;
  41449 
  41450     str = JS_ToCString(ctx, argv[0]);
  41451     if (!str)
  41452         return JS_EXCEPTION;
  41453     if (JS_ToInt32(ctx, &radix, argv[1])) {
  41454         JS_FreeCString(ctx, str);
  41455         return JS_EXCEPTION;
  41456     }
  41457     if (radix != 0 && (radix < 2 || radix > 36)) {
  41458         ret = JS_NAN;
  41459     } else {
  41460         p = str;
  41461         p += skip_spaces(p);
  41462         flags = ATOD_INT_ONLY | ATOD_ACCEPT_PREFIX_AFTER_SIGN;
  41463         ret = js_atof(ctx, p, NULL, radix, flags);
  41464     }
  41465     JS_FreeCString(ctx, str);
  41466     return ret;
  41467 }
  41468 
  41469 static JSValue js_parseFloat(JSContext *ctx, JSValueConst this_val,
  41470                              int argc, JSValueConst *argv)
  41471 {
  41472     const char *str, *p;
  41473     JSValue ret;
  41474 
  41475     str = JS_ToCString(ctx, argv[0]);
  41476     if (!str)
  41477         return JS_EXCEPTION;
  41478     p = str;
  41479     p += skip_spaces(p);
  41480     ret = js_atof(ctx, p, NULL, 10, 0);
  41481     JS_FreeCString(ctx, str);
  41482     return ret;
  41483 }
  41484 
  41485 /* Boolean */
  41486 static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst new_target,
  41487                                      int argc, JSValueConst *argv)
  41488 {
  41489     JSValue val, obj;
  41490     val = JS_NewBool(ctx, JS_ToBool(ctx, argv[0]));
  41491     if (!JS_IsUndefined(new_target)) {
  41492         obj = js_create_from_ctor(ctx, new_target, JS_CLASS_BOOLEAN);
  41493         if (!JS_IsException(obj))
  41494             JS_SetObjectData(ctx, obj, val);
  41495         return obj;
  41496     } else {
  41497         return val;
  41498     }
  41499 }
  41500 
  41501 static JSValue js_thisBooleanValue(JSContext *ctx, JSValueConst this_val)
  41502 {
  41503     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_BOOL)
  41504         return JS_DupValue(ctx, this_val);
  41505 
  41506     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
  41507         JSObject *p = JS_VALUE_GET_OBJ(this_val);
  41508         if (p->class_id == JS_CLASS_BOOLEAN) {
  41509             if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_BOOL)
  41510                 return p->u.object_data;
  41511         }
  41512     }
  41513     return JS_ThrowTypeError(ctx, "not a boolean");
  41514 }
  41515 
  41516 static JSValue js_boolean_toString(JSContext *ctx, JSValueConst this_val,
  41517                                    int argc, JSValueConst *argv)
  41518 {
  41519     JSValue val = js_thisBooleanValue(ctx, this_val);
  41520     if (JS_IsException(val))
  41521         return val;
  41522     return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ?
  41523                        JS_ATOM_true : JS_ATOM_false);
  41524 }
  41525 
  41526 static JSValue js_boolean_valueOf(JSContext *ctx, JSValueConst this_val,
  41527                                   int argc, JSValueConst *argv)
  41528 {
  41529     return js_thisBooleanValue(ctx, this_val);
  41530 }
  41531 
  41532 static const JSCFunctionListEntry js_boolean_proto_funcs[] = {
  41533     JS_CFUNC_DEF("toString", 0, js_boolean_toString ),
  41534     JS_CFUNC_DEF("valueOf", 0, js_boolean_valueOf ),
  41535 };
  41536 
  41537 /* String */
  41538 
  41539 static int js_string_get_own_property(JSContext *ctx,
  41540                                       JSPropertyDescriptor *desc,
  41541                                       JSValueConst obj, JSAtom prop)
  41542 {
  41543     JSObject *p;
  41544     JSString *p1;
  41545     uint32_t idx, ch;
  41546 
  41547     /* This is a class exotic method: obj class_id is JS_CLASS_STRING */
  41548     if (__JS_AtomIsTaggedInt(prop)) {
  41549         p = JS_VALUE_GET_OBJ(obj);
  41550         if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) {
  41551             p1 = JS_VALUE_GET_STRING(p->u.object_data);
  41552             idx = __JS_AtomToUInt32(prop);
  41553             if (idx < p1->len) {
  41554                 if (desc) {
  41555                     ch = string_get(p1, idx);
  41556                     desc->flags = JS_PROP_ENUMERABLE;
  41557                     desc->value = js_new_string_char(ctx, ch);
  41558                     desc->getter = JS_UNDEFINED;
  41559                     desc->setter = JS_UNDEFINED;
  41560                 }
  41561                 return TRUE;
  41562             }
  41563         }
  41564     }
  41565     return FALSE;
  41566 }
  41567 
  41568 static int js_string_define_own_property(JSContext *ctx,
  41569                                          JSValueConst this_obj,
  41570                                          JSAtom prop, JSValueConst val,
  41571                                          JSValueConst getter,
  41572                                          JSValueConst setter, int flags)
  41573 {
  41574     uint32_t idx;
  41575     JSObject *p;
  41576     JSString *p1, *p2;
  41577 
  41578     if (__JS_AtomIsTaggedInt(prop)) {
  41579         idx = __JS_AtomToUInt32(prop);
  41580         p = JS_VALUE_GET_OBJ(this_obj);
  41581         if (JS_VALUE_GET_TAG(p->u.object_data) != JS_TAG_STRING)
  41582             goto def;
  41583         p1 = JS_VALUE_GET_STRING(p->u.object_data);
  41584         if (idx >= p1->len)
  41585             goto def;
  41586         if (!check_define_prop_flags(JS_PROP_ENUMERABLE, flags))
  41587             goto fail;
  41588         /* check that the same value is configured */
  41589         if (flags & JS_PROP_HAS_VALUE) {
  41590             if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)
  41591                 goto fail;
  41592             p2 = JS_VALUE_GET_STRING(val);
  41593             if (p2->len != 1)
  41594                 goto fail;
  41595             if (string_get(p1, idx) != string_get(p2, 0)) {
  41596             fail:
  41597                 return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable");
  41598             }
  41599         }
  41600         return TRUE;
  41601     } else {
  41602     def:
  41603         return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter,
  41604                                  flags | JS_PROP_NO_EXOTIC);
  41605     }
  41606 }
  41607 
  41608 static int js_string_delete_property(JSContext *ctx,
  41609                                      JSValueConst obj, JSAtom prop)
  41610 {
  41611     uint32_t idx;
  41612 
  41613     if (__JS_AtomIsTaggedInt(prop)) {
  41614         idx = __JS_AtomToUInt32(prop);
  41615         if (idx < js_string_obj_get_length(ctx, obj)) {
  41616             return FALSE;
  41617         }
  41618     }
  41619     return TRUE;
  41620 }
  41621 
  41622 static const JSClassExoticMethods js_string_exotic_methods = {
  41623     .get_own_property = js_string_get_own_property,
  41624     .define_own_property = js_string_define_own_property,
  41625     .delete_property = js_string_delete_property,
  41626 };
  41627 
  41628 static JSValue js_string_constructor(JSContext *ctx, JSValueConst new_target,
  41629                                      int argc, JSValueConst *argv)
  41630 {
  41631     JSValue val, obj;
  41632     if (argc == 0) {
  41633         val = JS_AtomToString(ctx, JS_ATOM_empty_string);
  41634     } else {
  41635         if (JS_IsUndefined(new_target) && JS_IsSymbol(argv[0])) {
  41636             JSAtomStruct *p = JS_VALUE_GET_PTR(argv[0]);
  41637             val = JS_ConcatString3(ctx, "Symbol(", JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p)), ")");
  41638         } else {
  41639             val = JS_ToString(ctx, argv[0]);
  41640         }
  41641         if (JS_IsException(val))
  41642             return val;
  41643     }
  41644     if (!JS_IsUndefined(new_target)) {
  41645         JSString *p1 = JS_VALUE_GET_STRING(val);
  41646 
  41647         obj = js_create_from_ctor(ctx, new_target, JS_CLASS_STRING);
  41648         if (!JS_IsException(obj)) {
  41649             JS_SetObjectData(ctx, obj, val);
  41650             JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0);
  41651         }
  41652         return obj;
  41653     } else {
  41654         return val;
  41655     }
  41656 }
  41657 
  41658 static JSValue js_thisStringValue(JSContext *ctx, JSValueConst this_val)
  41659 {
  41660     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_STRING)
  41661         return JS_DupValue(ctx, this_val);
  41662 
  41663     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
  41664         JSObject *p = JS_VALUE_GET_OBJ(this_val);
  41665         if (p->class_id == JS_CLASS_STRING) {
  41666             if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING)
  41667                 return JS_DupValue(ctx, p->u.object_data);
  41668         }
  41669     }
  41670     return JS_ThrowTypeError(ctx, "not a string");
  41671 }
  41672 
  41673 static JSValue js_string_fromCharCode(JSContext *ctx, JSValueConst this_val,
  41674                                       int argc, JSValueConst *argv)
  41675 {
  41676     int i;
  41677     StringBuffer b_s, *b = &b_s;
  41678 
  41679     string_buffer_init(ctx, b, argc);
  41680 
  41681     for(i = 0; i < argc; i++) {
  41682         int32_t c;
  41683         if (JS_ToInt32(ctx, &c, argv[i]) || string_buffer_putc16(b, c & 0xffff)) {
  41684             string_buffer_free(b);
  41685             return JS_EXCEPTION;
  41686         }
  41687     }
  41688     return string_buffer_end(b);
  41689 }
  41690 
  41691 static JSValue js_string_fromCodePoint(JSContext *ctx, JSValueConst this_val,
  41692                                        int argc, JSValueConst *argv)
  41693 {
  41694     double d;
  41695     int i, c;
  41696     StringBuffer b_s, *b = &b_s;
  41697 
  41698     /* XXX: could pre-compute string length if all arguments are JS_TAG_INT */
  41699 
  41700     if (string_buffer_init(ctx, b, argc))
  41701         goto fail;
  41702     for(i = 0; i < argc; i++) {
  41703         if (JS_VALUE_GET_TAG(argv[i]) == JS_TAG_INT) {
  41704             c = JS_VALUE_GET_INT(argv[i]);
  41705             if (c < 0 || c > 0x10ffff)
  41706                 goto range_error;
  41707         } else {
  41708             if (JS_ToFloat64(ctx, &d, argv[i]))
  41709                 goto fail;
  41710             if (isnan(d) || d < 0 || d > 0x10ffff || (c = (int)d) != d)
  41711                 goto range_error;
  41712         }
  41713         if (string_buffer_putc(b, c))
  41714             goto fail;
  41715     }
  41716     return string_buffer_end(b);
  41717 
  41718  range_error:
  41719     JS_ThrowRangeError(ctx, "invalid code point");
  41720  fail:
  41721     string_buffer_free(b);
  41722     return JS_EXCEPTION;
  41723 }
  41724 
  41725 static JSValue js_string_raw(JSContext *ctx, JSValueConst this_val,
  41726                              int argc, JSValueConst *argv)
  41727 {
  41728     // raw(temp,...a)
  41729     JSValue cooked, val, raw;
  41730     StringBuffer b_s, *b = &b_s;
  41731     int64_t i, n;
  41732 
  41733     string_buffer_init(ctx, b, 0);
  41734     raw = JS_UNDEFINED;
  41735     cooked = JS_ToObject(ctx, argv[0]);
  41736     if (JS_IsException(cooked))
  41737         goto exception;
  41738     raw = JS_ToObjectFree(ctx, JS_GetProperty(ctx, cooked, JS_ATOM_raw));
  41739     if (JS_IsException(raw))
  41740         goto exception;
  41741     if (js_get_length64(ctx, &n, raw) < 0)
  41742         goto exception;
  41743 
  41744     for (i = 0; i < n; i++) {
  41745         val = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, raw, i));
  41746         if (JS_IsException(val))
  41747             goto exception;
  41748         string_buffer_concat_value_free(b, val);
  41749         if (i < n - 1 && i + 1 < argc) {
  41750             if (string_buffer_concat_value(b, argv[i + 1]))
  41751                 goto exception;
  41752         }
  41753     }
  41754     JS_FreeValue(ctx, cooked);
  41755     JS_FreeValue(ctx, raw);
  41756     return string_buffer_end(b);
  41757 
  41758 exception:
  41759     JS_FreeValue(ctx, cooked);
  41760     JS_FreeValue(ctx, raw);
  41761     string_buffer_free(b);
  41762     return JS_EXCEPTION;
  41763 }
  41764 
  41765 /* only used in test262 */
  41766 JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val,
  41767                                  int argc, JSValueConst *argv)
  41768 {
  41769     uint32_t start, end, i, n;
  41770     StringBuffer b_s, *b = &b_s;
  41771 
  41772     if (JS_ToUint32(ctx, &start, argv[0]) ||
  41773         JS_ToUint32(ctx, &end, argv[1]))
  41774         return JS_EXCEPTION;
  41775     end = min_uint32(end, 0x10ffff + 1);
  41776 
  41777     if (start > end) {
  41778         start = end;
  41779     }
  41780     n = end - start;
  41781     if (end > 0x10000) {
  41782         n += end - max_uint32(start, 0x10000);
  41783     }
  41784     if (string_buffer_init2(ctx, b, n, end >= 0x100))
  41785         return JS_EXCEPTION;
  41786     for(i = start; i < end; i++) {
  41787         string_buffer_putc(b, i);
  41788     }
  41789     return string_buffer_end(b);
  41790 }
  41791 
  41792 #if 0
  41793 static JSValue js_string___isSpace(JSContext *ctx, JSValueConst this_val,
  41794                                    int argc, JSValueConst *argv)
  41795 {
  41796     int c;
  41797     if (JS_ToInt32(ctx, &c, argv[0]))
  41798         return JS_EXCEPTION;
  41799     return JS_NewBool(ctx, lre_is_space(c));
  41800 }
  41801 #endif
  41802 
  41803 static JSValue js_string_charCodeAt(JSContext *ctx, JSValueConst this_val,
  41804                                      int argc, JSValueConst *argv)
  41805 {
  41806     JSValue val, ret;
  41807     JSString *p;
  41808     int idx, c;
  41809 
  41810     val = JS_ToStringCheckObject(ctx, this_val);
  41811     if (JS_IsException(val))
  41812         return val;
  41813     p = JS_VALUE_GET_STRING(val);
  41814     if (JS_ToInt32Sat(ctx, &idx, argv[0])) {
  41815         JS_FreeValue(ctx, val);
  41816         return JS_EXCEPTION;
  41817     }
  41818     if (idx < 0 || idx >= p->len) {
  41819         ret = JS_NAN;
  41820     } else {
  41821         c = string_get(p, idx);
  41822         ret = JS_NewInt32(ctx, c);
  41823     }
  41824     JS_FreeValue(ctx, val);
  41825     return ret;
  41826 }
  41827 
  41828 static JSValue js_string_charAt(JSContext *ctx, JSValueConst this_val,
  41829                                 int argc, JSValueConst *argv, int is_at)
  41830 {
  41831     JSValue val, ret;
  41832     JSString *p;
  41833     int idx, c;
  41834 
  41835     val = JS_ToStringCheckObject(ctx, this_val);
  41836     if (JS_IsException(val))
  41837         return val;
  41838     p = JS_VALUE_GET_STRING(val);
  41839     if (JS_ToInt32Sat(ctx, &idx, argv[0])) {
  41840         JS_FreeValue(ctx, val);
  41841         return JS_EXCEPTION;
  41842     }
  41843     if (idx < 0 && is_at)
  41844         idx += p->len;
  41845     if (idx < 0 || idx >= p->len) {
  41846         if (is_at)
  41847             ret = JS_UNDEFINED;
  41848         else
  41849             ret = js_new_string8(ctx, NULL, 0);
  41850     } else {
  41851         c = string_get(p, idx);
  41852         ret = js_new_string_char(ctx, c);
  41853     }
  41854     JS_FreeValue(ctx, val);
  41855     return ret;
  41856 }
  41857 
  41858 static JSValue js_string_codePointAt(JSContext *ctx, JSValueConst this_val,
  41859                                      int argc, JSValueConst *argv)
  41860 {
  41861     JSValue val, ret;
  41862     JSString *p;
  41863     int idx, c;
  41864 
  41865     val = JS_ToStringCheckObject(ctx, this_val);
  41866     if (JS_IsException(val))
  41867         return val;
  41868     p = JS_VALUE_GET_STRING(val);
  41869     if (JS_ToInt32Sat(ctx, &idx, argv[0])) {
  41870         JS_FreeValue(ctx, val);
  41871         return JS_EXCEPTION;
  41872     }
  41873     if (idx < 0 || idx >= p->len) {
  41874         ret = JS_UNDEFINED;
  41875     } else {
  41876         c = string_getc(p, &idx);
  41877         ret = JS_NewInt32(ctx, c);
  41878     }
  41879     JS_FreeValue(ctx, val);
  41880     return ret;
  41881 }
  41882 
  41883 static JSValue js_string_concat(JSContext *ctx, JSValueConst this_val,
  41884                                 int argc, JSValueConst *argv)
  41885 {
  41886     JSValue r;
  41887     int i;
  41888 
  41889     /* XXX: Use more efficient method */
  41890     /* XXX: This method is OK if r has a single refcount */
  41891     /* XXX: should use string_buffer? */
  41892     r = JS_ToStringCheckObject(ctx, this_val);
  41893     for (i = 0; i < argc; i++) {
  41894         if (JS_IsException(r))
  41895             break;
  41896         r = JS_ConcatString(ctx, r, JS_DupValue(ctx, argv[i]));
  41897     }
  41898     return r;
  41899 }
  41900 
  41901 static int string_cmp(JSString *p1, JSString *p2, int x1, int x2, int len)
  41902 {
  41903     int i, c1, c2;
  41904     for (i = 0; i < len; i++) {
  41905         if ((c1 = string_get(p1, x1 + i)) != (c2 = string_get(p2, x2 + i)))
  41906             return c1 - c2;
  41907     }
  41908     return 0;
  41909 }
  41910 
  41911 static int string_indexof_char(JSString *p, int c, int from)
  41912 {
  41913     /* assuming 0 <= from <= p->len */
  41914     int i, len = p->len;
  41915     if (p->is_wide_char) {
  41916         for (i = from; i < len; i++) {
  41917             if (p->u.str16[i] == c)
  41918                 return i;
  41919         }
  41920     } else {
  41921         if ((c & ~0xff) == 0) {
  41922             for (i = from; i < len; i++) {
  41923                 if (p->u.str8[i] == (uint8_t)c)
  41924                     return i;
  41925             }
  41926         }
  41927     }
  41928     return -1;
  41929 }
  41930 
  41931 static int string_indexof(JSString *p1, JSString *p2, int from)
  41932 {
  41933     /* assuming 0 <= from <= p1->len */
  41934     int c, i, j, len1 = p1->len, len2 = p2->len;
  41935     if (len2 == 0)
  41936         return from;
  41937     for (i = from, c = string_get(p2, 0); i + len2 <= len1; i = j + 1) {
  41938         j = string_indexof_char(p1, c, i);
  41939         if (j < 0 || j + len2 > len1)
  41940             break;
  41941         if (!string_cmp(p1, p2, j + 1, 1, len2 - 1))
  41942             return j;
  41943     }
  41944     return -1;
  41945 }
  41946 
  41947 static int64_t string_advance_index(JSString *p, int64_t index, BOOL unicode)
  41948 {
  41949     if (!unicode || index >= p->len || !p->is_wide_char) {
  41950         index++;
  41951     } else {
  41952         int index32 = (int)index;
  41953         string_getc(p, &index32);
  41954         index = index32;
  41955     }
  41956     return index;
  41957 }
  41958 
  41959 /* return the position of the first invalid character in the string or
  41960    -1 if none */
  41961 static int js_string_find_invalid_codepoint(JSString *p)
  41962 {
  41963     int i;
  41964     if (!p->is_wide_char)
  41965         return -1;
  41966     for(i = 0; i < p->len; i++) {
  41967         uint32_t c = p->u.str16[i];
  41968         if (is_surrogate(c)) {
  41969             if (is_hi_surrogate(c) && (i + 1) < p->len
  41970             &&  is_lo_surrogate(p->u.str16[i + 1])) {
  41971                 i++;
  41972             } else {
  41973                 return i;
  41974             }
  41975         }
  41976     }
  41977     return -1;
  41978 }
  41979 
  41980 static JSValue js_string_isWellFormed(JSContext *ctx, JSValueConst this_val,
  41981                                       int argc, JSValueConst *argv)
  41982 {
  41983     JSValue str;
  41984     JSString *p;
  41985     BOOL ret;
  41986 
  41987     str = JS_ToStringCheckObject(ctx, this_val);
  41988     if (JS_IsException(str))
  41989         return JS_EXCEPTION;
  41990     p = JS_VALUE_GET_STRING(str);
  41991     ret = (js_string_find_invalid_codepoint(p) < 0);
  41992     JS_FreeValue(ctx, str);
  41993     return JS_NewBool(ctx, ret);
  41994 }
  41995 
  41996 static JSValue js_string_toWellFormed(JSContext *ctx, JSValueConst this_val,
  41997                                       int argc, JSValueConst *argv)
  41998 {
  41999     JSValue str, ret;
  42000     JSString *p;
  42001     int i;
  42002 
  42003     str = JS_ToStringCheckObject(ctx, this_val);
  42004     if (JS_IsException(str))
  42005         return JS_EXCEPTION;
  42006 
  42007     p = JS_VALUE_GET_STRING(str);
  42008     /* avoid reallocating the string if it is well-formed */
  42009     i = js_string_find_invalid_codepoint(p);
  42010     if (i < 0)
  42011         return str;
  42012 
  42013     ret = js_new_string16(ctx, p->u.str16, p->len);
  42014     JS_FreeValue(ctx, str);
  42015     if (JS_IsException(ret))
  42016         return JS_EXCEPTION;
  42017 
  42018     p = JS_VALUE_GET_STRING(ret);
  42019     for (; i < p->len; i++) {
  42020         uint32_t c = p->u.str16[i];
  42021         if (is_surrogate(c)) {
  42022             if (is_hi_surrogate(c) && (i + 1) < p->len
  42023             &&  is_lo_surrogate(p->u.str16[i + 1])) {
  42024                 i++;
  42025             } else {
  42026                 p->u.str16[i] = 0xFFFD;
  42027             }
  42028         }
  42029     }
  42030     return ret;
  42031 }
  42032 
  42033 static JSValue js_string_indexOf(JSContext *ctx, JSValueConst this_val,
  42034                                  int argc, JSValueConst *argv, int lastIndexOf)
  42035 {
  42036     JSValue str, v;
  42037     int i, len, v_len, pos, start, stop, ret, inc;
  42038     JSString *p;
  42039     JSString *p1;
  42040 
  42041     str = JS_ToStringCheckObject(ctx, this_val);
  42042     if (JS_IsException(str))
  42043         return str;
  42044     v = JS_ToString(ctx, argv[0]);
  42045     if (JS_IsException(v))
  42046         goto fail;
  42047     p = JS_VALUE_GET_STRING(str);
  42048     p1 = JS_VALUE_GET_STRING(v);
  42049     len = p->len;
  42050     v_len = p1->len;
  42051     if (lastIndexOf) {
  42052         pos = len - v_len;
  42053         if (argc > 1) {
  42054             double d;
  42055             if (JS_ToFloat64(ctx, &d, argv[1]))
  42056                 goto fail;
  42057             if (!isnan(d)) {
  42058                 if (d <= 0)
  42059                     pos = 0;
  42060                 else if (d < pos)
  42061                     pos = d;
  42062             }
  42063         }
  42064         start = pos;
  42065         stop = 0;
  42066         inc = -1;
  42067     } else {
  42068         pos = 0;
  42069         if (argc > 1) {
  42070             if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0))
  42071                 goto fail;
  42072         }
  42073         start = pos;
  42074         stop = len - v_len;
  42075         inc = 1;
  42076     }
  42077     ret = -1;
  42078     if (len >= v_len && inc * (stop - start) >= 0) {
  42079         for (i = start;; i += inc) {
  42080             if (!string_cmp(p, p1, i, 0, v_len)) {
  42081                 ret = i;
  42082                 break;
  42083             }
  42084             if (i == stop)
  42085                 break;
  42086         }
  42087     }
  42088     JS_FreeValue(ctx, str);
  42089     JS_FreeValue(ctx, v);
  42090     return JS_NewInt32(ctx, ret);
  42091 
  42092 fail:
  42093     JS_FreeValue(ctx, str);
  42094     JS_FreeValue(ctx, v);
  42095     return JS_EXCEPTION;
  42096 }
  42097 
  42098 /* return < 0 if exception or TRUE/FALSE */
  42099 static int js_is_regexp(JSContext *ctx, JSValueConst obj);
  42100 
  42101 static JSValue js_string_includes(JSContext *ctx, JSValueConst this_val,
  42102                                   int argc, JSValueConst *argv, int magic)
  42103 {
  42104     JSValue str, v = JS_UNDEFINED;
  42105     int i, len, v_len, pos, start, stop, ret;
  42106     JSString *p;
  42107     JSString *p1;
  42108 
  42109     str = JS_ToStringCheckObject(ctx, this_val);
  42110     if (JS_IsException(str))
  42111         return str;
  42112     ret = js_is_regexp(ctx, argv[0]);
  42113     if (ret) {
  42114         if (ret > 0)
  42115             JS_ThrowTypeError(ctx, "regexp not supported");
  42116         goto fail;
  42117     }
  42118     v = JS_ToString(ctx, argv[0]);
  42119     if (JS_IsException(v))
  42120         goto fail;
  42121     p = JS_VALUE_GET_STRING(str);
  42122     p1 = JS_VALUE_GET_STRING(v);
  42123     len = p->len;
  42124     v_len = p1->len;
  42125     pos = (magic == 2) ? len : 0;
  42126     if (argc > 1 && !JS_IsUndefined(argv[1])) {
  42127         if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0))
  42128             goto fail;
  42129     }
  42130     len -= v_len;
  42131     ret = 0;
  42132     if (magic == 0) {
  42133         start = pos;
  42134         stop = len;
  42135     } else {
  42136         if (magic == 1) {
  42137             if (pos > len)
  42138                 goto done;
  42139         } else {
  42140             pos -= v_len;
  42141         }
  42142         start = stop = pos;
  42143     }
  42144     if (start >= 0 && start <= stop) {
  42145         for (i = start;; i++) {
  42146             if (!string_cmp(p, p1, i, 0, v_len)) {
  42147                 ret = 1;
  42148                 break;
  42149             }
  42150             if (i == stop)
  42151                 break;
  42152         }
  42153     }
  42154  done:
  42155     JS_FreeValue(ctx, str);
  42156     JS_FreeValue(ctx, v);
  42157     return JS_NewBool(ctx, ret);
  42158 
  42159 fail:
  42160     JS_FreeValue(ctx, str);
  42161     JS_FreeValue(ctx, v);
  42162     return JS_EXCEPTION;
  42163 }
  42164 
  42165 static int check_regexp_g_flag(JSContext *ctx, JSValueConst regexp)
  42166 {
  42167     int ret;
  42168     JSValue flags;
  42169 
  42170     ret = js_is_regexp(ctx, regexp);
  42171     if (ret < 0)
  42172         return -1;
  42173     if (ret) {
  42174         flags = JS_GetProperty(ctx, regexp, JS_ATOM_flags);
  42175         if (JS_IsException(flags))
  42176             return -1;
  42177         if (JS_IsUndefined(flags) || JS_IsNull(flags)) {
  42178             JS_ThrowTypeError(ctx, "cannot convert to object");
  42179             return -1;
  42180         }
  42181         flags = JS_ToStringFree(ctx, flags);
  42182         if (JS_IsException(flags))
  42183             return -1;
  42184         ret = string_indexof_char(JS_VALUE_GET_STRING(flags), 'g', 0);
  42185         JS_FreeValue(ctx, flags);
  42186         if (ret < 0) {
  42187             JS_ThrowTypeError(ctx, "regexp must have the 'g' flag");
  42188             return -1;
  42189         }
  42190     }
  42191     return 0;
  42192 }
  42193 
  42194 static JSValue js_string_match(JSContext *ctx, JSValueConst this_val,
  42195                                int argc, JSValueConst *argv, int atom)
  42196 {
  42197     // match(rx), search(rx), matchAll(rx)
  42198     // atom is JS_ATOM_Symbol_match, JS_ATOM_Symbol_search, or JS_ATOM_Symbol_matchAll
  42199     JSValueConst O = this_val, regexp = argv[0], args[2];
  42200     JSValue matcher, S, rx, result, str;
  42201     int args_len;
  42202 
  42203     if (JS_IsUndefined(O) || JS_IsNull(O))
  42204         return JS_ThrowTypeError(ctx, "cannot convert to object");
  42205 
  42206     if (!JS_IsUndefined(regexp) && !JS_IsNull(regexp)) {
  42207         matcher = JS_GetProperty(ctx, regexp, atom);
  42208         if (JS_IsException(matcher))
  42209             return JS_EXCEPTION;
  42210         if (atom == JS_ATOM_Symbol_matchAll) {
  42211             if (check_regexp_g_flag(ctx, regexp) < 0) {
  42212                 JS_FreeValue(ctx, matcher);
  42213                 return JS_EXCEPTION;
  42214             }
  42215         }
  42216         if (!JS_IsUndefined(matcher) && !JS_IsNull(matcher)) {
  42217             return JS_CallFree(ctx, matcher, regexp, 1, &O);
  42218         }
  42219     }
  42220     S = JS_ToString(ctx, O);
  42221     if (JS_IsException(S))
  42222         return JS_EXCEPTION;
  42223     args_len = 1;
  42224     args[0] = regexp;
  42225     str = JS_UNDEFINED;
  42226     if (atom == JS_ATOM_Symbol_matchAll) {
  42227         str = JS_NewString(ctx, "g");
  42228         if (JS_IsException(str))
  42229             goto fail;
  42230         args[args_len++] = (JSValueConst)str;
  42231     }
  42232     rx = JS_CallConstructor(ctx, ctx->regexp_ctor, args_len, args);
  42233     JS_FreeValue(ctx, str);
  42234     if (JS_IsException(rx)) {
  42235     fail:
  42236         JS_FreeValue(ctx, S);
  42237         return JS_EXCEPTION;
  42238     }
  42239     result = JS_InvokeFree(ctx, rx, atom, 1, (JSValueConst *)&S);
  42240     JS_FreeValue(ctx, S);
  42241     return result;
  42242 }
  42243 
  42244 static JSValue js_string___GetSubstitution(JSContext *ctx, JSValueConst this_val,
  42245                                            int argc, JSValueConst *argv)
  42246 {
  42247     // GetSubstitution(matched, str, position, captures, namedCaptures, rep)
  42248     JSValueConst matched, str, captures, namedCaptures, rep;
  42249     JSValue capture, name, s;
  42250     uint32_t position, len, matched_len, captures_len;
  42251     int i, j, j0, k, k1;
  42252     int c, c1;
  42253     StringBuffer b_s, *b = &b_s;
  42254     JSString *sp, *rp;
  42255 
  42256     matched = argv[0];
  42257     str = argv[1];
  42258     captures = argv[3];
  42259     namedCaptures = argv[4];
  42260     rep = argv[5];
  42261 
  42262     if (!JS_IsString(rep) || !JS_IsString(str))
  42263         return JS_ThrowTypeError(ctx, "not a string");
  42264 
  42265     sp = JS_VALUE_GET_STRING(str);
  42266     rp = JS_VALUE_GET_STRING(rep);
  42267 
  42268     string_buffer_init(ctx, b, 0);
  42269 
  42270     captures_len = 0;
  42271     if (!JS_IsUndefined(captures)) {
  42272         if (js_get_length32(ctx, &captures_len, captures))
  42273             goto exception;
  42274     }
  42275     if (js_get_length32(ctx, &matched_len, matched))
  42276         goto exception;
  42277     if (JS_ToUint32(ctx, &position, argv[2]) < 0)
  42278         goto exception;
  42279 
  42280     len = rp->len;
  42281     i = 0;
  42282     for(;;) {
  42283         j = string_indexof_char(rp, '$', i);
  42284         if (j < 0 || j + 1 >= len)
  42285             break;
  42286         string_buffer_concat(b, rp, i, j);
  42287         j0 = j++;
  42288         c = string_get(rp, j++);
  42289         if (c == '$') {
  42290             string_buffer_putc8(b, '$');
  42291         } else if (c == '&') {
  42292             if (string_buffer_concat_value(b, matched))
  42293                 goto exception;
  42294         } else if (c == '`') {
  42295             string_buffer_concat(b, sp, 0, position);
  42296         } else if (c == '\'') {
  42297             string_buffer_concat(b, sp, position + matched_len, sp->len);
  42298         } else if (c >= '0' && c <= '9') {
  42299             k = c - '0';
  42300             if (j < len) {
  42301                 c1 = string_get(rp, j);
  42302                 if (c1 >= '0' && c1 <= '9') {
  42303                     /* This behavior is specified in ES6 and refined in ECMA 2019 */
  42304                     /* ECMA 2019 does not have the extra test, but
  42305                        Test262 S15.5.4.11_A3_T1..3 require this behavior */
  42306                     k1 = k * 10 + c1 - '0';
  42307                     if (k1 >= 1 && k1 < captures_len) {
  42308                         k = k1;
  42309                         j++;
  42310                     }
  42311                 }
  42312             }
  42313             if (k >= 1 && k < captures_len) {
  42314                 s = JS_GetPropertyInt64(ctx, captures, k);
  42315                 if (JS_IsException(s))
  42316                     goto exception;
  42317                 if (!JS_IsUndefined(s)) {
  42318                     if (string_buffer_concat_value_free(b, s))
  42319                         goto exception;
  42320                 }
  42321             } else {
  42322                 goto norep;
  42323             }
  42324         } else if (c == '<' && !JS_IsUndefined(namedCaptures)) {
  42325             k = string_indexof_char(rp, '>', j);
  42326             if (k < 0)
  42327                 goto norep;
  42328             name = js_sub_string(ctx, rp, j, k);
  42329             if (JS_IsException(name))
  42330                 goto exception;
  42331             capture = JS_GetPropertyValue(ctx, namedCaptures, name);
  42332             if (JS_IsException(capture))
  42333                 goto exception;
  42334             if (!JS_IsUndefined(capture)) {
  42335                 if (string_buffer_concat_value_free(b, capture))
  42336                     goto exception;
  42337             }
  42338             j = k + 1;
  42339         } else {
  42340         norep:
  42341             string_buffer_concat(b, rp, j0, j);
  42342         }
  42343         i = j;
  42344     }
  42345     string_buffer_concat(b, rp, i, rp->len);
  42346     return string_buffer_end(b);
  42347 exception:
  42348     string_buffer_free(b);
  42349     return JS_EXCEPTION;
  42350 }
  42351 
  42352 static JSValue js_string_replace(JSContext *ctx, JSValueConst this_val,
  42353                                  int argc, JSValueConst *argv,
  42354                                  int is_replaceAll)
  42355 {
  42356     // replace(rx, rep)
  42357     JSValueConst O = this_val, searchValue = argv[0], replaceValue = argv[1];
  42358     JSValueConst args[6];
  42359     JSValue str, search_str, replaceValue_str, repl_str;
  42360     JSString *sp, *searchp;
  42361     StringBuffer b_s, *b = &b_s;
  42362     int pos, functionalReplace, endOfLastMatch;
  42363     BOOL is_first;
  42364 
  42365     if (JS_IsUndefined(O) || JS_IsNull(O))
  42366         return JS_ThrowTypeError(ctx, "cannot convert to object");
  42367 
  42368     search_str = JS_UNDEFINED;
  42369     replaceValue_str = JS_UNDEFINED;
  42370     repl_str = JS_UNDEFINED;
  42371 
  42372     if (!JS_IsUndefined(searchValue) && !JS_IsNull(searchValue)) {
  42373         JSValue replacer;
  42374         if (is_replaceAll) {
  42375             if (check_regexp_g_flag(ctx, searchValue) < 0)
  42376                 return JS_EXCEPTION;
  42377         }
  42378         replacer = JS_GetProperty(ctx, searchValue, JS_ATOM_Symbol_replace);
  42379         if (JS_IsException(replacer))
  42380             return JS_EXCEPTION;
  42381         if (!JS_IsUndefined(replacer) && !JS_IsNull(replacer)) {
  42382             args[0] = O;
  42383             args[1] = replaceValue;
  42384             return JS_CallFree(ctx, replacer, searchValue, 2, args);
  42385         }
  42386     }
  42387     string_buffer_init(ctx, b, 0);
  42388 
  42389     str = JS_ToString(ctx, O);
  42390     if (JS_IsException(str))
  42391         goto exception;
  42392     search_str = JS_ToString(ctx, searchValue);
  42393     if (JS_IsException(search_str))
  42394         goto exception;
  42395     functionalReplace = JS_IsFunction(ctx, replaceValue);
  42396     if (!functionalReplace) {
  42397         replaceValue_str = JS_ToString(ctx, replaceValue);
  42398         if (JS_IsException(replaceValue_str))
  42399             goto exception;
  42400     }
  42401 
  42402     sp = JS_VALUE_GET_STRING(str);
  42403     searchp = JS_VALUE_GET_STRING(search_str);
  42404     endOfLastMatch = 0;
  42405     is_first = TRUE;
  42406     for(;;) {
  42407         if (unlikely(searchp->len == 0)) {
  42408             if (is_first)
  42409                 pos = 0;
  42410             else if (endOfLastMatch >= sp->len)
  42411                 pos = -1;
  42412             else
  42413                 pos = endOfLastMatch + 1;
  42414         } else {
  42415             pos = string_indexof(sp, searchp, endOfLastMatch);
  42416         }
  42417         if (pos < 0) {
  42418             if (is_first) {
  42419                 string_buffer_free(b);
  42420                 JS_FreeValue(ctx, search_str);
  42421                 JS_FreeValue(ctx, replaceValue_str);
  42422                 return str;
  42423             } else {
  42424                 break;
  42425             }
  42426         }
  42427         if (functionalReplace) {
  42428             args[0] = search_str;
  42429             args[1] = JS_NewInt32(ctx, pos);
  42430             args[2] = str;
  42431             repl_str = JS_ToStringFree(ctx, JS_Call(ctx, replaceValue, JS_UNDEFINED, 3, args));
  42432         } else {
  42433             args[0] = search_str;
  42434             args[1] = str;
  42435             args[2] = JS_NewInt32(ctx, pos);
  42436             args[3] = JS_UNDEFINED;
  42437             args[4] = JS_UNDEFINED;
  42438             args[5] = replaceValue_str;
  42439             repl_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args);
  42440         }
  42441         if (JS_IsException(repl_str))
  42442             goto exception;
  42443 
  42444         string_buffer_concat(b, sp, endOfLastMatch, pos);
  42445         string_buffer_concat_value_free(b, repl_str);
  42446         endOfLastMatch = pos + searchp->len;
  42447         is_first = FALSE;
  42448         if (!is_replaceAll)
  42449             break;
  42450     }
  42451     string_buffer_concat(b, sp, endOfLastMatch, sp->len);
  42452     JS_FreeValue(ctx, search_str);
  42453     JS_FreeValue(ctx, replaceValue_str);
  42454     JS_FreeValue(ctx, str);
  42455     return string_buffer_end(b);
  42456 
  42457 exception:
  42458     string_buffer_free(b);
  42459     JS_FreeValue(ctx, search_str);
  42460     JS_FreeValue(ctx, replaceValue_str);
  42461     JS_FreeValue(ctx, str);
  42462     return JS_EXCEPTION;
  42463 }
  42464 
  42465 static JSValue js_string_split(JSContext *ctx, JSValueConst this_val,
  42466                                int argc, JSValueConst *argv)
  42467 {
  42468     // split(sep, limit)
  42469     JSValueConst O = this_val, separator = argv[0], limit = argv[1];
  42470     JSValueConst args[2];
  42471     JSValue S, A, R, T;
  42472     uint32_t lim, lengthA;
  42473     int64_t p, q, s, r, e;
  42474     JSString *sp, *rp;
  42475 
  42476     if (JS_IsUndefined(O) || JS_IsNull(O))
  42477         return JS_ThrowTypeError(ctx, "cannot convert to object");
  42478 
  42479     S = JS_UNDEFINED;
  42480     A = JS_UNDEFINED;
  42481     R = JS_UNDEFINED;
  42482 
  42483     if (!JS_IsUndefined(separator) && !JS_IsNull(separator)) {
  42484         JSValue splitter;
  42485         splitter = JS_GetProperty(ctx, separator, JS_ATOM_Symbol_split);
  42486         if (JS_IsException(splitter))
  42487             return JS_EXCEPTION;
  42488         if (!JS_IsUndefined(splitter) && !JS_IsNull(splitter)) {
  42489             args[0] = O;
  42490             args[1] = limit;
  42491             return JS_CallFree(ctx, splitter, separator, 2, args);
  42492         }
  42493     }
  42494     S = JS_ToString(ctx, O);
  42495     if (JS_IsException(S))
  42496         goto exception;
  42497     A = JS_NewArray(ctx);
  42498     if (JS_IsException(A))
  42499         goto exception;
  42500     lengthA = 0;
  42501     if (JS_IsUndefined(limit)) {
  42502         lim = 0xffffffff;
  42503     } else {
  42504         if (JS_ToUint32(ctx, &lim, limit) < 0)
  42505             goto exception;
  42506     }
  42507     sp = JS_VALUE_GET_STRING(S);
  42508     s = sp->len;
  42509     R = JS_ToString(ctx, separator);
  42510     if (JS_IsException(R))
  42511         goto exception;
  42512     rp = JS_VALUE_GET_STRING(R);
  42513     r = rp->len;
  42514     p = 0;
  42515     if (lim == 0)
  42516         goto done;
  42517     if (JS_IsUndefined(separator))
  42518         goto add_tail;
  42519     if (s == 0) {
  42520         if (r != 0)
  42521             goto add_tail;
  42522         goto done;
  42523     }
  42524     q = p;
  42525     for (q = p; (q += !r) <= s - r - !r; q = p = e + r) {
  42526         e = string_indexof(sp, rp, q);
  42527         if (e < 0)
  42528             break;
  42529         T = js_sub_string(ctx, sp, p, e);
  42530         if (JS_IsException(T))
  42531             goto exception;
  42532         if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T, 0) < 0)
  42533             goto exception;
  42534         if (lengthA == lim)
  42535             goto done;
  42536     }
  42537 add_tail:
  42538     T = js_sub_string(ctx, sp, p, s);
  42539     if (JS_IsException(T))
  42540         goto exception;
  42541     if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T,0 ) < 0)
  42542         goto exception;
  42543 done:
  42544     JS_FreeValue(ctx, S);
  42545     JS_FreeValue(ctx, R);
  42546     return A;
  42547 
  42548 exception:
  42549     JS_FreeValue(ctx, A);
  42550     JS_FreeValue(ctx, S);
  42551     JS_FreeValue(ctx, R);
  42552     return JS_EXCEPTION;
  42553 }
  42554 
  42555 static JSValue js_string_substring(JSContext *ctx, JSValueConst this_val,
  42556                                    int argc, JSValueConst *argv)
  42557 {
  42558     JSValue str, ret;
  42559     int a, b, start, end;
  42560     JSString *p;
  42561 
  42562     str = JS_ToStringCheckObject(ctx, this_val);
  42563     if (JS_IsException(str))
  42564         return str;
  42565     p = JS_VALUE_GET_STRING(str);
  42566     if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, p->len, 0)) {
  42567         JS_FreeValue(ctx, str);
  42568         return JS_EXCEPTION;
  42569     }
  42570     b = p->len;
  42571     if (!JS_IsUndefined(argv[1])) {
  42572         if (JS_ToInt32Clamp(ctx, &b, argv[1], 0, p->len, 0)) {
  42573             JS_FreeValue(ctx, str);
  42574             return JS_EXCEPTION;
  42575         }
  42576     }
  42577     if (a < b) {
  42578         start = a;
  42579         end = b;
  42580     } else {
  42581         start = b;
  42582         end = a;
  42583     }
  42584     ret = js_sub_string(ctx, p, start, end);
  42585     JS_FreeValue(ctx, str);
  42586     return ret;
  42587 }
  42588 
  42589 static JSValue js_string_substr(JSContext *ctx, JSValueConst this_val,
  42590                                 int argc, JSValueConst *argv)
  42591 {
  42592     JSValue str, ret;
  42593     int a, len, n;
  42594     JSString *p;
  42595 
  42596     str = JS_ToStringCheckObject(ctx, this_val);
  42597     if (JS_IsException(str))
  42598         return str;
  42599     p = JS_VALUE_GET_STRING(str);
  42600     len = p->len;
  42601     if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, len, len)) {
  42602         JS_FreeValue(ctx, str);
  42603         return JS_EXCEPTION;
  42604     }
  42605     n = len - a;
  42606     if (!JS_IsUndefined(argv[1])) {
  42607         if (JS_ToInt32Clamp(ctx, &n, argv[1], 0, len - a, 0)) {
  42608             JS_FreeValue(ctx, str);
  42609             return JS_EXCEPTION;
  42610         }
  42611     }
  42612     ret = js_sub_string(ctx, p, a, a + n);
  42613     JS_FreeValue(ctx, str);
  42614     return ret;
  42615 }
  42616 
  42617 static JSValue js_string_slice(JSContext *ctx, JSValueConst this_val,
  42618                                int argc, JSValueConst *argv)
  42619 {
  42620     JSValue str, ret;
  42621     int len, start, end;
  42622     JSString *p;
  42623 
  42624     str = JS_ToStringCheckObject(ctx, this_val);
  42625     if (JS_IsException(str))
  42626         return str;
  42627     p = JS_VALUE_GET_STRING(str);
  42628     len = p->len;
  42629     if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) {
  42630         JS_FreeValue(ctx, str);
  42631         return JS_EXCEPTION;
  42632     }
  42633     end = len;
  42634     if (!JS_IsUndefined(argv[1])) {
  42635         if (JS_ToInt32Clamp(ctx, &end, argv[1], 0, len, len)) {
  42636             JS_FreeValue(ctx, str);
  42637             return JS_EXCEPTION;
  42638         }
  42639     }
  42640     ret = js_sub_string(ctx, p, start, max_int(end, start));
  42641     JS_FreeValue(ctx, str);
  42642     return ret;
  42643 }
  42644 
  42645 static JSValue js_string_pad(JSContext *ctx, JSValueConst this_val,
  42646                              int argc, JSValueConst *argv, int padEnd)
  42647 {
  42648     JSValue str, v = JS_UNDEFINED;
  42649     StringBuffer b_s, *b = &b_s;
  42650     JSString *p, *p1 = NULL;
  42651     int n, len, c = ' ';
  42652 
  42653     str = JS_ToStringCheckObject(ctx, this_val);
  42654     if (JS_IsException(str))
  42655         goto fail1;
  42656     if (JS_ToInt32Sat(ctx, &n, argv[0]))
  42657         goto fail2;
  42658     p = JS_VALUE_GET_STRING(str);
  42659     len = p->len;
  42660     if (len >= n)
  42661         return str;
  42662     if (argc > 1 && !JS_IsUndefined(argv[1])) {
  42663         v = JS_ToString(ctx, argv[1]);
  42664         if (JS_IsException(v))
  42665             goto fail2;
  42666         p1 = JS_VALUE_GET_STRING(v);
  42667         if (p1->len == 0) {
  42668             JS_FreeValue(ctx, v);
  42669             return str;
  42670         }
  42671         if (p1->len == 1) {
  42672             c = string_get(p1, 0);
  42673             p1 = NULL;
  42674         }
  42675     }
  42676     if (n > JS_STRING_LEN_MAX) {
  42677         JS_ThrowRangeError(ctx, "invalid string length");
  42678         goto fail2;
  42679     }
  42680     if (string_buffer_init(ctx, b, n))
  42681         goto fail3;
  42682     n -= len;
  42683     if (padEnd) {
  42684         if (string_buffer_concat(b, p, 0, len))
  42685             goto fail;
  42686     }
  42687     if (p1) {
  42688         while (n > 0) {
  42689             int chunk = min_int(n, p1->len);
  42690             if (string_buffer_concat(b, p1, 0, chunk))
  42691                 goto fail;
  42692             n -= chunk;
  42693         }
  42694     } else {
  42695         if (string_buffer_fill(b, c, n))
  42696             goto fail;
  42697     }
  42698     if (!padEnd) {
  42699         if (string_buffer_concat(b, p, 0, len))
  42700             goto fail;
  42701     }
  42702     JS_FreeValue(ctx, v);
  42703     JS_FreeValue(ctx, str);
  42704     return string_buffer_end(b);
  42705 
  42706 fail:
  42707     string_buffer_free(b);
  42708 fail3:
  42709     JS_FreeValue(ctx, v);
  42710 fail2:
  42711     JS_FreeValue(ctx, str);
  42712 fail1:
  42713     return JS_EXCEPTION;
  42714 }
  42715 
  42716 static JSValue js_string_repeat(JSContext *ctx, JSValueConst this_val,
  42717                                 int argc, JSValueConst *argv)
  42718 {
  42719     JSValue str;
  42720     StringBuffer b_s, *b = &b_s;
  42721     JSString *p;
  42722     int64_t val;
  42723     int n, len;
  42724 
  42725     str = JS_ToStringCheckObject(ctx, this_val);
  42726     if (JS_IsException(str))
  42727         goto fail;
  42728     if (JS_ToInt64Sat(ctx, &val, argv[0]))
  42729         goto fail;
  42730     if (val < 0 || val > 2147483647) {
  42731         JS_ThrowRangeError(ctx, "invalid repeat count");
  42732         goto fail;
  42733     }
  42734     n = val;
  42735     p = JS_VALUE_GET_STRING(str);
  42736     len = p->len;
  42737     if (len == 0 || n == 1)
  42738         return str;
  42739     // XXX: potential arithmetic overflow
  42740     if (val * len > JS_STRING_LEN_MAX) {
  42741         JS_ThrowRangeError(ctx, "invalid string length");
  42742         goto fail;
  42743     }
  42744     if (string_buffer_init2(ctx, b, n * len, p->is_wide_char))
  42745         goto fail;
  42746     if (len == 1) {
  42747         string_buffer_fill(b, string_get(p, 0), n);
  42748     } else {
  42749         while (n-- > 0) {
  42750             string_buffer_concat(b, p, 0, len);
  42751         }
  42752     }
  42753     JS_FreeValue(ctx, str);
  42754     return string_buffer_end(b);
  42755 
  42756 fail:
  42757     JS_FreeValue(ctx, str);
  42758     return JS_EXCEPTION;
  42759 }
  42760 
  42761 static JSValue js_string_trim(JSContext *ctx, JSValueConst this_val,
  42762                               int argc, JSValueConst *argv, int magic)
  42763 {
  42764     JSValue str, ret;
  42765     int a, b, len;
  42766     JSString *p;
  42767 
  42768     str = JS_ToStringCheckObject(ctx, this_val);
  42769     if (JS_IsException(str))
  42770         return str;
  42771     p = JS_VALUE_GET_STRING(str);
  42772     a = 0;
  42773     b = len = p->len;
  42774     if (magic & 1) {
  42775         while (a < len && lre_is_space(string_get(p, a)))
  42776             a++;
  42777     }
  42778     if (magic & 2) {
  42779         while (b > a && lre_is_space(string_get(p, b - 1)))
  42780             b--;
  42781     }
  42782     ret = js_sub_string(ctx, p, a, b);
  42783     JS_FreeValue(ctx, str);
  42784     return ret;
  42785 }
  42786 
  42787 static JSValue js_string___quote(JSContext *ctx, JSValueConst this_val,
  42788                                  int argc, JSValueConst *argv)
  42789 {
  42790     return JS_ToQuotedString(ctx, this_val);
  42791 }
  42792 
  42793 /* return 0 if before the first char */
  42794 static int string_prevc(JSString *p, int *pidx)
  42795 {
  42796     int idx, c, c1;
  42797 
  42798     idx = *pidx;
  42799     if (idx <= 0)
  42800         return 0;
  42801     idx--;
  42802     if (p->is_wide_char) {
  42803         c = p->u.str16[idx];
  42804         if (is_lo_surrogate(c) && idx > 0) {
  42805             c1 = p->u.str16[idx - 1];
  42806             if (is_hi_surrogate(c1)) {
  42807                 c = from_surrogate(c1, c);
  42808                 idx--;
  42809             }
  42810         }
  42811     } else {
  42812         c = p->u.str8[idx];
  42813     }
  42814     *pidx = idx;
  42815     return c;
  42816 }
  42817 
  42818 static BOOL test_final_sigma(JSString *p, int sigma_pos)
  42819 {
  42820     int k, c1;
  42821 
  42822     /* before C: skip case ignorable chars and check there is
  42823        a cased letter */
  42824     k = sigma_pos;
  42825     for(;;) {
  42826         c1 = string_prevc(p, &k);
  42827         if (!lre_is_case_ignorable(c1))
  42828             break;
  42829     }
  42830     if (!lre_is_cased(c1))
  42831         return FALSE;
  42832 
  42833     /* after C: skip case ignorable chars and check there is
  42834        no cased letter */
  42835     k = sigma_pos + 1;
  42836     for(;;) {
  42837         if (k >= p->len)
  42838             return TRUE;
  42839         c1 = string_getc(p, &k);
  42840         if (!lre_is_case_ignorable(c1))
  42841             break;
  42842     }
  42843     return !lre_is_cased(c1);
  42844 }
  42845 
  42846 static JSValue js_string_toLowerCase(JSContext *ctx, JSValueConst this_val,
  42847                                      int argc, JSValueConst *argv, int to_lower)
  42848 {
  42849     JSValue val;
  42850     StringBuffer b_s, *b = &b_s;
  42851     JSString *p;
  42852     int i, c, j, l;
  42853     uint32_t res[LRE_CC_RES_LEN_MAX];
  42854 
  42855     val = JS_ToStringCheckObject(ctx, this_val);
  42856     if (JS_IsException(val))
  42857         return val;
  42858     p = JS_VALUE_GET_STRING(val);
  42859     if (p->len == 0)
  42860         return val;
  42861     if (string_buffer_init(ctx, b, p->len))
  42862         goto fail;
  42863     for(i = 0; i < p->len;) {
  42864         c = string_getc(p, &i);
  42865         if (c == 0x3a3 && to_lower && test_final_sigma(p, i - 1)) {
  42866             res[0] = 0x3c2; /* final sigma */
  42867             l = 1;
  42868         } else {
  42869             l = lre_case_conv(res, c, to_lower);
  42870         }
  42871         for(j = 0; j < l; j++) {
  42872             if (string_buffer_putc(b, res[j]))
  42873                 goto fail;
  42874         }
  42875     }
  42876     JS_FreeValue(ctx, val);
  42877     return string_buffer_end(b);
  42878  fail:
  42879     JS_FreeValue(ctx, val);
  42880     string_buffer_free(b);
  42881     return JS_EXCEPTION;
  42882 }
  42883 
  42884 #ifdef CONFIG_ALL_UNICODE
  42885 
  42886 /* return (-1, NULL) if exception, otherwise (len, buf) */
  42887 static int JS_ToUTF32String(JSContext *ctx, uint32_t **pbuf, JSValueConst val1)
  42888 {
  42889     JSValue val;
  42890     JSString *p;
  42891     uint32_t *buf;
  42892     int i, j, len;
  42893 
  42894     val = JS_ToString(ctx, val1);
  42895     if (JS_IsException(val))
  42896         return -1;
  42897     p = JS_VALUE_GET_STRING(val);
  42898     len = p->len;
  42899     /* UTF32 buffer length is len minus the number of correct surrogates pairs */
  42900     buf = js_malloc(ctx, sizeof(buf[0]) * max_int(len, 1));
  42901     if (!buf) {
  42902         JS_FreeValue(ctx, val);
  42903         goto fail;
  42904     }
  42905     for(i = j = 0; i < len;)
  42906         buf[j++] = string_getc(p, &i);
  42907     JS_FreeValue(ctx, val);
  42908     *pbuf = buf;
  42909     return j;
  42910  fail:
  42911     *pbuf = NULL;
  42912     return -1;
  42913 }
  42914 
  42915 static JSValue JS_NewUTF32String(JSContext *ctx, const uint32_t *buf, int len)
  42916 {
  42917     int i;
  42918     StringBuffer b_s, *b = &b_s;
  42919     if (string_buffer_init(ctx, b, len))
  42920         return JS_EXCEPTION;
  42921     for(i = 0; i < len; i++) {
  42922         if (string_buffer_putc(b, buf[i]))
  42923             goto fail;
  42924     }
  42925     return string_buffer_end(b);
  42926  fail:
  42927     string_buffer_free(b);
  42928     return JS_EXCEPTION;
  42929 }
  42930 
  42931 static int js_string_normalize1(JSContext *ctx, uint32_t **pout_buf,
  42932                                 JSValueConst val,
  42933                                 UnicodeNormalizationEnum n_type)
  42934 {
  42935     int buf_len, out_len;
  42936     uint32_t *buf, *out_buf;
  42937 
  42938     buf_len = JS_ToUTF32String(ctx, &buf, val);
  42939     if (buf_len < 0)
  42940         return -1;
  42941     out_len = unicode_normalize(&out_buf, buf, buf_len, n_type,
  42942                                 ctx->rt, (DynBufReallocFunc *)js_realloc_rt);
  42943     js_free(ctx, buf);
  42944     if (out_len < 0)
  42945         return -1;
  42946     *pout_buf = out_buf;
  42947     return out_len;
  42948 }
  42949 
  42950 static JSValue js_string_normalize(JSContext *ctx, JSValueConst this_val,
  42951                                    int argc, JSValueConst *argv)
  42952 {
  42953     const char *form, *p;
  42954     size_t form_len;
  42955     int is_compat, out_len;
  42956     UnicodeNormalizationEnum n_type;
  42957     JSValue val;
  42958     uint32_t *out_buf;
  42959 
  42960     val = JS_ToStringCheckObject(ctx, this_val);
  42961     if (JS_IsException(val))
  42962         return val;
  42963 
  42964     if (argc == 0 || JS_IsUndefined(argv[0])) {
  42965         n_type = UNICODE_NFC;
  42966     } else {
  42967         form = JS_ToCStringLen(ctx, &form_len, argv[0]);
  42968         if (!form)
  42969             goto fail1;
  42970         p = form;
  42971         if (p[0] != 'N' || p[1] != 'F')
  42972             goto bad_form;
  42973         p += 2;
  42974         is_compat = FALSE;
  42975         if (*p == 'K') {
  42976             is_compat = TRUE;
  42977             p++;
  42978         }
  42979         if (*p == 'C' || *p == 'D') {
  42980             n_type = UNICODE_NFC + is_compat * 2 + (*p - 'C');
  42981             if ((p + 1 - form) != form_len)
  42982                 goto bad_form;
  42983         } else {
  42984         bad_form:
  42985             JS_FreeCString(ctx, form);
  42986             JS_ThrowRangeError(ctx, "bad normalization form");
  42987         fail1:
  42988             JS_FreeValue(ctx, val);
  42989             return JS_EXCEPTION;
  42990         }
  42991         JS_FreeCString(ctx, form);
  42992     }
  42993 
  42994     out_len = js_string_normalize1(ctx, &out_buf, val, n_type);
  42995     JS_FreeValue(ctx, val);
  42996     if (out_len < 0)
  42997         return JS_EXCEPTION;
  42998     val = JS_NewUTF32String(ctx, out_buf, out_len);
  42999     js_free(ctx, out_buf);
  43000     return val;
  43001 }
  43002 
  43003 /* return < 0, 0 or > 0 */
  43004 static int js_UTF32_compare(const uint32_t *buf1, int buf1_len,
  43005                             const uint32_t *buf2, int buf2_len)
  43006 {
  43007     int i, len, c, res;
  43008     len = min_int(buf1_len, buf2_len);
  43009     for(i = 0; i < len; i++) {
  43010         /* Note: range is limited so a subtraction is valid */
  43011         c = buf1[i] - buf2[i];
  43012         if (c != 0)
  43013             return c;
  43014     }
  43015     if (buf1_len == buf2_len)
  43016         res = 0;
  43017     else if (buf1_len < buf2_len)
  43018         res = -1;
  43019     else
  43020         res = 1;
  43021     return res;
  43022 }
  43023 
  43024 static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val,
  43025                                        int argc, JSValueConst *argv)
  43026 {
  43027     JSValue a, b;
  43028     int cmp, a_len, b_len;
  43029     uint32_t *a_buf, *b_buf;
  43030 
  43031     a = JS_ToStringCheckObject(ctx, this_val);
  43032     if (JS_IsException(a))
  43033         return JS_EXCEPTION;
  43034     b = JS_ToString(ctx, argv[0]);
  43035     if (JS_IsException(b)) {
  43036         JS_FreeValue(ctx, a);
  43037         return JS_EXCEPTION;
  43038     }
  43039     a_len = js_string_normalize1(ctx, &a_buf, a, UNICODE_NFC);
  43040     JS_FreeValue(ctx, a);
  43041     if (a_len < 0) {
  43042         JS_FreeValue(ctx, b);
  43043         return JS_EXCEPTION;
  43044     }
  43045 
  43046     b_len = js_string_normalize1(ctx, &b_buf, b, UNICODE_NFC);
  43047     JS_FreeValue(ctx, b);
  43048     if (b_len < 0) {
  43049         js_free(ctx, a_buf);
  43050         return JS_EXCEPTION;
  43051     }
  43052     cmp = js_UTF32_compare(a_buf, a_len, b_buf, b_len);
  43053     js_free(ctx, a_buf);
  43054     js_free(ctx, b_buf);
  43055     return JS_NewInt32(ctx, cmp);
  43056 }
  43057 #else /* CONFIG_ALL_UNICODE */
  43058 static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val,
  43059                                        int argc, JSValueConst *argv)
  43060 {
  43061     JSValue a, b;
  43062     int cmp;
  43063 
  43064     a = JS_ToStringCheckObject(ctx, this_val);
  43065     if (JS_IsException(a))
  43066         return JS_EXCEPTION;
  43067     b = JS_ToString(ctx, argv[0]);
  43068     if (JS_IsException(b)) {
  43069         JS_FreeValue(ctx, a);
  43070         return JS_EXCEPTION;
  43071     }
  43072     cmp = js_string_compare(ctx, JS_VALUE_GET_STRING(a), JS_VALUE_GET_STRING(b));
  43073     JS_FreeValue(ctx, a);
  43074     JS_FreeValue(ctx, b);
  43075     return JS_NewInt32(ctx, cmp);
  43076 }
  43077 #endif /* !CONFIG_ALL_UNICODE */
  43078 
  43079 /* also used for String.prototype.valueOf */
  43080 static JSValue js_string_toString(JSContext *ctx, JSValueConst this_val,
  43081                                   int argc, JSValueConst *argv)
  43082 {
  43083     return js_thisStringValue(ctx, this_val);
  43084 }
  43085 
  43086 #if 0
  43087 static JSValue js_string___toStringCheckObject(JSContext *ctx, JSValueConst this_val,
  43088                                                int argc, JSValueConst *argv)
  43089 {
  43090     return JS_ToStringCheckObject(ctx, argv[0]);
  43091 }
  43092 
  43093 static JSValue js_string___toString(JSContext *ctx, JSValueConst this_val,
  43094                                     int argc, JSValueConst *argv)
  43095 {
  43096     return JS_ToString(ctx, argv[0]);
  43097 }
  43098 
  43099 static JSValue js_string___advanceStringIndex(JSContext *ctx, JSValueConst
  43100                                               this_val,
  43101                                               int argc, JSValueConst *argv)
  43102 {
  43103     JSValue str;
  43104     int idx;
  43105     BOOL is_unicode;
  43106     JSString *p;
  43107 
  43108     str = JS_ToString(ctx, argv[0]);
  43109     if (JS_IsException(str))
  43110         return str;
  43111     if (JS_ToInt32Sat(ctx, &idx, argv[1])) {
  43112         JS_FreeValue(ctx, str);
  43113         return JS_EXCEPTION;
  43114     }
  43115     is_unicode = JS_ToBool(ctx, argv[2]);
  43116     p = JS_VALUE_GET_STRING(str);
  43117     if (!is_unicode || (unsigned)idx >= p->len || !p->is_wide_char) {
  43118         idx++;
  43119     } else {
  43120         string_getc(p, &idx);
  43121     }
  43122     JS_FreeValue(ctx, str);
  43123     return JS_NewInt32(ctx, idx);
  43124 }
  43125 #endif
  43126 
  43127 /* String Iterator */
  43128 
  43129 static JSValue js_string_iterator_next(JSContext *ctx, JSValueConst this_val,
  43130                                        int argc, JSValueConst *argv,
  43131                                        BOOL *pdone, int magic)
  43132 {
  43133     JSArrayIteratorData *it;
  43134     uint32_t idx, c, start;
  43135     JSString *p;
  43136 
  43137     it = JS_GetOpaque2(ctx, this_val, JS_CLASS_STRING_ITERATOR);
  43138     if (!it) {
  43139         *pdone = FALSE;
  43140         return JS_EXCEPTION;
  43141     }
  43142     if (JS_IsUndefined(it->obj))
  43143         goto done;
  43144     p = JS_VALUE_GET_STRING(it->obj);
  43145     idx = it->idx;
  43146     if (idx >= p->len) {
  43147         JS_FreeValue(ctx, it->obj);
  43148         it->obj = JS_UNDEFINED;
  43149     done:
  43150         *pdone = TRUE;
  43151         return JS_UNDEFINED;
  43152     }
  43153 
  43154     start = idx;
  43155     c = string_getc(p, (int *)&idx);
  43156     it->idx = idx;
  43157     *pdone = FALSE;
  43158     if (c <= 0xffff) {
  43159         return js_new_string_char(ctx, c);
  43160     } else {
  43161         return js_new_string16(ctx, p->u.str16 + start, 2);
  43162     }
  43163 }
  43164 
  43165 /* ES6 Annex B 2.3.2 etc. */
  43166 enum {
  43167     magic_string_anchor,
  43168     magic_string_big,
  43169     magic_string_blink,
  43170     magic_string_bold,
  43171     magic_string_fixed,
  43172     magic_string_fontcolor,
  43173     magic_string_fontsize,
  43174     magic_string_italics,
  43175     magic_string_link,
  43176     magic_string_small,
  43177     magic_string_strike,
  43178     magic_string_sub,
  43179     magic_string_sup,
  43180 };
  43181 
  43182 static JSValue js_string_CreateHTML(JSContext *ctx, JSValueConst this_val,
  43183                                     int argc, JSValueConst *argv, int magic)
  43184 {
  43185     JSValue str;
  43186     const JSString *p;
  43187     StringBuffer b_s, *b = &b_s;
  43188     static struct { const char *tag, *attr; } const defs[] = {
  43189         { "a", "name" }, { "big", NULL }, { "blink", NULL }, { "b", NULL },
  43190         { "tt", NULL }, { "font", "color" }, { "font", "size" }, { "i", NULL },
  43191         { "a", "href" }, { "small", NULL }, { "strike", NULL },
  43192         { "sub", NULL }, { "sup", NULL },
  43193     };
  43194 
  43195     str = JS_ToStringCheckObject(ctx, this_val);
  43196     if (JS_IsException(str))
  43197         return JS_EXCEPTION;
  43198     string_buffer_init(ctx, b, 7);
  43199     string_buffer_putc8(b, '<');
  43200     string_buffer_puts8(b, defs[magic].tag);
  43201     if (defs[magic].attr) {
  43202         // r += " " + attr + "=\"" + value + "\"";
  43203         JSValue value;
  43204         int i;
  43205 
  43206         string_buffer_putc8(b, ' ');
  43207         string_buffer_puts8(b, defs[magic].attr);
  43208         string_buffer_puts8(b, "=\"");
  43209         value = JS_ToStringCheckObject(ctx, argv[0]);
  43210         if (JS_IsException(value)) {
  43211             JS_FreeValue(ctx, str);
  43212             string_buffer_free(b);
  43213             return JS_EXCEPTION;
  43214         }
  43215         p = JS_VALUE_GET_STRING(value);
  43216         for (i = 0; i < p->len; i++) {
  43217             int c = string_get(p, i);
  43218             if (c == '"') {
  43219                 string_buffer_puts8(b, "&quot;");
  43220             } else {
  43221                 string_buffer_putc16(b, c);
  43222             }
  43223         }
  43224         JS_FreeValue(ctx, value);
  43225         string_buffer_putc8(b, '\"');
  43226     }
  43227     // return r + ">" + str + "</" + tag + ">";
  43228     string_buffer_putc8(b, '>');
  43229     string_buffer_concat_value_free(b, str);
  43230     string_buffer_puts8(b, "</");
  43231     string_buffer_puts8(b, defs[magic].tag);
  43232     string_buffer_putc8(b, '>');
  43233     return string_buffer_end(b);
  43234 }
  43235 
  43236 static const JSCFunctionListEntry js_string_funcs[] = {
  43237     JS_CFUNC_DEF("fromCharCode", 1, js_string_fromCharCode ),
  43238     JS_CFUNC_DEF("fromCodePoint", 1, js_string_fromCodePoint ),
  43239     JS_CFUNC_DEF("raw", 1, js_string_raw ),
  43240     //JS_CFUNC_DEF("__toString", 1, js_string___toString ),
  43241     //JS_CFUNC_DEF("__isSpace", 1, js_string___isSpace ),
  43242     //JS_CFUNC_DEF("__toStringCheckObject", 1, js_string___toStringCheckObject ),
  43243     //JS_CFUNC_DEF("__advanceStringIndex", 3, js_string___advanceStringIndex ),
  43244     //JS_CFUNC_DEF("__GetSubstitution", 6, js_string___GetSubstitution ),
  43245 };
  43246 
  43247 static const JSCFunctionListEntry js_string_proto_funcs[] = {
  43248     JS_PROP_INT32_DEF("length", 0, JS_PROP_CONFIGURABLE ),
  43249     JS_CFUNC_MAGIC_DEF("at", 1, js_string_charAt, 1 ),
  43250     JS_CFUNC_DEF("charCodeAt", 1, js_string_charCodeAt ),
  43251     JS_CFUNC_MAGIC_DEF("charAt", 1, js_string_charAt, 0 ),
  43252     JS_CFUNC_DEF("concat", 1, js_string_concat ),
  43253     JS_CFUNC_DEF("codePointAt", 1, js_string_codePointAt ),
  43254     JS_CFUNC_DEF("isWellFormed", 0, js_string_isWellFormed ),
  43255     JS_CFUNC_DEF("toWellFormed", 0, js_string_toWellFormed ),
  43256     JS_CFUNC_MAGIC_DEF("indexOf", 1, js_string_indexOf, 0 ),
  43257     JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_string_indexOf, 1 ),
  43258     JS_CFUNC_MAGIC_DEF("includes", 1, js_string_includes, 0 ),
  43259     JS_CFUNC_MAGIC_DEF("endsWith", 1, js_string_includes, 2 ),
  43260     JS_CFUNC_MAGIC_DEF("startsWith", 1, js_string_includes, 1 ),
  43261     JS_CFUNC_MAGIC_DEF("match", 1, js_string_match, JS_ATOM_Symbol_match ),
  43262     JS_CFUNC_MAGIC_DEF("matchAll", 1, js_string_match, JS_ATOM_Symbol_matchAll ),
  43263     JS_CFUNC_MAGIC_DEF("search", 1, js_string_match, JS_ATOM_Symbol_search ),
  43264     JS_CFUNC_DEF("split", 2, js_string_split ),
  43265     JS_CFUNC_DEF("substring", 2, js_string_substring ),
  43266     JS_CFUNC_DEF("substr", 2, js_string_substr ),
  43267     JS_CFUNC_DEF("slice", 2, js_string_slice ),
  43268     JS_CFUNC_DEF("repeat", 1, js_string_repeat ),
  43269     JS_CFUNC_MAGIC_DEF("replace", 2, js_string_replace, 0 ),
  43270     JS_CFUNC_MAGIC_DEF("replaceAll", 2, js_string_replace, 1 ),
  43271     JS_CFUNC_MAGIC_DEF("padEnd", 1, js_string_pad, 1 ),
  43272     JS_CFUNC_MAGIC_DEF("padStart", 1, js_string_pad, 0 ),
  43273     JS_CFUNC_MAGIC_DEF("trim", 0, js_string_trim, 3 ),
  43274     JS_CFUNC_MAGIC_DEF("trimEnd", 0, js_string_trim, 2 ),
  43275     JS_ALIAS_DEF("trimRight", "trimEnd" ),
  43276     JS_CFUNC_MAGIC_DEF("trimStart", 0, js_string_trim, 1 ),
  43277     JS_ALIAS_DEF("trimLeft", "trimStart" ),
  43278     JS_CFUNC_DEF("toString", 0, js_string_toString ),
  43279     JS_CFUNC_DEF("valueOf", 0, js_string_toString ),
  43280     JS_CFUNC_DEF("__quote", 1, js_string___quote ),
  43281     JS_CFUNC_DEF("localeCompare", 1, js_string_localeCompare ),
  43282     JS_CFUNC_MAGIC_DEF("toLowerCase", 0, js_string_toLowerCase, 1 ),
  43283     JS_CFUNC_MAGIC_DEF("toUpperCase", 0, js_string_toLowerCase, 0 ),
  43284     JS_CFUNC_MAGIC_DEF("toLocaleLowerCase", 0, js_string_toLowerCase, 1 ),
  43285     JS_CFUNC_MAGIC_DEF("toLocaleUpperCase", 0, js_string_toLowerCase, 0 ),
  43286     JS_CFUNC_MAGIC_DEF("[Symbol.iterator]", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE | 4 ),
  43287     /* ES6 Annex B 2.3.2 etc. */
  43288     JS_CFUNC_MAGIC_DEF("anchor", 1, js_string_CreateHTML, magic_string_anchor ),
  43289     JS_CFUNC_MAGIC_DEF("big", 0, js_string_CreateHTML, magic_string_big ),
  43290     JS_CFUNC_MAGIC_DEF("blink", 0, js_string_CreateHTML, magic_string_blink ),
  43291     JS_CFUNC_MAGIC_DEF("bold", 0, js_string_CreateHTML, magic_string_bold ),
  43292     JS_CFUNC_MAGIC_DEF("fixed", 0, js_string_CreateHTML, magic_string_fixed ),
  43293     JS_CFUNC_MAGIC_DEF("fontcolor", 1, js_string_CreateHTML, magic_string_fontcolor ),
  43294     JS_CFUNC_MAGIC_DEF("fontsize", 1, js_string_CreateHTML, magic_string_fontsize ),
  43295     JS_CFUNC_MAGIC_DEF("italics", 0, js_string_CreateHTML, magic_string_italics ),
  43296     JS_CFUNC_MAGIC_DEF("link", 1, js_string_CreateHTML, magic_string_link ),
  43297     JS_CFUNC_MAGIC_DEF("small", 0, js_string_CreateHTML, magic_string_small ),
  43298     JS_CFUNC_MAGIC_DEF("strike", 0, js_string_CreateHTML, magic_string_strike ),
  43299     JS_CFUNC_MAGIC_DEF("sub", 0, js_string_CreateHTML, magic_string_sub ),
  43300     JS_CFUNC_MAGIC_DEF("sup", 0, js_string_CreateHTML, magic_string_sup ),
  43301 };
  43302 
  43303 static const JSCFunctionListEntry js_string_iterator_proto_funcs[] = {
  43304     JS_ITERATOR_NEXT_DEF("next", 0, js_string_iterator_next, 0 ),
  43305     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "String Iterator", JS_PROP_CONFIGURABLE ),
  43306 };
  43307 
  43308 #ifdef CONFIG_ALL_UNICODE
  43309 static const JSCFunctionListEntry js_string_proto_normalize[] = {
  43310     JS_CFUNC_DEF("normalize", 0, js_string_normalize ),
  43311 };
  43312 #endif
  43313 
  43314 void JS_AddIntrinsicStringNormalize(JSContext *ctx)
  43315 {
  43316 #ifdef CONFIG_ALL_UNICODE
  43317     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_normalize,
  43318                                countof(js_string_proto_normalize));
  43319 #endif
  43320 }
  43321 
  43322 /* Math */
  43323 
  43324 /* precondition: a and b are not NaN */
  43325 static double js_fmin(double a, double b)
  43326 {
  43327     if (a == 0 && b == 0) {
  43328         JSFloat64Union a1, b1;
  43329         a1.d = a;
  43330         b1.d = b;
  43331         a1.u64 |= b1.u64;
  43332         return a1.d;
  43333     } else {
  43334         return fmin(a, b);
  43335     }
  43336 }
  43337 
  43338 /* precondition: a and b are not NaN */
  43339 static double js_fmax(double a, double b)
  43340 {
  43341     if (a == 0 && b == 0) {
  43342         JSFloat64Union a1, b1;
  43343         a1.d = a;
  43344         b1.d = b;
  43345         a1.u64 &= b1.u64;
  43346         return a1.d;
  43347     } else {
  43348         return fmax(a, b);
  43349     }
  43350 }
  43351 
  43352 static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val,
  43353                                int argc, JSValueConst *argv, int magic)
  43354 {
  43355     BOOL is_max = magic;
  43356     double r, a;
  43357     int i;
  43358     uint32_t tag;
  43359 
  43360     if (unlikely(argc == 0)) {
  43361         return __JS_NewFloat64(ctx, is_max ? -1.0 / 0.0 : 1.0 / 0.0);
  43362     }
  43363 
  43364     tag = JS_VALUE_GET_TAG(argv[0]);
  43365     if (tag == JS_TAG_INT) {
  43366         int a1, r1 = JS_VALUE_GET_INT(argv[0]);
  43367         for(i = 1; i < argc; i++) {
  43368             tag = JS_VALUE_GET_TAG(argv[i]);
  43369             if (tag != JS_TAG_INT) {
  43370                 r = r1;
  43371                 goto generic_case;
  43372             }
  43373             a1 = JS_VALUE_GET_INT(argv[i]);
  43374             if (is_max)
  43375                 r1 = max_int(r1, a1);
  43376             else
  43377                 r1 = min_int(r1, a1);
  43378 
  43379         }
  43380         return JS_NewInt32(ctx, r1);
  43381     } else {
  43382         if (JS_ToFloat64(ctx, &r, argv[0]))
  43383             return JS_EXCEPTION;
  43384         i = 1;
  43385     generic_case:
  43386         while (i < argc) {
  43387             if (JS_ToFloat64(ctx, &a, argv[i]))
  43388                 return JS_EXCEPTION;
  43389             if (!isnan(r)) {
  43390                 if (isnan(a)) {
  43391                     r = a;
  43392                 } else {
  43393                     if (is_max)
  43394                         r = js_fmax(r, a);
  43395                     else
  43396                         r = js_fmin(r, a);
  43397                 }
  43398             }
  43399             i++;
  43400         }
  43401         return JS_NewFloat64(ctx, r);
  43402     }
  43403 }
  43404 
  43405 static double js_math_sign(double a)
  43406 {
  43407     if (isnan(a) || a == 0.0)
  43408         return a;
  43409     if (a < 0)
  43410         return -1;
  43411     else
  43412         return 1;
  43413 }
  43414 
  43415 static double js_math_round(double a)
  43416 {
  43417     JSFloat64Union u;
  43418     uint64_t frac_mask, one;
  43419     unsigned int e, s;
  43420 
  43421     u.d = a;
  43422     e = (u.u64 >> 52) & 0x7ff;
  43423     if (e < 1023) {
  43424         /* abs(a) < 1 */
  43425         if (e == (1023 - 1) && u.u64 != 0xbfe0000000000000) {
  43426             /* abs(a) > 0.5 or a = 0.5: return +/-1.0 */
  43427             u.u64 = (u.u64 & ((uint64_t)1 << 63)) | ((uint64_t)1023 << 52);
  43428         } else {
  43429             /* return +/-0.0 */
  43430             u.u64 &= (uint64_t)1 << 63;
  43431         }
  43432     } else if (e < (1023 + 52)) {
  43433         s = u.u64 >> 63;
  43434         one = (uint64_t)1 << (52 - (e - 1023));
  43435         frac_mask = one - 1;
  43436         u.u64 += (one >> 1) - s;
  43437         u.u64 &= ~frac_mask; /* truncate to an integer */
  43438     }
  43439     /* otherwise: abs(a) >= 2^52, or NaN, +/-Infinity: no change */
  43440     return u.d;
  43441 }
  43442 
  43443 static JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val,
  43444                              int argc, JSValueConst *argv)
  43445 {
  43446     double r, a;
  43447     int i;
  43448 
  43449     r = 0;
  43450     if (argc > 0) {
  43451         if (JS_ToFloat64(ctx, &r, argv[0]))
  43452             return JS_EXCEPTION;
  43453         if (argc == 1) {
  43454             r = fabs(r);
  43455         } else {
  43456             /* use the built-in function to minimize precision loss */
  43457             for (i = 1; i < argc; i++) {
  43458                 if (JS_ToFloat64(ctx, &a, argv[i]))
  43459                     return JS_EXCEPTION;
  43460                 r = hypot(r, a);
  43461             }
  43462         }
  43463     }
  43464     return JS_NewFloat64(ctx, r);
  43465 }
  43466 
  43467 static double js_math_fround(double a)
  43468 {
  43469     return (float)a;
  43470 }
  43471 
  43472 static JSValue js_math_imul(JSContext *ctx, JSValueConst this_val,
  43473                             int argc, JSValueConst *argv)
  43474 {
  43475     uint32_t a, b, c;
  43476     int32_t d;
  43477 
  43478     if (JS_ToUint32(ctx, &a, argv[0]))
  43479         return JS_EXCEPTION;
  43480     if (JS_ToUint32(ctx, &b, argv[1]))
  43481         return JS_EXCEPTION;
  43482     c = a * b;
  43483     memcpy(&d, &c, sizeof(d));
  43484     return JS_NewInt32(ctx, d);
  43485 }
  43486 
  43487 static JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val,
  43488                              int argc, JSValueConst *argv)
  43489 {
  43490     uint32_t a, r;
  43491 
  43492     if (JS_ToUint32(ctx, &a, argv[0]))
  43493         return JS_EXCEPTION;
  43494     if (a == 0)
  43495         r = 32;
  43496     else
  43497         r = clz32(a);
  43498     return JS_NewInt32(ctx, r);
  43499 }
  43500 
  43501 /* xorshift* random number generator by Marsaglia */
  43502 static uint64_t xorshift64star(uint64_t *pstate)
  43503 {
  43504     uint64_t x;
  43505     x = *pstate;
  43506     x ^= x >> 12;
  43507     x ^= x << 25;
  43508     x ^= x >> 27;
  43509     *pstate = x;
  43510     return x * 0x2545F4914F6CDD1D;
  43511 }
  43512 
  43513 static void js_random_init(JSContext *ctx)
  43514 {
  43515     struct timeval tv;
  43516     gettimeofday(&tv, NULL);
  43517     ctx->random_state = ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec;
  43518     /* the state must be non zero */
  43519     if (ctx->random_state == 0)
  43520         ctx->random_state = 1;
  43521 }
  43522 
  43523 static JSValue js_math_random(JSContext *ctx, JSValueConst this_val,
  43524                               int argc, JSValueConst *argv)
  43525 {
  43526     JSFloat64Union u;
  43527     uint64_t v;
  43528 
  43529     v = xorshift64star(&ctx->random_state);
  43530     /* 1.0 <= u.d < 2 */
  43531     u.u64 = ((uint64_t)0x3ff << 52) | (v >> 12);
  43532     return __JS_NewFloat64(ctx, u.d - 1.0);
  43533 }
  43534 
  43535 static const JSCFunctionListEntry js_math_funcs[] = {
  43536     JS_CFUNC_MAGIC_DEF("min", 2, js_math_min_max, 0 ),
  43537     JS_CFUNC_MAGIC_DEF("max", 2, js_math_min_max, 1 ),
  43538     JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, fabs ),
  43539     JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, floor ),
  43540     JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, ceil ),
  43541     JS_CFUNC_SPECIAL_DEF("round", 1, f_f, js_math_round ),
  43542     JS_CFUNC_SPECIAL_DEF("sqrt", 1, f_f, sqrt ),
  43543 
  43544     JS_CFUNC_SPECIAL_DEF("acos", 1, f_f, acos ),
  43545     JS_CFUNC_SPECIAL_DEF("asin", 1, f_f, asin ),
  43546     JS_CFUNC_SPECIAL_DEF("atan", 1, f_f, atan ),
  43547     JS_CFUNC_SPECIAL_DEF("atan2", 2, f_f_f, atan2 ),
  43548     JS_CFUNC_SPECIAL_DEF("cos", 1, f_f, cos ),
  43549     JS_CFUNC_SPECIAL_DEF("exp", 1, f_f, exp ),
  43550     JS_CFUNC_SPECIAL_DEF("log", 1, f_f, log ),
  43551     JS_CFUNC_SPECIAL_DEF("pow", 2, f_f_f, js_pow ),
  43552     JS_CFUNC_SPECIAL_DEF("sin", 1, f_f, sin ),
  43553     JS_CFUNC_SPECIAL_DEF("tan", 1, f_f, tan ),
  43554     /* ES6 */
  43555     JS_CFUNC_SPECIAL_DEF("trunc", 1, f_f, trunc ),
  43556     JS_CFUNC_SPECIAL_DEF("sign", 1, f_f, js_math_sign ),
  43557     JS_CFUNC_SPECIAL_DEF("cosh", 1, f_f, cosh ),
  43558     JS_CFUNC_SPECIAL_DEF("sinh", 1, f_f, sinh ),
  43559     JS_CFUNC_SPECIAL_DEF("tanh", 1, f_f, tanh ),
  43560     JS_CFUNC_SPECIAL_DEF("acosh", 1, f_f, acosh ),
  43561     JS_CFUNC_SPECIAL_DEF("asinh", 1, f_f, asinh ),
  43562     JS_CFUNC_SPECIAL_DEF("atanh", 1, f_f, atanh ),
  43563     JS_CFUNC_SPECIAL_DEF("expm1", 1, f_f, expm1 ),
  43564     JS_CFUNC_SPECIAL_DEF("log1p", 1, f_f, log1p ),
  43565     JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, log2 ),
  43566     JS_CFUNC_SPECIAL_DEF("log10", 1, f_f, log10 ),
  43567     JS_CFUNC_SPECIAL_DEF("cbrt", 1, f_f, cbrt ),
  43568     JS_CFUNC_DEF("hypot", 2, js_math_hypot ),
  43569     JS_CFUNC_DEF("random", 0, js_math_random ),
  43570     JS_CFUNC_SPECIAL_DEF("fround", 1, f_f, js_math_fround ),
  43571     JS_CFUNC_DEF("imul", 2, js_math_imul ),
  43572     JS_CFUNC_DEF("clz32", 1, js_math_clz32 ),
  43573     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Math", JS_PROP_CONFIGURABLE ),
  43574     JS_PROP_DOUBLE_DEF("E", 2.718281828459045, 0 ),
  43575     JS_PROP_DOUBLE_DEF("LN10", 2.302585092994046, 0 ),
  43576     JS_PROP_DOUBLE_DEF("LN2", 0.6931471805599453, 0 ),
  43577     JS_PROP_DOUBLE_DEF("LOG2E", 1.4426950408889634, 0 ),
  43578     JS_PROP_DOUBLE_DEF("LOG10E", 0.4342944819032518, 0 ),
  43579     JS_PROP_DOUBLE_DEF("PI", 3.141592653589793, 0 ),
  43580     JS_PROP_DOUBLE_DEF("SQRT1_2", 0.7071067811865476, 0 ),
  43581     JS_PROP_DOUBLE_DEF("SQRT2", 1.4142135623730951, 0 ),
  43582 };
  43583 
  43584 static const JSCFunctionListEntry js_math_obj[] = {
  43585     JS_OBJECT_DEF("Math", js_math_funcs, countof(js_math_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
  43586 };
  43587 
  43588 /* Date */
  43589 
  43590 /* OS dependent. d = argv[0] is in ms from 1970. Return the difference
  43591    between UTC time and local time 'd' in minutes */
  43592 static int getTimezoneOffset(int64_t time)
  43593 {
  43594     time_t ti;
  43595     int res;
  43596 
  43597     time /= 1000; /* convert to seconds */
  43598     if (sizeof(time_t) == 4) {
  43599         /* on 32-bit systems, we need to clamp the time value to the
  43600            range of `time_t`. This is better than truncating values to
  43601            32 bits and hopefully provides the same result as 64-bit
  43602            implementation of localtime_r.
  43603          */
  43604         if ((time_t)-1 < 0) {
  43605             if (time < INT32_MIN) {
  43606                 time = INT32_MIN;
  43607             } else if (time > INT32_MAX) {
  43608                 time = INT32_MAX;
  43609             }
  43610         } else {
  43611             if (time < 0) {
  43612                 time = 0;
  43613             } else if (time > UINT32_MAX) {
  43614                 time = UINT32_MAX;
  43615             }
  43616         }
  43617     }
  43618     ti = time;
  43619 #if defined(_WIN32)
  43620     {
  43621         struct tm *tm;
  43622         time_t gm_ti, loc_ti;
  43623 
  43624         tm = gmtime(&ti);
  43625         gm_ti = mktime(tm);
  43626 
  43627         tm = localtime(&ti);
  43628         loc_ti = mktime(tm);
  43629 
  43630         res = (gm_ti - loc_ti) / 60;
  43631     }
  43632 #else
  43633     {
  43634         struct tm tm;
  43635         localtime_r(&ti, &tm);
  43636         res = -tm.tm_gmtoff / 60;
  43637     }
  43638 #endif
  43639     return res;
  43640 }
  43641 
  43642 #if 0
  43643 static JSValue js___date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val,
  43644                                            int argc, JSValueConst *argv)
  43645 {
  43646     double dd;
  43647 
  43648     if (JS_ToFloat64(ctx, &dd, argv[0]))
  43649         return JS_EXCEPTION;
  43650     if (isnan(dd))
  43651         return __JS_NewFloat64(ctx, dd);
  43652     else
  43653         return JS_NewInt32(ctx, getTimezoneOffset((int64_t)dd));
  43654 }
  43655 
  43656 static JSValue js_get_prototype_from_ctor(JSContext *ctx, JSValueConst ctor,
  43657                                           JSValueConst def_proto)
  43658 {
  43659     JSValue proto;
  43660     proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype);
  43661     if (JS_IsException(proto))
  43662         return proto;
  43663     if (!JS_IsObject(proto)) {
  43664         JS_FreeValue(ctx, proto);
  43665         proto = JS_DupValue(ctx, def_proto);
  43666     }
  43667     return proto;
  43668 }
  43669 
  43670 /* create a new date object */
  43671 static JSValue js___date_create(JSContext *ctx, JSValueConst this_val,
  43672                                 int argc, JSValueConst *argv)
  43673 {
  43674     JSValue obj, proto;
  43675     proto = js_get_prototype_from_ctor(ctx, argv[0], argv[1]);
  43676     if (JS_IsException(proto))
  43677         return proto;
  43678     obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_DATE);
  43679     JS_FreeValue(ctx, proto);
  43680     if (!JS_IsException(obj))
  43681         JS_SetObjectData(ctx, obj, JS_DupValue(ctx, argv[2]));
  43682     return obj;
  43683 }
  43684 #endif
  43685 
  43686 /* RegExp */
  43687 
  43688 static void js_regexp_finalizer(JSRuntime *rt, JSValue val)
  43689 {
  43690     JSObject *p = JS_VALUE_GET_OBJ(val);
  43691     JSRegExp *re = &p->u.regexp;
  43692     JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->bytecode));
  43693     JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->pattern));
  43694 }
  43695 
  43696 /* create a string containing the RegExp bytecode */
  43697 static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern,
  43698                                  JSValueConst flags)
  43699 {
  43700     const char *str;
  43701     int re_flags, mask;
  43702     uint8_t *re_bytecode_buf;
  43703     size_t i, len;
  43704     int re_bytecode_len;
  43705     JSValue ret;
  43706     char error_msg[64];
  43707 
  43708     re_flags = 0;
  43709     if (!JS_IsUndefined(flags)) {
  43710         str = JS_ToCStringLen(ctx, &len, flags);
  43711         if (!str)
  43712             return JS_EXCEPTION;
  43713         /* XXX: re_flags = LRE_FLAG_OCTAL unless strict mode? */
  43714         for (i = 0; i < len; i++) {
  43715             switch(str[i]) {
  43716             case 'd':
  43717                 mask = LRE_FLAG_INDICES;
  43718                 break;
  43719             case 'g':
  43720                 mask = LRE_FLAG_GLOBAL;
  43721                 break;
  43722             case 'i':
  43723                 mask = LRE_FLAG_IGNORECASE;
  43724                 break;
  43725             case 'm':
  43726                 mask = LRE_FLAG_MULTILINE;
  43727                 break;
  43728             case 's':
  43729                 mask = LRE_FLAG_DOTALL;
  43730                 break;
  43731             case 'u':
  43732                 mask = LRE_FLAG_UNICODE;
  43733                 break;
  43734             case 'y':
  43735                 mask = LRE_FLAG_STICKY;
  43736                 break;
  43737             default:
  43738                 goto bad_flags;
  43739             }
  43740             if ((re_flags & mask) != 0) {
  43741             bad_flags:
  43742                 JS_FreeCString(ctx, str);
  43743                 return JS_ThrowSyntaxError(ctx, "invalid regular expression flags");
  43744             }
  43745             re_flags |= mask;
  43746         }
  43747         JS_FreeCString(ctx, str);
  43748     }
  43749 
  43750     str = JS_ToCStringLen2(ctx, &len, pattern, !(re_flags & LRE_FLAG_UNICODE));
  43751     if (!str)
  43752         return JS_EXCEPTION;
  43753     re_bytecode_buf = lre_compile(&re_bytecode_len, error_msg,
  43754                                   sizeof(error_msg), str, len, re_flags, ctx);
  43755     JS_FreeCString(ctx, str);
  43756     if (!re_bytecode_buf) {
  43757         JS_ThrowSyntaxError(ctx, "%s", error_msg);
  43758         return JS_EXCEPTION;
  43759     }
  43760 
  43761     ret = js_new_string8(ctx, re_bytecode_buf, re_bytecode_len);
  43762     js_free(ctx, re_bytecode_buf);
  43763     return ret;
  43764 }
  43765 
  43766 /* create a RegExp object from a string containing the RegExp bytecode
  43767    and the source pattern */
  43768 static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor,
  43769                                               JSValue pattern, JSValue bc)
  43770 {
  43771     JSValue obj;
  43772     JSObject *p;
  43773     JSRegExp *re;
  43774 
  43775     /* sanity check */
  43776     if (JS_VALUE_GET_TAG(bc) != JS_TAG_STRING ||
  43777         JS_VALUE_GET_TAG(pattern) != JS_TAG_STRING) {
  43778         JS_ThrowTypeError(ctx, "string expected");
  43779     fail:
  43780         JS_FreeValue(ctx, bc);
  43781         JS_FreeValue(ctx, pattern);
  43782         return JS_EXCEPTION;
  43783     }
  43784 
  43785     obj = js_create_from_ctor(ctx, ctor, JS_CLASS_REGEXP);
  43786     if (JS_IsException(obj))
  43787         goto fail;
  43788     p = JS_VALUE_GET_OBJ(obj);
  43789     re = &p->u.regexp;
  43790     re->pattern = JS_VALUE_GET_STRING(pattern);
  43791     re->bytecode = JS_VALUE_GET_STRING(bc);
  43792     JS_DefinePropertyValue(ctx, obj, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0),
  43793                            JS_PROP_WRITABLE);
  43794     return obj;
  43795 }
  43796 
  43797 static JSRegExp *js_get_regexp(JSContext *ctx, JSValueConst obj, BOOL throw_error)
  43798 {
  43799     if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
  43800         JSObject *p = JS_VALUE_GET_OBJ(obj);
  43801         if (p->class_id == JS_CLASS_REGEXP)
  43802             return &p->u.regexp;
  43803     }
  43804     if (throw_error) {
  43805         JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP);
  43806     }
  43807     return NULL;
  43808 }
  43809 
  43810 /* return < 0 if exception or TRUE/FALSE */
  43811 static int js_is_regexp(JSContext *ctx, JSValueConst obj)
  43812 {
  43813     JSValue m;
  43814 
  43815     if (!JS_IsObject(obj))
  43816         return FALSE;
  43817     m = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_match);
  43818     if (JS_IsException(m))
  43819         return -1;
  43820     if (!JS_IsUndefined(m))
  43821         return JS_ToBoolFree(ctx, m);
  43822     return js_get_regexp(ctx, obj, FALSE) != NULL;
  43823 }
  43824 
  43825 static JSValue js_regexp_constructor(JSContext *ctx, JSValueConst new_target,
  43826                                      int argc, JSValueConst *argv)
  43827 {
  43828     JSValue pattern, flags, bc, val;
  43829     JSValueConst pat, flags1;
  43830     JSRegExp *re;
  43831     int pat_is_regexp;
  43832 
  43833     pat = argv[0];
  43834     flags1 = argv[1];
  43835     pat_is_regexp = js_is_regexp(ctx, pat);
  43836     if (pat_is_regexp < 0)
  43837         return JS_EXCEPTION;
  43838     if (JS_IsUndefined(new_target)) {
  43839         /* called as a function */
  43840         new_target = JS_GetActiveFunction(ctx);
  43841         if (pat_is_regexp && JS_IsUndefined(flags1)) {
  43842             JSValue ctor;
  43843             BOOL res;
  43844             ctor = JS_GetProperty(ctx, pat, JS_ATOM_constructor);
  43845             if (JS_IsException(ctor))
  43846                 return ctor;
  43847             res = js_same_value(ctx, ctor, new_target);
  43848             JS_FreeValue(ctx, ctor);
  43849             if (res)
  43850                 return JS_DupValue(ctx, pat);
  43851         }
  43852     }
  43853     re = js_get_regexp(ctx, pat, FALSE);
  43854     if (re) {
  43855         pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));
  43856         if (JS_IsUndefined(flags1)) {
  43857             bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode));
  43858             goto no_compilation;
  43859         } else {
  43860             flags = JS_ToString(ctx, flags1);
  43861             if (JS_IsException(flags))
  43862                 goto fail;
  43863         }
  43864     } else {
  43865         flags = JS_UNDEFINED;
  43866         if (pat_is_regexp) {
  43867             pattern = JS_GetProperty(ctx, pat, JS_ATOM_source);
  43868             if (JS_IsException(pattern))
  43869                 goto fail;
  43870             if (JS_IsUndefined(flags1)) {
  43871                 flags = JS_GetProperty(ctx, pat, JS_ATOM_flags);
  43872                 if (JS_IsException(flags))
  43873                     goto fail;
  43874             } else {
  43875                 flags = JS_DupValue(ctx, flags1);
  43876             }
  43877         } else {
  43878             pattern = JS_DupValue(ctx, pat);
  43879             flags = JS_DupValue(ctx, flags1);
  43880         }
  43881         if (JS_IsUndefined(pattern)) {
  43882             pattern = JS_AtomToString(ctx, JS_ATOM_empty_string);
  43883         } else {
  43884             val = pattern;
  43885             pattern = JS_ToString(ctx, val);
  43886             JS_FreeValue(ctx, val);
  43887             if (JS_IsException(pattern))
  43888                 goto fail;
  43889         }
  43890     }
  43891     bc = js_compile_regexp(ctx, pattern, flags);
  43892     if (JS_IsException(bc))
  43893         goto fail;
  43894     JS_FreeValue(ctx, flags);
  43895  no_compilation:
  43896     return js_regexp_constructor_internal(ctx, new_target, pattern, bc);
  43897  fail:
  43898     JS_FreeValue(ctx, pattern);
  43899     JS_FreeValue(ctx, flags);
  43900     return JS_EXCEPTION;
  43901 }
  43902 
  43903 static JSValue js_regexp_compile(JSContext *ctx, JSValueConst this_val,
  43904                                  int argc, JSValueConst *argv)
  43905 {
  43906     JSRegExp *re1, *re;
  43907     JSValueConst pattern1, flags1;
  43908     JSValue bc, pattern;
  43909 
  43910     re = js_get_regexp(ctx, this_val, TRUE);
  43911     if (!re)
  43912         return JS_EXCEPTION;
  43913     pattern1 = argv[0];
  43914     flags1 = argv[1];
  43915     re1 = js_get_regexp(ctx, pattern1, FALSE);
  43916     if (re1) {
  43917         if (!JS_IsUndefined(flags1))
  43918             return JS_ThrowTypeError(ctx, "flags must be undefined");
  43919         pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->pattern));
  43920         bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->bytecode));
  43921     } else {
  43922         bc = JS_UNDEFINED;
  43923         if (JS_IsUndefined(pattern1))
  43924             pattern = JS_AtomToString(ctx, JS_ATOM_empty_string);
  43925         else
  43926             pattern = JS_ToString(ctx, pattern1);
  43927         if (JS_IsException(pattern))
  43928             goto fail;
  43929         bc = js_compile_regexp(ctx, pattern, flags1);
  43930         if (JS_IsException(bc))
  43931             goto fail;
  43932     }
  43933     JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));
  43934     JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode));
  43935     re->pattern = JS_VALUE_GET_STRING(pattern);
  43936     re->bytecode = JS_VALUE_GET_STRING(bc);
  43937     if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
  43938                        JS_NewInt32(ctx, 0)) < 0)
  43939         return JS_EXCEPTION;
  43940     return JS_DupValue(ctx, this_val);
  43941  fail:
  43942     JS_FreeValue(ctx, pattern);
  43943     JS_FreeValue(ctx, bc);
  43944     return JS_EXCEPTION;
  43945 }
  43946 
  43947 #if 0
  43948 static JSValue js_regexp_get___source(JSContext *ctx, JSValueConst this_val)
  43949 {
  43950     JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
  43951     if (!re)
  43952         return JS_EXCEPTION;
  43953     return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));
  43954 }
  43955 
  43956 static JSValue js_regexp_get___flags(JSContext *ctx, JSValueConst this_val)
  43957 {
  43958     JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
  43959     int flags;
  43960 
  43961     if (!re)
  43962         return JS_EXCEPTION;
  43963     flags = lre_get_flags(re->bytecode->u.str8);
  43964     return JS_NewInt32(ctx, flags);
  43965 }
  43966 #endif
  43967 
  43968 static JSValue js_regexp_get_source(JSContext *ctx, JSValueConst this_val)
  43969 {
  43970     JSRegExp *re;
  43971     JSString *p;
  43972     StringBuffer b_s, *b = &b_s;
  43973     int i, n, c, c2, bra;
  43974 
  43975     if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
  43976         return JS_ThrowTypeErrorNotAnObject(ctx);
  43977 
  43978     if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP]))
  43979         goto empty_regex;
  43980 
  43981     re = js_get_regexp(ctx, this_val, TRUE);
  43982     if (!re)
  43983         return JS_EXCEPTION;
  43984 
  43985     p = re->pattern;
  43986 
  43987     if (p->len == 0) {
  43988     empty_regex:
  43989         return JS_NewString(ctx, "(?:)");
  43990     }
  43991     string_buffer_init2(ctx, b, p->len, p->is_wide_char);
  43992 
  43993     /* Escape '/' and newline sequences as needed */
  43994     bra = 0;
  43995     for (i = 0, n = p->len; i < n;) {
  43996         c2 = -1;
  43997         switch (c = string_get(p, i++)) {
  43998         case '\\':
  43999             if (i < n)
  44000                 c2 = string_get(p, i++);
  44001             break;
  44002         case ']':
  44003             bra = 0;
  44004             break;
  44005         case '[':
  44006             if (!bra) {
  44007                 if (i < n && string_get(p, i) == ']')
  44008                     c2 = string_get(p, i++);
  44009                 bra = 1;
  44010             }
  44011             break;
  44012         case '\n':
  44013             c = '\\';
  44014             c2 = 'n';
  44015             break;
  44016         case '\r':
  44017             c = '\\';
  44018             c2 = 'r';
  44019             break;
  44020         case '/':
  44021             if (!bra) {
  44022                 c = '\\';
  44023                 c2 = '/';
  44024             }
  44025             break;
  44026         }
  44027         string_buffer_putc16(b, c);
  44028         if (c2 >= 0)
  44029             string_buffer_putc16(b, c2);
  44030     }
  44031     return string_buffer_end(b);
  44032 }
  44033 
  44034 static JSValue js_regexp_get_flag(JSContext *ctx, JSValueConst this_val, int mask)
  44035 {
  44036     JSRegExp *re;
  44037     int flags;
  44038 
  44039     if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
  44040         return JS_ThrowTypeErrorNotAnObject(ctx);
  44041 
  44042     re = js_get_regexp(ctx, this_val, FALSE);
  44043     if (!re) {
  44044         if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP]))
  44045             return JS_UNDEFINED;
  44046         else
  44047             return JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP);
  44048     }
  44049 
  44050     flags = lre_get_flags(re->bytecode->u.str8);
  44051     return JS_NewBool(ctx, flags & mask);
  44052 }
  44053 
  44054 static JSValue js_regexp_get_flags(JSContext *ctx, JSValueConst this_val)
  44055 {
  44056     char str[8], *p = str;
  44057     int res;
  44058 
  44059     if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
  44060         return JS_ThrowTypeErrorNotAnObject(ctx);
  44061 
  44062     res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "hasIndices"));
  44063     if (res < 0)
  44064         goto exception;
  44065     if (res)
  44066         *p++ = 'd';
  44067     res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_global));
  44068     if (res < 0)
  44069         goto exception;
  44070     if (res)
  44071         *p++ = 'g';
  44072     res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "ignoreCase"));
  44073     if (res < 0)
  44074         goto exception;
  44075     if (res)
  44076         *p++ = 'i';
  44077     res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "multiline"));
  44078     if (res < 0)
  44079         goto exception;
  44080     if (res)
  44081         *p++ = 'm';
  44082     res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "dotAll"));
  44083     if (res < 0)
  44084         goto exception;
  44085     if (res)
  44086         *p++ = 's';
  44087     res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_unicode));
  44088     if (res < 0)
  44089         goto exception;
  44090     if (res)
  44091         *p++ = 'u';
  44092     res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "sticky"));
  44093     if (res < 0)
  44094         goto exception;
  44095     if (res)
  44096         *p++ = 'y';
  44097     return JS_NewStringLen(ctx, str, p - str);
  44098 
  44099 exception:
  44100     return JS_EXCEPTION;
  44101 }
  44102 
  44103 static JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val,
  44104                                   int argc, JSValueConst *argv)
  44105 {
  44106     JSValue pattern, flags;
  44107     StringBuffer b_s, *b = &b_s;
  44108 
  44109     if (!JS_IsObject(this_val))
  44110         return JS_ThrowTypeErrorNotAnObject(ctx);
  44111 
  44112     string_buffer_init(ctx, b, 0);
  44113     string_buffer_putc8(b, '/');
  44114     pattern = JS_GetProperty(ctx, this_val, JS_ATOM_source);
  44115     if (string_buffer_concat_value_free(b, pattern))
  44116         goto fail;
  44117     string_buffer_putc8(b, '/');
  44118     flags = JS_GetProperty(ctx, this_val, JS_ATOM_flags);
  44119     if (string_buffer_concat_value_free(b, flags))
  44120         goto fail;
  44121     return string_buffer_end(b);
  44122 
  44123 fail:
  44124     string_buffer_free(b);
  44125     return JS_EXCEPTION;
  44126 }
  44127 
  44128 BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size)
  44129 {
  44130     JSContext *ctx = opaque;
  44131     return js_check_stack_overflow(ctx->rt, alloca_size);
  44132 }
  44133 
  44134 void *lre_realloc(void *opaque, void *ptr, size_t size)
  44135 {
  44136     JSContext *ctx = opaque;
  44137     /* No JS exception is raised here */
  44138     return js_realloc_rt(ctx->rt, ptr, size);
  44139 }
  44140 
  44141 static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val,
  44142                               int argc, JSValueConst *argv)
  44143 {
  44144     JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
  44145     JSString *str;
  44146     JSValue t, ret, str_val, obj, val, groups;
  44147     JSValue indices, indices_groups;
  44148     uint8_t *re_bytecode;
  44149     uint8_t **capture, *str_buf;
  44150     int rc, capture_count, shift, i, re_flags;
  44151     int64_t last_index;
  44152     const char *group_name_ptr;
  44153 
  44154     if (!re)
  44155         return JS_EXCEPTION;
  44156 
  44157     str_val = JS_ToString(ctx, argv[0]);
  44158     if (JS_IsException(str_val))
  44159         return JS_EXCEPTION;
  44160 
  44161     ret = JS_EXCEPTION;
  44162     obj = JS_NULL;
  44163     groups = JS_UNDEFINED;
  44164     indices = JS_UNDEFINED;
  44165     indices_groups = JS_UNDEFINED;
  44166     capture = NULL;
  44167 
  44168     val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex);
  44169     if (JS_IsException(val) || JS_ToLengthFree(ctx, &last_index, val))
  44170         goto fail;
  44171 
  44172     re_bytecode = re->bytecode->u.str8;
  44173     re_flags = lre_get_flags(re_bytecode);
  44174     if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) {
  44175         last_index = 0;
  44176     }
  44177     str = JS_VALUE_GET_STRING(str_val);
  44178     capture_count = lre_get_capture_count(re_bytecode);
  44179     if (capture_count > 0) {
  44180         capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2);
  44181         if (!capture)
  44182             goto fail;
  44183     }
  44184     shift = str->is_wide_char;
  44185     str_buf = str->u.str8;
  44186     if (last_index > str->len) {
  44187         rc = 2;
  44188     } else {
  44189         rc = lre_exec(capture, re_bytecode,
  44190                       str_buf, last_index, str->len,
  44191                       shift, ctx);
  44192     }
  44193     if (rc != 1) {
  44194         if (rc >= 0) {
  44195             if (rc == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) {
  44196                 if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
  44197                                    JS_NewInt32(ctx, 0)) < 0)
  44198                     goto fail;
  44199             }
  44200         } else {
  44201             JS_ThrowInternalError(ctx, "out of memory in regexp execution");
  44202             goto fail;
  44203         }
  44204     } else {
  44205         int prop_flags;
  44206         if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) {
  44207             if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
  44208                                JS_NewInt32(ctx, (capture[1] - str_buf) >> shift)) < 0)
  44209                 goto fail;
  44210         }
  44211         obj = JS_NewArray(ctx);
  44212         if (JS_IsException(obj))
  44213             goto fail;
  44214         prop_flags = JS_PROP_C_W_E | JS_PROP_THROW;
  44215         group_name_ptr = lre_get_groupnames(re_bytecode);
  44216         if (group_name_ptr) {
  44217             groups = JS_NewObjectProto(ctx, JS_NULL);
  44218             if (JS_IsException(groups))
  44219                 goto fail;
  44220         }
  44221         if (re_flags & LRE_FLAG_INDICES) {
  44222             indices = JS_NewArray(ctx);
  44223             if (JS_IsException(indices))
  44224                 goto fail;
  44225             if (group_name_ptr) {
  44226                 indices_groups = JS_NewObjectProto(ctx, JS_NULL);
  44227                 if (JS_IsException(indices_groups))
  44228                     goto fail;
  44229             }
  44230         }
  44231 
  44232         for(i = 0; i < capture_count; i++) {
  44233             const char *name = NULL;
  44234             uint8_t **match = &capture[2 * i];
  44235             int start = -1;
  44236             int end = -1;
  44237             JSValue val;
  44238 
  44239             if (group_name_ptr && i > 0) {
  44240                 if (*group_name_ptr) name = group_name_ptr;
  44241                 group_name_ptr += strlen(group_name_ptr) + 1;
  44242             }
  44243 
  44244             if (match[0] && match[1]) {
  44245                 start = (match[0] - str_buf) >> shift;
  44246                 end = (match[1] - str_buf) >> shift;
  44247             }
  44248 
  44249             if (!JS_IsUndefined(indices)) {
  44250                 val = JS_UNDEFINED;
  44251                 if (start != -1) {
  44252                     val = JS_NewArray(ctx);
  44253                     if (JS_IsException(val))
  44254                         goto fail;
  44255                     if (JS_DefinePropertyValueUint32(ctx, val, 0,
  44256                                                      JS_NewInt32(ctx, start),
  44257                                                      prop_flags) < 0) {
  44258                         JS_FreeValue(ctx, val);
  44259                         goto fail;
  44260                     }
  44261                     if (JS_DefinePropertyValueUint32(ctx, val, 1,
  44262                                                      JS_NewInt32(ctx, end),
  44263                                                      prop_flags) < 0) {
  44264                         JS_FreeValue(ctx, val);
  44265                         goto fail;
  44266                     }
  44267                 }
  44268                 if (name && !JS_IsUndefined(indices_groups)) {
  44269                     val = JS_DupValue(ctx, val);
  44270                     if (JS_DefinePropertyValueStr(ctx, indices_groups,
  44271                                                   name, val, prop_flags) < 0) {
  44272                         JS_FreeValue(ctx, val);
  44273                         goto fail;
  44274                     }
  44275                 }
  44276                 if (JS_DefinePropertyValueUint32(ctx, indices, i, val,
  44277                                                  prop_flags) < 0) {
  44278                     goto fail;
  44279                 }
  44280             }
  44281 
  44282             val = JS_UNDEFINED;
  44283             if (start != -1) {
  44284                 val = js_sub_string(ctx, str, start, end);
  44285                 if (JS_IsException(val))
  44286                     goto fail;
  44287             }
  44288 
  44289             if (name) {
  44290                 if (JS_DefinePropertyValueStr(ctx, groups, name,
  44291                                               JS_DupValue(ctx, val),
  44292                                               prop_flags) < 0) {
  44293                     JS_FreeValue(ctx, val);
  44294                     goto fail;
  44295                 }
  44296             }
  44297 
  44298             if (JS_DefinePropertyValueUint32(ctx, obj, i, val, prop_flags) < 0)
  44299                 goto fail;
  44300         }
  44301 
  44302         t = groups, groups = JS_UNDEFINED;
  44303         if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_groups,
  44304                                    t, prop_flags) < 0) {
  44305             goto fail;
  44306         }
  44307 
  44308         t = JS_NewInt32(ctx, (capture[0] - str_buf) >> shift);
  44309         if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_index, t, prop_flags) < 0)
  44310             goto fail;
  44311 
  44312         t = str_val, str_val = JS_UNDEFINED;
  44313         if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_input, t, prop_flags) < 0)
  44314             goto fail;
  44315 
  44316         if (!JS_IsUndefined(indices)) {
  44317             t = indices_groups, indices_groups = JS_UNDEFINED;
  44318             if (JS_DefinePropertyValue(ctx, indices, JS_ATOM_groups,
  44319                                        t, prop_flags) < 0) {
  44320                 goto fail;
  44321             }
  44322             t = indices, indices = JS_UNDEFINED;
  44323             if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_indices,
  44324                                        t, prop_flags) < 0) {
  44325                 goto fail;
  44326             }
  44327         }
  44328     }
  44329     ret = obj;
  44330     obj = JS_UNDEFINED;
  44331 fail:
  44332     JS_FreeValue(ctx, indices_groups);
  44333     JS_FreeValue(ctx, indices);
  44334     JS_FreeValue(ctx, str_val);
  44335     JS_FreeValue(ctx, groups);
  44336     JS_FreeValue(ctx, obj);
  44337     js_free(ctx, capture);
  44338     return ret;
  44339 }
  44340 
  44341 /* delete portions of a string that match a given regex */
  44342 static JSValue JS_RegExpDelete(JSContext *ctx, JSValueConst this_val, JSValueConst arg)
  44343 {
  44344     JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
  44345     JSString *str;
  44346     JSValue str_val, val;
  44347     uint8_t *re_bytecode;
  44348     int ret;
  44349     uint8_t **capture, *str_buf;
  44350     int capture_count, shift, re_flags;
  44351     int next_src_pos, start, end;
  44352     int64_t last_index;
  44353     StringBuffer b_s, *b = &b_s;
  44354 
  44355     if (!re)
  44356         return JS_EXCEPTION;
  44357 
  44358     string_buffer_init(ctx, b, 0);
  44359 
  44360     capture = NULL;
  44361     str_val = JS_ToString(ctx, arg);
  44362     if (JS_IsException(str_val))
  44363         goto fail;
  44364     str = JS_VALUE_GET_STRING(str_val);
  44365     re_bytecode = re->bytecode->u.str8;
  44366     re_flags = lre_get_flags(re_bytecode);
  44367     if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) {
  44368         last_index = 0;
  44369     } else {
  44370         val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex);
  44371         if (JS_IsException(val) || JS_ToLengthFree(ctx, &last_index, val))
  44372             goto fail;
  44373     }
  44374     capture_count = lre_get_capture_count(re_bytecode);
  44375     if (capture_count > 0) {
  44376         capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2);
  44377         if (!capture)
  44378             goto fail;
  44379     }
  44380     shift = str->is_wide_char;
  44381     str_buf = str->u.str8;
  44382     next_src_pos = 0;
  44383     for (;;) {
  44384         if (last_index > str->len)
  44385             break;
  44386 
  44387         ret = lre_exec(capture, re_bytecode,
  44388                        str_buf, last_index, str->len, shift, ctx);
  44389         if (ret != 1) {
  44390             if (ret >= 0) {
  44391                 if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) {
  44392                     if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
  44393                                        JS_NewInt32(ctx, 0)) < 0)
  44394                         goto fail;
  44395                 }
  44396             } else {
  44397                 JS_ThrowInternalError(ctx, "out of memory in regexp execution");
  44398                 goto fail;
  44399             }
  44400             break;
  44401         }
  44402         start = (capture[0] - str_buf) >> shift;
  44403         end = (capture[1] - str_buf) >> shift;
  44404         last_index = end;
  44405         if (next_src_pos < start) {
  44406             if (string_buffer_concat(b, str, next_src_pos, start))
  44407                 goto fail;
  44408         }
  44409         next_src_pos = end;
  44410         if (!(re_flags & LRE_FLAG_GLOBAL)) {
  44411             if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
  44412                                JS_NewInt32(ctx, end)) < 0)
  44413                 goto fail;
  44414             break;
  44415         }
  44416         if (end == start) {
  44417             if (!(re_flags & LRE_FLAG_UNICODE) || (unsigned)end >= str->len || !str->is_wide_char) {
  44418                 end++;
  44419             } else {
  44420                 string_getc(str, &end);
  44421             }
  44422         }
  44423         last_index = end;
  44424     }
  44425     if (string_buffer_concat(b, str, next_src_pos, str->len))
  44426         goto fail;
  44427     JS_FreeValue(ctx, str_val);
  44428     js_free(ctx, capture);
  44429     return string_buffer_end(b);
  44430 fail:
  44431     JS_FreeValue(ctx, str_val);
  44432     js_free(ctx, capture);
  44433     string_buffer_free(b);
  44434     return JS_EXCEPTION;
  44435 }
  44436 
  44437 static JSValue JS_RegExpExec(JSContext *ctx, JSValueConst r, JSValueConst s)
  44438 {
  44439     JSValue method, ret;
  44440 
  44441     method = JS_GetProperty(ctx, r, JS_ATOM_exec);
  44442     if (JS_IsException(method))
  44443         return method;
  44444     if (JS_IsFunction(ctx, method)) {
  44445         ret = JS_CallFree(ctx, method, r, 1, &s);
  44446         if (JS_IsException(ret))
  44447             return ret;
  44448         if (!JS_IsObject(ret) && !JS_IsNull(ret)) {
  44449             JS_FreeValue(ctx, ret);
  44450             return JS_ThrowTypeError(ctx, "RegExp exec method must return an object or null");
  44451         }
  44452         return ret;
  44453     }
  44454     JS_FreeValue(ctx, method);
  44455     return js_regexp_exec(ctx, r, 1, &s);
  44456 }
  44457 
  44458 #if 0
  44459 static JSValue js_regexp___RegExpExec(JSContext *ctx, JSValueConst this_val,
  44460                                       int argc, JSValueConst *argv)
  44461 {
  44462     return JS_RegExpExec(ctx, argv[0], argv[1]);
  44463 }
  44464 static JSValue js_regexp___RegExpDelete(JSContext *ctx, JSValueConst this_val,
  44465                                         int argc, JSValueConst *argv)
  44466 {
  44467     return JS_RegExpDelete(ctx, argv[0], argv[1]);
  44468 }
  44469 #endif
  44470 
  44471 static JSValue js_regexp_test(JSContext *ctx, JSValueConst this_val,
  44472                               int argc, JSValueConst *argv)
  44473 {
  44474     JSValue val;
  44475     BOOL ret;
  44476 
  44477     val = JS_RegExpExec(ctx, this_val, argv[0]);
  44478     if (JS_IsException(val))
  44479         return JS_EXCEPTION;
  44480     ret = !JS_IsNull(val);
  44481     JS_FreeValue(ctx, val);
  44482     return JS_NewBool(ctx, ret);
  44483 }
  44484 
  44485 static JSValue js_regexp_Symbol_match(JSContext *ctx, JSValueConst this_val,
  44486                                       int argc, JSValueConst *argv)
  44487 {
  44488     // [Symbol.match](str)
  44489     JSValueConst rx = this_val;
  44490     JSValue A, S, flags, result, matchStr;
  44491     int global, n, fullUnicode, isEmpty;
  44492     JSString *p;
  44493 
  44494     if (!JS_IsObject(rx))
  44495         return JS_ThrowTypeErrorNotAnObject(ctx);
  44496 
  44497     A = JS_UNDEFINED;
  44498     flags = JS_UNDEFINED;
  44499     result = JS_UNDEFINED;
  44500     matchStr = JS_UNDEFINED;
  44501     S = JS_ToString(ctx, argv[0]);
  44502     if (JS_IsException(S))
  44503         goto exception;
  44504 
  44505     flags = JS_GetProperty(ctx, rx, JS_ATOM_flags);
  44506     if (JS_IsException(flags))
  44507         goto exception;
  44508     flags = JS_ToStringFree(ctx, flags);
  44509     if (JS_IsException(flags))
  44510         goto exception;
  44511     p = JS_VALUE_GET_STRING(flags);
  44512 
  44513     // TODO(bnoordhuis) query 'u' flag the same way?
  44514     global = (-1 != string_indexof_char(p, 'g', 0));
  44515     if (!global) {
  44516         A = JS_RegExpExec(ctx, rx, S);
  44517     } else {
  44518         fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode));
  44519         if (fullUnicode < 0)
  44520             goto exception;
  44521 
  44522         if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0)
  44523             goto exception;
  44524         A = JS_NewArray(ctx);
  44525         if (JS_IsException(A))
  44526             goto exception;
  44527         n = 0;
  44528         for(;;) {
  44529             JS_FreeValue(ctx, result);
  44530             result = JS_RegExpExec(ctx, rx, S);
  44531             if (JS_IsException(result))
  44532                 goto exception;
  44533             if (JS_IsNull(result))
  44534                 break;
  44535             matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));
  44536             if (JS_IsException(matchStr))
  44537                 goto exception;
  44538             isEmpty = JS_IsEmptyString(matchStr);
  44539             if (JS_SetPropertyInt64(ctx, A, n++, matchStr) < 0)
  44540                 goto exception;
  44541             if (isEmpty) {
  44542                 int64_t thisIndex, nextIndex;
  44543                 if (JS_ToLengthFree(ctx, &thisIndex,
  44544                                     JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0)
  44545                     goto exception;
  44546                 p = JS_VALUE_GET_STRING(S);
  44547                 nextIndex = string_advance_index(p, thisIndex, fullUnicode);
  44548                 if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0)
  44549                     goto exception;
  44550             }
  44551         }
  44552         if (n == 0) {
  44553             JS_FreeValue(ctx, A);
  44554             A = JS_NULL;
  44555         }
  44556     }
  44557     JS_FreeValue(ctx, result);
  44558     JS_FreeValue(ctx, flags);
  44559     JS_FreeValue(ctx, S);
  44560     return A;
  44561 
  44562 exception:
  44563     JS_FreeValue(ctx, A);
  44564     JS_FreeValue(ctx, result);
  44565     JS_FreeValue(ctx, flags);
  44566     JS_FreeValue(ctx, S);
  44567     return JS_EXCEPTION;
  44568 }
  44569 
  44570 typedef struct JSRegExpStringIteratorData {
  44571     JSValue iterating_regexp;
  44572     JSValue iterated_string;
  44573     BOOL global;
  44574     BOOL unicode;
  44575     BOOL done;
  44576 } JSRegExpStringIteratorData;
  44577 
  44578 static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val)
  44579 {
  44580     JSObject *p = JS_VALUE_GET_OBJ(val);
  44581     JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data;
  44582     if (it) {
  44583         JS_FreeValueRT(rt, it->iterating_regexp);
  44584         JS_FreeValueRT(rt, it->iterated_string);
  44585         js_free_rt(rt, it);
  44586     }
  44587 }
  44588 
  44589 static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val,
  44590                                            JS_MarkFunc *mark_func)
  44591 {
  44592     JSObject *p = JS_VALUE_GET_OBJ(val);
  44593     JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data;
  44594     if (it) {
  44595         JS_MarkValue(rt, it->iterating_regexp, mark_func);
  44596         JS_MarkValue(rt, it->iterated_string, mark_func);
  44597     }
  44598 }
  44599 
  44600 static JSValue js_regexp_string_iterator_next(JSContext *ctx,
  44601                                               JSValueConst this_val,
  44602                                               int argc, JSValueConst *argv,
  44603                                               BOOL *pdone, int magic)
  44604 {
  44605     JSRegExpStringIteratorData *it;
  44606     JSValueConst R, S;
  44607     JSValue matchStr = JS_UNDEFINED, match = JS_UNDEFINED;
  44608     JSString *sp;
  44609 
  44610     it = JS_GetOpaque2(ctx, this_val, JS_CLASS_REGEXP_STRING_ITERATOR);
  44611     if (!it)
  44612         goto exception;
  44613     if (it->done) {
  44614         *pdone = TRUE;
  44615         return JS_UNDEFINED;
  44616     }
  44617     R = it->iterating_regexp;
  44618     S = it->iterated_string;
  44619     match = JS_RegExpExec(ctx, R, S);
  44620     if (JS_IsException(match))
  44621         goto exception;
  44622     if (JS_IsNull(match)) {
  44623         it->done = TRUE;
  44624         *pdone = TRUE;
  44625         return JS_UNDEFINED;
  44626     } else if (it->global) {
  44627         matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, match, 0));
  44628         if (JS_IsException(matchStr))
  44629             goto exception;
  44630         if (JS_IsEmptyString(matchStr)) {
  44631             int64_t thisIndex, nextIndex;
  44632             if (JS_ToLengthFree(ctx, &thisIndex,
  44633                                 JS_GetProperty(ctx, R, JS_ATOM_lastIndex)) < 0)
  44634                 goto exception;
  44635             sp = JS_VALUE_GET_STRING(S);
  44636             nextIndex = string_advance_index(sp, thisIndex, it->unicode);
  44637             if (JS_SetProperty(ctx, R, JS_ATOM_lastIndex,
  44638                                JS_NewInt64(ctx, nextIndex)) < 0)
  44639                 goto exception;
  44640         }
  44641         JS_FreeValue(ctx, matchStr);
  44642     } else {
  44643         it->done = TRUE;
  44644     }
  44645     *pdone = FALSE;
  44646     return match;
  44647  exception:
  44648     JS_FreeValue(ctx, match);
  44649     JS_FreeValue(ctx, matchStr);
  44650     *pdone = FALSE;
  44651     return JS_EXCEPTION;
  44652 }
  44653 
  44654 static JSValue js_regexp_Symbol_matchAll(JSContext *ctx, JSValueConst this_val,
  44655                                          int argc, JSValueConst *argv)
  44656 {
  44657     // [Symbol.matchAll](str)
  44658     JSValueConst R = this_val;
  44659     JSValue S, C, flags, matcher, iter;
  44660     JSValueConst args[2];
  44661     JSString *strp;
  44662     int64_t lastIndex;
  44663     JSRegExpStringIteratorData *it;
  44664 
  44665     if (!JS_IsObject(R))
  44666         return JS_ThrowTypeErrorNotAnObject(ctx);
  44667 
  44668     C = JS_UNDEFINED;
  44669     flags = JS_UNDEFINED;
  44670     matcher = JS_UNDEFINED;
  44671     iter = JS_UNDEFINED;
  44672 
  44673     S = JS_ToString(ctx, argv[0]);
  44674     if (JS_IsException(S))
  44675         goto exception;
  44676     C = JS_SpeciesConstructor(ctx, R, ctx->regexp_ctor);
  44677     if (JS_IsException(C))
  44678         goto exception;
  44679     flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, R, JS_ATOM_flags));
  44680     if (JS_IsException(flags))
  44681         goto exception;
  44682     args[0] = R;
  44683     args[1] = flags;
  44684     matcher = JS_CallConstructor(ctx, C, 2, args);
  44685     if (JS_IsException(matcher))
  44686         goto exception;
  44687     if (JS_ToLengthFree(ctx, &lastIndex,
  44688                         JS_GetProperty(ctx, R, JS_ATOM_lastIndex)))
  44689         goto exception;
  44690     if (JS_SetProperty(ctx, matcher, JS_ATOM_lastIndex,
  44691                        JS_NewInt64(ctx, lastIndex)) < 0)
  44692         goto exception;
  44693 
  44694     iter = JS_NewObjectClass(ctx, JS_CLASS_REGEXP_STRING_ITERATOR);
  44695     if (JS_IsException(iter))
  44696         goto exception;
  44697     it = js_malloc(ctx, sizeof(*it));
  44698     if (!it)
  44699         goto exception;
  44700     it->iterating_regexp = matcher;
  44701     it->iterated_string = S;
  44702     strp = JS_VALUE_GET_STRING(flags);
  44703     it->global = string_indexof_char(strp, 'g', 0) >= 0;
  44704     it->unicode = string_indexof_char(strp, 'u', 0) >= 0;
  44705     it->done = FALSE;
  44706     JS_SetOpaque(iter, it);
  44707 
  44708     JS_FreeValue(ctx, C);
  44709     JS_FreeValue(ctx, flags);
  44710     return iter;
  44711  exception:
  44712     JS_FreeValue(ctx, S);
  44713     JS_FreeValue(ctx, C);
  44714     JS_FreeValue(ctx, flags);
  44715     JS_FreeValue(ctx, matcher);
  44716     JS_FreeValue(ctx, iter);
  44717     return JS_EXCEPTION;
  44718 }
  44719 
  44720 typedef struct ValueBuffer {
  44721     JSContext *ctx;
  44722     JSValue *arr;
  44723     JSValue def[4];
  44724     int len;
  44725     int size;
  44726     int error_status;
  44727 } ValueBuffer;
  44728 
  44729 static int value_buffer_init(JSContext *ctx, ValueBuffer *b)
  44730 {
  44731     b->ctx = ctx;
  44732     b->len = 0;
  44733     b->size = 4;
  44734     b->error_status = 0;
  44735     b->arr = b->def;
  44736     return 0;
  44737 }
  44738 
  44739 static void value_buffer_free(ValueBuffer *b)
  44740 {
  44741     while (b->len > 0)
  44742         JS_FreeValue(b->ctx, b->arr[--b->len]);
  44743     if (b->arr != b->def)
  44744         js_free(b->ctx, b->arr);
  44745     b->arr = b->def;
  44746     b->size = 4;
  44747 }
  44748 
  44749 static int value_buffer_append(ValueBuffer *b, JSValue val)
  44750 {
  44751     if (b->error_status)
  44752         return -1;
  44753 
  44754     if (b->len >= b->size) {
  44755         int new_size = (b->len + (b->len >> 1) + 31) & ~16;
  44756         size_t slack;
  44757         JSValue *new_arr;
  44758 
  44759         if (b->arr == b->def) {
  44760             new_arr = js_realloc2(b->ctx, NULL, sizeof(*b->arr) * new_size, &slack);
  44761             if (new_arr)
  44762                 memcpy(new_arr, b->def, sizeof b->def);
  44763         } else {
  44764             new_arr = js_realloc2(b->ctx, b->arr, sizeof(*b->arr) * new_size, &slack);
  44765         }
  44766         if (!new_arr) {
  44767             value_buffer_free(b);
  44768             JS_FreeValue(b->ctx, val);
  44769             b->error_status = -1;
  44770             return -1;
  44771         }
  44772         new_size += slack / sizeof(*new_arr);
  44773         b->arr = new_arr;
  44774         b->size = new_size;
  44775     }
  44776     b->arr[b->len++] = val;
  44777     return 0;
  44778 }
  44779 
  44780 static int js_is_standard_regexp(JSContext *ctx, JSValueConst rx)
  44781 {
  44782     JSValue val;
  44783     int res;
  44784 
  44785     val = JS_GetProperty(ctx, rx, JS_ATOM_constructor);
  44786     if (JS_IsException(val))
  44787         return -1;
  44788     // rx.constructor === RegExp
  44789     res = js_same_value(ctx, val, ctx->regexp_ctor);
  44790     JS_FreeValue(ctx, val);
  44791     if (res) {
  44792         val = JS_GetProperty(ctx, rx, JS_ATOM_exec);
  44793         if (JS_IsException(val))
  44794             return -1;
  44795         // rx.exec === RE_exec
  44796         res = JS_IsCFunction(ctx, val, js_regexp_exec, 0);
  44797         JS_FreeValue(ctx, val);
  44798     }
  44799     return res;
  44800 }
  44801 
  44802 static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val,
  44803                                         int argc, JSValueConst *argv)
  44804 {
  44805     // [Symbol.replace](str, rep)
  44806     JSValueConst rx = this_val, rep = argv[1];
  44807     JSValueConst args[6];
  44808     JSValue flags, str, rep_val, matched, tab, rep_str, namedCaptures, res;
  44809     JSString *p, *sp, *rp;
  44810     StringBuffer b_s, *b = &b_s;
  44811     ValueBuffer v_b, *results = &v_b;
  44812     int nextSourcePosition, n, j, functionalReplace, is_global, fullUnicode;
  44813     uint32_t nCaptures;
  44814     int64_t position;
  44815 
  44816     if (!JS_IsObject(rx))
  44817         return JS_ThrowTypeErrorNotAnObject(ctx);
  44818 
  44819     string_buffer_init(ctx, b, 0);
  44820     value_buffer_init(ctx, results);
  44821 
  44822     rep_val = JS_UNDEFINED;
  44823     matched = JS_UNDEFINED;
  44824     tab = JS_UNDEFINED;
  44825     flags = JS_UNDEFINED;
  44826     rep_str = JS_UNDEFINED;
  44827     namedCaptures = JS_UNDEFINED;
  44828 
  44829     str = JS_ToString(ctx, argv[0]);
  44830     if (JS_IsException(str))
  44831         goto exception;
  44832 
  44833     sp = JS_VALUE_GET_STRING(str);
  44834     rp = NULL;
  44835     functionalReplace = JS_IsFunction(ctx, rep);
  44836     if (!functionalReplace) {
  44837         rep_val = JS_ToString(ctx, rep);
  44838         if (JS_IsException(rep_val))
  44839             goto exception;
  44840         rp = JS_VALUE_GET_STRING(rep_val);
  44841     }
  44842 
  44843     flags = JS_GetProperty(ctx, rx, JS_ATOM_flags);
  44844     if (JS_IsException(flags))
  44845         goto exception;
  44846     flags = JS_ToStringFree(ctx, flags);
  44847     if (JS_IsException(flags))
  44848         goto exception;
  44849     p = JS_VALUE_GET_STRING(flags);
  44850 
  44851     // TODO(bnoordhuis) query 'u' flag the same way?
  44852     fullUnicode = 0;
  44853     is_global = (-1 != string_indexof_char(p, 'g', 0));
  44854     if (is_global) {
  44855         fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode));
  44856         if (fullUnicode < 0)
  44857             goto exception;
  44858         if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0)
  44859             goto exception;
  44860     }
  44861 
  44862     if (rp && rp->len == 0 && is_global && js_is_standard_regexp(ctx, rx)) {
  44863         /* use faster version for simple cases */
  44864         res = JS_RegExpDelete(ctx, rx, str);
  44865         goto done;
  44866     }
  44867     for(;;) {
  44868         JSValue result;
  44869         result = JS_RegExpExec(ctx, rx, str);
  44870         if (JS_IsException(result))
  44871             goto exception;
  44872         if (JS_IsNull(result))
  44873             break;
  44874         if (value_buffer_append(results, result) < 0)
  44875             goto exception;
  44876         if (!is_global)
  44877             break;
  44878         JS_FreeValue(ctx, matched);
  44879         matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));
  44880         if (JS_IsException(matched))
  44881             goto exception;
  44882         if (JS_IsEmptyString(matched)) {
  44883             /* always advance of at least one char */
  44884             int64_t thisIndex, nextIndex;
  44885             if (JS_ToLengthFree(ctx, &thisIndex, JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0)
  44886                 goto exception;
  44887             nextIndex = string_advance_index(sp, thisIndex, fullUnicode);
  44888             if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0)
  44889                 goto exception;
  44890         }
  44891     }
  44892     nextSourcePosition = 0;
  44893     for(j = 0; j < results->len; j++) {
  44894         JSValueConst result;
  44895         result = results->arr[j];
  44896         if (js_get_length32(ctx, &nCaptures, result) < 0)
  44897             goto exception;
  44898         JS_FreeValue(ctx, matched);
  44899         matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));
  44900         if (JS_IsException(matched))
  44901             goto exception;
  44902         if (JS_ToLengthFree(ctx, &position, JS_GetProperty(ctx, result, JS_ATOM_index)))
  44903             goto exception;
  44904         if (position > sp->len)
  44905             position = sp->len;
  44906         else if (position < 0)
  44907             position = 0;
  44908         /* ignore substition if going backward (can happen
  44909            with custom regexp object) */
  44910         JS_FreeValue(ctx, tab);
  44911         tab = JS_NewArray(ctx);
  44912         if (JS_IsException(tab))
  44913             goto exception;
  44914         if (JS_DefinePropertyValueInt64(ctx, tab, 0, JS_DupValue(ctx, matched),
  44915                                         JS_PROP_C_W_E | JS_PROP_THROW) < 0)
  44916             goto exception;
  44917         for(n = 1; n < nCaptures; n++) {
  44918             JSValue capN;
  44919             capN = JS_GetPropertyInt64(ctx, result, n);
  44920             if (JS_IsException(capN))
  44921                 goto exception;
  44922             if (!JS_IsUndefined(capN)) {
  44923                 capN = JS_ToStringFree(ctx, capN);
  44924                 if (JS_IsException(capN))
  44925                     goto exception;
  44926             }
  44927             if (JS_DefinePropertyValueInt64(ctx, tab, n, capN,
  44928                                             JS_PROP_C_W_E | JS_PROP_THROW) < 0)
  44929                 goto exception;
  44930         }
  44931         JS_FreeValue(ctx, namedCaptures);
  44932         namedCaptures = JS_GetProperty(ctx, result, JS_ATOM_groups);
  44933         if (JS_IsException(namedCaptures))
  44934             goto exception;
  44935         if (functionalReplace) {
  44936             if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_NewInt32(ctx, position), JS_PROP_C_W_E | JS_PROP_THROW) < 0)
  44937                 goto exception;
  44938             if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, str), JS_PROP_C_W_E | JS_PROP_THROW) < 0)
  44939                 goto exception;
  44940             if (!JS_IsUndefined(namedCaptures)) {
  44941                 if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, namedCaptures), JS_PROP_C_W_E | JS_PROP_THROW) < 0)
  44942                     goto exception;
  44943             }
  44944             args[0] = JS_UNDEFINED;
  44945             args[1] = tab;
  44946             JS_FreeValue(ctx, rep_str);
  44947             rep_str = JS_ToStringFree(ctx, js_function_apply(ctx, rep, 2, args, 0));
  44948         } else {
  44949             JSValue namedCaptures1;
  44950             if (!JS_IsUndefined(namedCaptures)) {
  44951                 namedCaptures1 = JS_ToObject(ctx, namedCaptures);
  44952                 if (JS_IsException(namedCaptures1))
  44953                     goto exception;
  44954             } else {
  44955                 namedCaptures1 = JS_UNDEFINED;
  44956             }
  44957             args[0] = matched;
  44958             args[1] = str;
  44959             args[2] = JS_NewInt32(ctx, position);
  44960             args[3] = tab;
  44961             args[4] = namedCaptures1;
  44962             args[5] = rep_val;
  44963             JS_FreeValue(ctx, rep_str);
  44964             rep_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args);
  44965             JS_FreeValue(ctx, namedCaptures1);
  44966         }
  44967         if (JS_IsException(rep_str))
  44968             goto exception;
  44969         if (position >= nextSourcePosition) {
  44970             string_buffer_concat(b, sp, nextSourcePosition, position);
  44971             string_buffer_concat_value(b, rep_str);
  44972             nextSourcePosition = position + JS_VALUE_GET_STRING(matched)->len;
  44973         }
  44974     }
  44975     string_buffer_concat(b, sp, nextSourcePosition, sp->len);
  44976     res = string_buffer_end(b);
  44977     goto done1;
  44978 
  44979 exception:
  44980     res = JS_EXCEPTION;
  44981 done:
  44982     string_buffer_free(b);
  44983 done1:
  44984     value_buffer_free(results);
  44985     JS_FreeValue(ctx, rep_val);
  44986     JS_FreeValue(ctx, matched);
  44987     JS_FreeValue(ctx, flags);
  44988     JS_FreeValue(ctx, tab);
  44989     JS_FreeValue(ctx, rep_str);
  44990     JS_FreeValue(ctx, namedCaptures);
  44991     JS_FreeValue(ctx, str);
  44992     return res;
  44993 }
  44994 
  44995 static JSValue js_regexp_Symbol_search(JSContext *ctx, JSValueConst this_val,
  44996                                        int argc, JSValueConst *argv)
  44997 {
  44998     JSValueConst rx = this_val;
  44999     JSValue str, previousLastIndex, currentLastIndex, result, index;
  45000 
  45001     if (!JS_IsObject(rx))
  45002         return JS_ThrowTypeErrorNotAnObject(ctx);
  45003 
  45004     result = JS_UNDEFINED;
  45005     currentLastIndex = JS_UNDEFINED;
  45006     previousLastIndex = JS_UNDEFINED;
  45007     str = JS_ToString(ctx, argv[0]);
  45008     if (JS_IsException(str))
  45009         goto exception;
  45010 
  45011     previousLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex);
  45012     if (JS_IsException(previousLastIndex))
  45013         goto exception;
  45014 
  45015     if (!js_same_value(ctx, previousLastIndex, JS_NewInt32(ctx, 0))) {
  45016         if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) {
  45017             goto exception;
  45018         }
  45019     }
  45020     result = JS_RegExpExec(ctx, rx, str);
  45021     if (JS_IsException(result))
  45022         goto exception;
  45023     currentLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex);
  45024     if (JS_IsException(currentLastIndex))
  45025         goto exception;
  45026     if (js_same_value(ctx, currentLastIndex, previousLastIndex)) {
  45027         JS_FreeValue(ctx, previousLastIndex);
  45028     } else {
  45029         if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, previousLastIndex) < 0) {
  45030             previousLastIndex = JS_UNDEFINED;
  45031             goto exception;
  45032         }
  45033     }
  45034     JS_FreeValue(ctx, str);
  45035     JS_FreeValue(ctx, currentLastIndex);
  45036 
  45037     if (JS_IsNull(result)) {
  45038         return JS_NewInt32(ctx, -1);
  45039     } else {
  45040         index = JS_GetProperty(ctx, result, JS_ATOM_index);
  45041         JS_FreeValue(ctx, result);
  45042         return index;
  45043     }
  45044 
  45045 exception:
  45046     JS_FreeValue(ctx, result);
  45047     JS_FreeValue(ctx, str);
  45048     JS_FreeValue(ctx, currentLastIndex);
  45049     JS_FreeValue(ctx, previousLastIndex);
  45050     return JS_EXCEPTION;
  45051 }
  45052 
  45053 static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val,
  45054                                        int argc, JSValueConst *argv)
  45055 {
  45056     // [Symbol.split](str, limit)
  45057     JSValueConst rx = this_val;
  45058     JSValueConst args[2];
  45059     JSValue str, ctor, splitter, A, flags, z, sub;
  45060     JSString *strp;
  45061     uint32_t lim, size, p, q;
  45062     int unicodeMatching;
  45063     int64_t lengthA, e, numberOfCaptures, i;
  45064 
  45065     if (!JS_IsObject(rx))
  45066         return JS_ThrowTypeErrorNotAnObject(ctx);
  45067 
  45068     ctor = JS_UNDEFINED;
  45069     splitter = JS_UNDEFINED;
  45070     A = JS_UNDEFINED;
  45071     flags = JS_UNDEFINED;
  45072     z = JS_UNDEFINED;
  45073     str = JS_ToString(ctx, argv[0]);
  45074     if (JS_IsException(str))
  45075         goto exception;
  45076     ctor = JS_SpeciesConstructor(ctx, rx, ctx->regexp_ctor);
  45077     if (JS_IsException(ctor))
  45078         goto exception;
  45079     flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_flags));
  45080     if (JS_IsException(flags))
  45081         goto exception;
  45082     strp = JS_VALUE_GET_STRING(flags);
  45083     unicodeMatching = string_indexof_char(strp, 'u', 0) >= 0;
  45084     if (string_indexof_char(strp, 'y', 0) < 0) {
  45085         flags = JS_ConcatString3(ctx, "", flags, "y");
  45086         if (JS_IsException(flags))
  45087             goto exception;
  45088     }
  45089     args[0] = rx;
  45090     args[1] = flags;
  45091     splitter = JS_CallConstructor(ctx, ctor, 2, args);
  45092     if (JS_IsException(splitter))
  45093         goto exception;
  45094     A = JS_NewArray(ctx);
  45095     if (JS_IsException(A))
  45096         goto exception;
  45097     lengthA = 0;
  45098     if (JS_IsUndefined(argv[1])) {
  45099         lim = 0xffffffff;
  45100     } else {
  45101         if (JS_ToUint32(ctx, &lim, argv[1]) < 0)
  45102             goto exception;
  45103         if (lim == 0)
  45104             goto done;
  45105     }
  45106     strp = JS_VALUE_GET_STRING(str);
  45107     p = q = 0;
  45108     size = strp->len;
  45109     if (size == 0) {
  45110         z = JS_RegExpExec(ctx, splitter, str);
  45111         if (JS_IsException(z))
  45112             goto exception;
  45113         if (JS_IsNull(z))
  45114             goto add_tail;
  45115         goto done;
  45116     }
  45117     while (q < size) {
  45118         if (JS_SetProperty(ctx, splitter, JS_ATOM_lastIndex, JS_NewInt32(ctx, q)) < 0)
  45119             goto exception;
  45120         JS_FreeValue(ctx, z);
  45121         z = JS_RegExpExec(ctx, splitter, str);
  45122         if (JS_IsException(z))
  45123             goto exception;
  45124         if (JS_IsNull(z)) {
  45125             q = string_advance_index(strp, q, unicodeMatching);
  45126         } else {
  45127             if (JS_ToLengthFree(ctx, &e, JS_GetProperty(ctx, splitter, JS_ATOM_lastIndex)))
  45128                 goto exception;
  45129             if (e > size)
  45130                 e = size;
  45131             if (e == p) {
  45132                 q = string_advance_index(strp, q, unicodeMatching);
  45133             } else {
  45134                 sub = js_sub_string(ctx, strp, p, q);
  45135                 if (JS_IsException(sub))
  45136                     goto exception;
  45137                 if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub,
  45138                                                 JS_PROP_C_W_E | JS_PROP_THROW) < 0)
  45139                     goto exception;
  45140                 if (lengthA == lim)
  45141                     goto done;
  45142                 p = e;
  45143                 if (js_get_length64(ctx, &numberOfCaptures, z))
  45144                     goto exception;
  45145                 for(i = 1; i < numberOfCaptures; i++) {
  45146                     sub = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, z, i));
  45147                     if (JS_IsException(sub))
  45148                         goto exception;
  45149                     if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0)
  45150                         goto exception;
  45151                     if (lengthA == lim)
  45152                         goto done;
  45153                 }
  45154                 q = p;
  45155             }
  45156         }
  45157     }
  45158 add_tail:
  45159     if (p > size)
  45160         p = size;
  45161     sub = js_sub_string(ctx, strp, p, size);
  45162     if (JS_IsException(sub))
  45163         goto exception;
  45164     if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0)
  45165         goto exception;
  45166     goto done;
  45167 exception:
  45168     JS_FreeValue(ctx, A);
  45169     A = JS_EXCEPTION;
  45170 done:
  45171     JS_FreeValue(ctx, str);
  45172     JS_FreeValue(ctx, ctor);
  45173     JS_FreeValue(ctx, splitter);
  45174     JS_FreeValue(ctx, flags);
  45175     JS_FreeValue(ctx, z);
  45176     return A;
  45177 }
  45178 
  45179 static const JSCFunctionListEntry js_regexp_funcs[] = {
  45180     JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
  45181     //JS_CFUNC_DEF("__RegExpExec", 2, js_regexp___RegExpExec ),
  45182     //JS_CFUNC_DEF("__RegExpDelete", 2, js_regexp___RegExpDelete ),
  45183 };
  45184 
  45185 static const JSCFunctionListEntry js_regexp_proto_funcs[] = {
  45186     JS_CGETSET_DEF("flags", js_regexp_get_flags, NULL ),
  45187     JS_CGETSET_DEF("source", js_regexp_get_source, NULL ),
  45188     JS_CGETSET_MAGIC_DEF("global", js_regexp_get_flag, NULL, LRE_FLAG_GLOBAL ),
  45189     JS_CGETSET_MAGIC_DEF("ignoreCase", js_regexp_get_flag, NULL, LRE_FLAG_IGNORECASE ),
  45190     JS_CGETSET_MAGIC_DEF("multiline", js_regexp_get_flag, NULL, LRE_FLAG_MULTILINE ),
  45191     JS_CGETSET_MAGIC_DEF("dotAll", js_regexp_get_flag, NULL, LRE_FLAG_DOTALL ),
  45192     JS_CGETSET_MAGIC_DEF("unicode", js_regexp_get_flag, NULL, LRE_FLAG_UNICODE ),
  45193     JS_CGETSET_MAGIC_DEF("sticky", js_regexp_get_flag, NULL, LRE_FLAG_STICKY ),
  45194     JS_CGETSET_MAGIC_DEF("hasIndices", js_regexp_get_flag, NULL, LRE_FLAG_INDICES ),
  45195     JS_CFUNC_DEF("exec", 1, js_regexp_exec ),
  45196     JS_CFUNC_DEF("compile", 2, js_regexp_compile ),
  45197     JS_CFUNC_DEF("test", 1, js_regexp_test ),
  45198     JS_CFUNC_DEF("toString", 0, js_regexp_toString ),
  45199     JS_CFUNC_DEF("[Symbol.replace]", 2, js_regexp_Symbol_replace ),
  45200     JS_CFUNC_DEF("[Symbol.match]", 1, js_regexp_Symbol_match ),
  45201     JS_CFUNC_DEF("[Symbol.matchAll]", 1, js_regexp_Symbol_matchAll ),
  45202     JS_CFUNC_DEF("[Symbol.search]", 1, js_regexp_Symbol_search ),
  45203     JS_CFUNC_DEF("[Symbol.split]", 2, js_regexp_Symbol_split ),
  45204     //JS_CGETSET_DEF("__source", js_regexp_get___source, NULL ),
  45205     //JS_CGETSET_DEF("__flags", js_regexp_get___flags, NULL ),
  45206 };
  45207 
  45208 static const JSCFunctionListEntry js_regexp_string_iterator_proto_funcs[] = {
  45209     JS_ITERATOR_NEXT_DEF("next", 0, js_regexp_string_iterator_next, 0 ),
  45210     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "RegExp String Iterator", JS_PROP_CONFIGURABLE ),
  45211 };
  45212 
  45213 void JS_AddIntrinsicRegExpCompiler(JSContext *ctx)
  45214 {
  45215     ctx->compile_regexp = js_compile_regexp;
  45216 }
  45217 
  45218 void JS_AddIntrinsicRegExp(JSContext *ctx)
  45219 {
  45220     JSValueConst obj;
  45221 
  45222     JS_AddIntrinsicRegExpCompiler(ctx);
  45223 
  45224     ctx->class_proto[JS_CLASS_REGEXP] = JS_NewObject(ctx);
  45225     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP], js_regexp_proto_funcs,
  45226                                countof(js_regexp_proto_funcs));
  45227     obj = JS_NewGlobalCConstructor(ctx, "RegExp", js_regexp_constructor, 2,
  45228                                    ctx->class_proto[JS_CLASS_REGEXP]);
  45229     ctx->regexp_ctor = JS_DupValue(ctx, obj);
  45230     JS_SetPropertyFunctionList(ctx, obj, js_regexp_funcs, countof(js_regexp_funcs));
  45231 
  45232     ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR] =
  45233         JS_NewObjectProto(ctx, ctx->iterator_proto);
  45234     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR],
  45235                                js_regexp_string_iterator_proto_funcs,
  45236                                countof(js_regexp_string_iterator_proto_funcs));
  45237 }
  45238 
  45239 /* JSON */
  45240 
  45241 static int json_parse_expect(JSParseState *s, int tok)
  45242 {
  45243     if (s->token.val != tok) {
  45244         /* XXX: dump token correctly in all cases */
  45245         return js_parse_error(s, "expecting '%c'", tok);
  45246     }
  45247     return json_next_token(s);
  45248 }
  45249 
  45250 static JSValue json_parse_value(JSParseState *s)
  45251 {
  45252     JSContext *ctx = s->ctx;
  45253     JSValue val = JS_NULL;
  45254     int ret;
  45255 
  45256     switch(s->token.val) {
  45257     case '{':
  45258         {
  45259             JSValue prop_val;
  45260             JSAtom prop_name;
  45261 
  45262             if (json_next_token(s))
  45263                 goto fail;
  45264             val = JS_NewObject(ctx);
  45265             if (JS_IsException(val))
  45266                 goto fail;
  45267             if (s->token.val != '}') {
  45268                 for(;;) {
  45269                     if (s->token.val == TOK_STRING) {
  45270                         prop_name = JS_ValueToAtom(ctx, s->token.u.str.str);
  45271                         if (prop_name == JS_ATOM_NULL)
  45272                             goto fail;
  45273                     } else if (s->ext_json && s->token.val == TOK_IDENT) {
  45274                         prop_name = JS_DupAtom(ctx, s->token.u.ident.atom);
  45275                     } else {
  45276                         js_parse_error(s, "expecting property name");
  45277                         goto fail;
  45278                     }
  45279                     if (json_next_token(s))
  45280                         goto fail1;
  45281                     if (json_parse_expect(s, ':'))
  45282                         goto fail1;
  45283                     prop_val = json_parse_value(s);
  45284                     if (JS_IsException(prop_val)) {
  45285                     fail1:
  45286                         JS_FreeAtom(ctx, prop_name);
  45287                         goto fail;
  45288                     }
  45289                     ret = JS_DefinePropertyValue(ctx, val, prop_name,
  45290                                                  prop_val, JS_PROP_C_W_E);
  45291                     JS_FreeAtom(ctx, prop_name);
  45292                     if (ret < 0)
  45293                         goto fail;
  45294 
  45295                     if (s->token.val != ',')
  45296                         break;
  45297                     if (json_next_token(s))
  45298                         goto fail;
  45299                     if (s->ext_json && s->token.val == '}')
  45300                         break;
  45301                 }
  45302             }
  45303             if (json_parse_expect(s, '}'))
  45304                 goto fail;
  45305         }
  45306         break;
  45307     case '[':
  45308         {
  45309             JSValue el;
  45310             uint32_t idx;
  45311 
  45312             if (json_next_token(s))
  45313                 goto fail;
  45314             val = JS_NewArray(ctx);
  45315             if (JS_IsException(val))
  45316                 goto fail;
  45317             if (s->token.val != ']') {
  45318                 idx = 0;
  45319                 for(;;) {
  45320                     el = json_parse_value(s);
  45321                     if (JS_IsException(el))
  45322                         goto fail;
  45323                     ret = JS_DefinePropertyValueUint32(ctx, val, idx, el, JS_PROP_C_W_E);
  45324                     if (ret < 0)
  45325                         goto fail;
  45326                     if (s->token.val != ',')
  45327                         break;
  45328                     if (json_next_token(s))
  45329                         goto fail;
  45330                     idx++;
  45331                     if (s->ext_json && s->token.val == ']')
  45332                         break;
  45333                 }
  45334             }
  45335             if (json_parse_expect(s, ']'))
  45336                 goto fail;
  45337         }
  45338         break;
  45339     case TOK_STRING:
  45340         val = JS_DupValue(ctx, s->token.u.str.str);
  45341         if (json_next_token(s))
  45342             goto fail;
  45343         break;
  45344     case TOK_NUMBER:
  45345         val = s->token.u.num.val;
  45346         if (json_next_token(s))
  45347             goto fail;
  45348         break;
  45349     case TOK_IDENT:
  45350         if (s->token.u.ident.atom == JS_ATOM_false ||
  45351             s->token.u.ident.atom == JS_ATOM_true) {
  45352             val = JS_NewBool(ctx, s->token.u.ident.atom == JS_ATOM_true);
  45353         } else if (s->token.u.ident.atom == JS_ATOM_null) {
  45354             val = JS_NULL;
  45355         } else {
  45356             goto def_token;
  45357         }
  45358         if (json_next_token(s))
  45359             goto fail;
  45360         break;
  45361     default:
  45362     def_token:
  45363         if (s->token.val == TOK_EOF) {
  45364             js_parse_error(s, "Unexpected end of JSON input");
  45365         } else {
  45366             js_parse_error(s, "unexpected token: '%.*s'",
  45367                            (int)(s->buf_ptr - s->token.ptr), s->token.ptr);
  45368         }
  45369         goto fail;
  45370     }
  45371     return val;
  45372  fail:
  45373     JS_FreeValue(ctx, val);
  45374     return JS_EXCEPTION;
  45375 }
  45376 
  45377 JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len,
  45378                       const char *filename, int flags)
  45379 {
  45380     JSParseState s1, *s = &s1;
  45381     JSValue val = JS_UNDEFINED;
  45382 
  45383     js_parse_init(ctx, s, buf, buf_len, filename);
  45384     s->ext_json = ((flags & JS_PARSE_JSON_EXT) != 0);
  45385     if (json_next_token(s))
  45386         goto fail;
  45387     val = json_parse_value(s);
  45388     if (JS_IsException(val))
  45389         goto fail;
  45390     if (s->token.val != TOK_EOF) {
  45391         if (js_parse_error(s, "unexpected data at the end"))
  45392             goto fail;
  45393     }
  45394     return val;
  45395  fail:
  45396     JS_FreeValue(ctx, val);
  45397     free_token(s, &s->token);
  45398     return JS_EXCEPTION;
  45399 }
  45400 
  45401 JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len,
  45402                      const char *filename)
  45403 {
  45404     return JS_ParseJSON2(ctx, buf, buf_len, filename, 0);
  45405 }
  45406 
  45407 static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder,
  45408                                          JSAtom name, JSValueConst reviver)
  45409 {
  45410     JSValue val, new_el, name_val, res;
  45411     JSValueConst args[2];
  45412     int ret, is_array;
  45413     uint32_t i, len = 0;
  45414     JSAtom prop;
  45415     JSPropertyEnum *atoms = NULL;
  45416 
  45417     if (js_check_stack_overflow(ctx->rt, 0)) {
  45418         return JS_ThrowStackOverflow(ctx);
  45419     }
  45420 
  45421     val = JS_GetProperty(ctx, holder, name);
  45422     if (JS_IsException(val))
  45423         return val;
  45424     if (JS_IsObject(val)) {
  45425         is_array = JS_IsArray(ctx, val);
  45426         if (is_array < 0)
  45427             goto fail;
  45428         if (is_array) {
  45429             if (js_get_length32(ctx, &len, val))
  45430                 goto fail;
  45431         } else {
  45432             ret = JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, JS_VALUE_GET_OBJ(val), JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK);
  45433             if (ret < 0)
  45434                 goto fail;
  45435         }
  45436         for(i = 0; i < len; i++) {
  45437             if (is_array) {
  45438                 prop = JS_NewAtomUInt32(ctx, i);
  45439                 if (prop == JS_ATOM_NULL)
  45440                     goto fail;
  45441             } else {
  45442                 prop = JS_DupAtom(ctx, atoms[i].atom);
  45443             }
  45444             new_el = internalize_json_property(ctx, val, prop, reviver);
  45445             if (JS_IsException(new_el)) {
  45446                 JS_FreeAtom(ctx, prop);
  45447                 goto fail;
  45448             }
  45449             if (JS_IsUndefined(new_el)) {
  45450                 ret = JS_DeleteProperty(ctx, val, prop, 0);
  45451             } else {
  45452                 ret = JS_DefinePropertyValue(ctx, val, prop, new_el, JS_PROP_C_W_E);
  45453             }
  45454             JS_FreeAtom(ctx, prop);
  45455             if (ret < 0)
  45456                 goto fail;
  45457         }
  45458     }
  45459     js_free_prop_enum(ctx, atoms, len);
  45460     atoms = NULL;
  45461     name_val = JS_AtomToValue(ctx, name);
  45462     if (JS_IsException(name_val))
  45463         goto fail;
  45464     args[0] = name_val;
  45465     args[1] = val;
  45466     res = JS_Call(ctx, reviver, holder, 2, args);
  45467     JS_FreeValue(ctx, name_val);
  45468     JS_FreeValue(ctx, val);
  45469     return res;
  45470  fail:
  45471     js_free_prop_enum(ctx, atoms, len);
  45472     JS_FreeValue(ctx, val);
  45473     return JS_EXCEPTION;
  45474 }
  45475 
  45476 static JSValue js_json_parse(JSContext *ctx, JSValueConst this_val,
  45477                              int argc, JSValueConst *argv)
  45478 {
  45479     JSValue obj, root;
  45480     JSValueConst reviver;
  45481     const char *str;
  45482     size_t len;
  45483 
  45484     str = JS_ToCStringLen(ctx, &len, argv[0]);
  45485     if (!str)
  45486         return JS_EXCEPTION;
  45487     obj = JS_ParseJSON(ctx, str, len, "<input>");
  45488     JS_FreeCString(ctx, str);
  45489     if (JS_IsException(obj))
  45490         return obj;
  45491     if (argc > 1 && JS_IsFunction(ctx, argv[1])) {
  45492         reviver = argv[1];
  45493         root = JS_NewObject(ctx);
  45494         if (JS_IsException(root)) {
  45495             JS_FreeValue(ctx, obj);
  45496             return JS_EXCEPTION;
  45497         }
  45498         if (JS_DefinePropertyValue(ctx, root, JS_ATOM_empty_string, obj,
  45499                                    JS_PROP_C_W_E) < 0) {
  45500             JS_FreeValue(ctx, root);
  45501             return JS_EXCEPTION;
  45502         }
  45503         obj = internalize_json_property(ctx, root, JS_ATOM_empty_string,
  45504                                         reviver);
  45505         JS_FreeValue(ctx, root);
  45506     }
  45507     return obj;
  45508 }
  45509 
  45510 typedef struct JSONStringifyContext {
  45511     JSValueConst replacer_func;
  45512     JSValue stack;
  45513     JSValue property_list;
  45514     JSValue gap;
  45515     JSValue empty;
  45516     StringBuffer *b;
  45517 } JSONStringifyContext;
  45518 
  45519 static JSValue JS_ToQuotedStringFree(JSContext *ctx, JSValue val) {
  45520     JSValue r = JS_ToQuotedString(ctx, val);
  45521     JS_FreeValue(ctx, val);
  45522     return r;
  45523 }
  45524 
  45525 static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc,
  45526                              JSValueConst holder, JSValue val, JSValueConst key)
  45527 {
  45528     JSValue v;
  45529     JSValueConst args[2];
  45530 
  45531     /* check for object.toJSON method */
  45532     /* ECMA specifies this is done only for Object and BigInt */
  45533     /* we do it for BigFloat and BigDecimal as an extension */
  45534     if (JS_IsObject(val) || JS_IsBigInt(ctx, val)
  45535 #ifdef CONFIG_BIGNUM
  45536     ||  JS_IsBigFloat(val) || JS_IsBigDecimal(val)
  45537 #endif
  45538         ) {
  45539         JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON);
  45540         if (JS_IsException(f))
  45541             goto exception;
  45542         if (JS_IsFunction(ctx, f)) {
  45543             v = JS_CallFree(ctx, f, val, 1, &key);
  45544             JS_FreeValue(ctx, val);
  45545             val = v;
  45546             if (JS_IsException(val))
  45547                 goto exception;
  45548         } else {
  45549             JS_FreeValue(ctx, f);
  45550         }
  45551     }
  45552 
  45553     if (!JS_IsUndefined(jsc->replacer_func)) {
  45554         args[0] = key;
  45555         args[1] = val;
  45556         v = JS_Call(ctx, jsc->replacer_func, holder, 2, args);
  45557         JS_FreeValue(ctx, val);
  45558         val = v;
  45559         if (JS_IsException(val))
  45560             goto exception;
  45561     }
  45562 
  45563     switch (JS_VALUE_GET_NORM_TAG(val)) {
  45564     case JS_TAG_OBJECT:
  45565         if (JS_IsFunction(ctx, val))
  45566             break;
  45567     case JS_TAG_STRING:
  45568     case JS_TAG_INT:
  45569     case JS_TAG_FLOAT64:
  45570     case JS_TAG_BOOL:
  45571     case JS_TAG_NULL:
  45572     case JS_TAG_BIG_INT:
  45573 #ifdef CONFIG_BIGNUM
  45574     case JS_TAG_BIG_FLOAT:
  45575     case JS_TAG_BIG_DECIMAL:
  45576 #endif
  45577     case JS_TAG_EXCEPTION:
  45578         return val;
  45579     default:
  45580         break;
  45581     }
  45582     JS_FreeValue(ctx, val);
  45583     return JS_UNDEFINED;
  45584 
  45585 exception:
  45586     JS_FreeValue(ctx, val);
  45587     return JS_EXCEPTION;
  45588 }
  45589 
  45590 static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
  45591                           JSValueConst holder, JSValue val,
  45592                           JSValueConst indent)
  45593 {
  45594     JSValue indent1, sep, sep1, tab, v, prop;
  45595     JSObject *p;
  45596     int64_t i, len;
  45597     int cl, ret;
  45598     BOOL has_content;
  45599 
  45600     indent1 = JS_UNDEFINED;
  45601     sep = JS_UNDEFINED;
  45602     sep1 = JS_UNDEFINED;
  45603     tab = JS_UNDEFINED;
  45604     prop = JS_UNDEFINED;
  45605 
  45606     if (JS_IsObject(val)) {
  45607         p = JS_VALUE_GET_OBJ(val);
  45608         cl = p->class_id;
  45609         if (cl == JS_CLASS_STRING) {
  45610             val = JS_ToStringFree(ctx, val);
  45611             if (JS_IsException(val))
  45612                 goto exception;
  45613             goto concat_primitive;
  45614         } else if (cl == JS_CLASS_NUMBER) {
  45615             val = JS_ToNumberFree(ctx, val);
  45616             if (JS_IsException(val))
  45617                 goto exception;
  45618             goto concat_primitive;
  45619         } else if (cl == JS_CLASS_BOOLEAN || cl == JS_CLASS_BIG_INT
  45620 #ifdef CONFIG_BIGNUM
  45621                || cl == JS_CLASS_BIG_FLOAT
  45622                || cl == JS_CLASS_BIG_DECIMAL
  45623 #endif
  45624                    )
  45625         {
  45626             /* This will thow the same error as for the primitive object */
  45627             set_value(ctx, &val, JS_DupValue(ctx, p->u.object_data));
  45628             goto concat_primitive;
  45629         }
  45630         v = js_array_includes(ctx, jsc->stack, 1, (JSValueConst *)&val);
  45631         if (JS_IsException(v))
  45632             goto exception;
  45633         if (JS_ToBoolFree(ctx, v)) {
  45634             JS_ThrowTypeError(ctx, "circular reference");
  45635             goto exception;
  45636         }
  45637         indent1 = JS_ConcatString(ctx, JS_DupValue(ctx, indent), JS_DupValue(ctx, jsc->gap));
  45638         if (JS_IsException(indent1))
  45639             goto exception;
  45640         if (!JS_IsEmptyString(jsc->gap)) {
  45641             sep = JS_ConcatString3(ctx, "\n", JS_DupValue(ctx, indent1), "");
  45642             if (JS_IsException(sep))
  45643                 goto exception;
  45644             sep1 = JS_NewString(ctx, " ");
  45645             if (JS_IsException(sep1))
  45646                 goto exception;
  45647         } else {
  45648             sep = JS_DupValue(ctx, jsc->empty);
  45649             sep1 = JS_DupValue(ctx, jsc->empty);
  45650         }
  45651         v = js_array_push(ctx, jsc->stack, 1, (JSValueConst *)&val, 0);
  45652         if (check_exception_free(ctx, v))
  45653             goto exception;
  45654         ret = JS_IsArray(ctx, val);
  45655         if (ret < 0)
  45656             goto exception;
  45657         if (ret) {
  45658             if (js_get_length64(ctx, &len, val))
  45659                 goto exception;
  45660             string_buffer_putc8(jsc->b, '[');
  45661             for(i = 0; i < len; i++) {
  45662                 if (i > 0)
  45663                     string_buffer_putc8(jsc->b, ',');
  45664                 string_buffer_concat_value(jsc->b, sep);
  45665                 v = JS_GetPropertyInt64(ctx, val, i);
  45666                 if (JS_IsException(v))
  45667                     goto exception;
  45668                 /* XXX: could do this string conversion only when needed */
  45669                 prop = JS_ToStringFree(ctx, JS_NewInt64(ctx, i));
  45670                 if (JS_IsException(prop))
  45671                     goto exception;
  45672                 v = js_json_check(ctx, jsc, val, v, prop);
  45673                 JS_FreeValue(ctx, prop);
  45674                 prop = JS_UNDEFINED;
  45675                 if (JS_IsException(v))
  45676                     goto exception;
  45677                 if (JS_IsUndefined(v))
  45678                     v = JS_NULL;
  45679                 if (js_json_to_str(ctx, jsc, val, v, indent1))
  45680                     goto exception;
  45681             }
  45682             if (len > 0 && !JS_IsEmptyString(jsc->gap)) {
  45683                 string_buffer_putc8(jsc->b, '\n');
  45684                 string_buffer_concat_value(jsc->b, indent);
  45685             }
  45686             string_buffer_putc8(jsc->b, ']');
  45687         } else {
  45688             if (!JS_IsUndefined(jsc->property_list))
  45689                 tab = JS_DupValue(ctx, jsc->property_list);
  45690             else
  45691                 tab = js_object_keys(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val, JS_ITERATOR_KIND_KEY);
  45692             if (JS_IsException(tab))
  45693                 goto exception;
  45694             if (js_get_length64(ctx, &len, tab))
  45695                 goto exception;
  45696             string_buffer_putc8(jsc->b, '{');
  45697             has_content = FALSE;
  45698             for(i = 0; i < len; i++) {
  45699                 JS_FreeValue(ctx, prop);
  45700                 prop = JS_GetPropertyInt64(ctx, tab, i);
  45701                 if (JS_IsException(prop))
  45702                     goto exception;
  45703                 v = JS_GetPropertyValue(ctx, val, JS_DupValue(ctx, prop));
  45704                 if (JS_IsException(v))
  45705                     goto exception;
  45706                 v = js_json_check(ctx, jsc, val, v, prop);
  45707                 if (JS_IsException(v))
  45708                     goto exception;
  45709                 if (!JS_IsUndefined(v)) {
  45710                     if (has_content)
  45711                         string_buffer_putc8(jsc->b, ',');
  45712                     prop = JS_ToQuotedStringFree(ctx, prop);
  45713                     if (JS_IsException(prop)) {
  45714                         JS_FreeValue(ctx, v);
  45715                         goto exception;
  45716                     }
  45717                     string_buffer_concat_value(jsc->b, sep);
  45718                     string_buffer_concat_value(jsc->b, prop);
  45719                     string_buffer_putc8(jsc->b, ':');
  45720                     string_buffer_concat_value(jsc->b, sep1);
  45721                     if (js_json_to_str(ctx, jsc, val, v, indent1))
  45722                         goto exception;
  45723                     has_content = TRUE;
  45724                 }
  45725             }
  45726             if (has_content && !JS_IsEmptyString(jsc->gap)) {
  45727                 string_buffer_putc8(jsc->b, '\n');
  45728                 string_buffer_concat_value(jsc->b, indent);
  45729             }
  45730             string_buffer_putc8(jsc->b, '}');
  45731         }
  45732         if (check_exception_free(ctx, js_array_pop(ctx, jsc->stack, 0, NULL, 0)))
  45733             goto exception;
  45734         JS_FreeValue(ctx, val);
  45735         JS_FreeValue(ctx, tab);
  45736         JS_FreeValue(ctx, sep);
  45737         JS_FreeValue(ctx, sep1);
  45738         JS_FreeValue(ctx, indent1);
  45739         JS_FreeValue(ctx, prop);
  45740         return 0;
  45741     }
  45742  concat_primitive:
  45743     switch (JS_VALUE_GET_NORM_TAG(val)) {
  45744     case JS_TAG_STRING:
  45745         val = JS_ToQuotedStringFree(ctx, val);
  45746         if (JS_IsException(val))
  45747             goto exception;
  45748         goto concat_value;
  45749     case JS_TAG_FLOAT64:
  45750         if (!isfinite(JS_VALUE_GET_FLOAT64(val))) {
  45751             val = JS_NULL;
  45752         }
  45753         goto concat_value;
  45754     case JS_TAG_INT:
  45755     case JS_TAG_BOOL:
  45756     case JS_TAG_NULL:
  45757     concat_value:
  45758         return string_buffer_concat_value_free(jsc->b, val);
  45759     case JS_TAG_BIG_INT:
  45760 #ifdef CONFIG_BIGNUM
  45761     case JS_TAG_BIG_FLOAT:
  45762     case JS_TAG_BIG_DECIMAL:
  45763 #endif
  45764         /* reject big numbers: use toJSON method to override */
  45765         JS_ThrowTypeError(ctx, "Do not know how to serialize a BigInt");
  45766         goto exception;
  45767     default:
  45768         JS_FreeValue(ctx, val);
  45769         return 0;
  45770     }
  45771 
  45772 exception:
  45773     JS_FreeValue(ctx, val);
  45774     JS_FreeValue(ctx, tab);
  45775     JS_FreeValue(ctx, sep);
  45776     JS_FreeValue(ctx, sep1);
  45777     JS_FreeValue(ctx, indent1);
  45778     JS_FreeValue(ctx, prop);
  45779     return -1;
  45780 }
  45781 
  45782 JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj,
  45783                          JSValueConst replacer, JSValueConst space0)
  45784 {
  45785     StringBuffer b_s;
  45786     JSONStringifyContext jsc_s, *jsc = &jsc_s;
  45787     JSValue val, v, space, ret, wrapper;
  45788     int res;
  45789     int64_t i, j, n;
  45790 
  45791     jsc->replacer_func = JS_UNDEFINED;
  45792     jsc->stack = JS_UNDEFINED;
  45793     jsc->property_list = JS_UNDEFINED;
  45794     jsc->gap = JS_UNDEFINED;
  45795     jsc->b = &b_s;
  45796     jsc->empty = JS_AtomToString(ctx, JS_ATOM_empty_string);
  45797     ret = JS_UNDEFINED;
  45798     wrapper = JS_UNDEFINED;
  45799 
  45800     string_buffer_init(ctx, jsc->b, 0);
  45801     jsc->stack = JS_NewArray(ctx);
  45802     if (JS_IsException(jsc->stack))
  45803         goto exception;
  45804     if (JS_IsFunction(ctx, replacer)) {
  45805         jsc->replacer_func = replacer;
  45806     } else {
  45807         res = JS_IsArray(ctx, replacer);
  45808         if (res < 0)
  45809             goto exception;
  45810         if (res) {
  45811             /* XXX: enumeration is not fully correct */
  45812             jsc->property_list = JS_NewArray(ctx);
  45813             if (JS_IsException(jsc->property_list))
  45814                 goto exception;
  45815             if (js_get_length64(ctx, &n, replacer))
  45816                 goto exception;
  45817             for (i = j = 0; i < n; i++) {
  45818                 JSValue present;
  45819                 v = JS_GetPropertyInt64(ctx, replacer, i);
  45820                 if (JS_IsException(v))
  45821                     goto exception;
  45822                 if (JS_IsObject(v)) {
  45823                     JSObject *p = JS_VALUE_GET_OBJ(v);
  45824                     if (p->class_id == JS_CLASS_STRING ||
  45825                         p->class_id == JS_CLASS_NUMBER) {
  45826                         v = JS_ToStringFree(ctx, v);
  45827                         if (JS_IsException(v))
  45828                             goto exception;
  45829                     } else {
  45830                         JS_FreeValue(ctx, v);
  45831                         continue;
  45832                     }
  45833                 } else if (JS_IsNumber(v)) {
  45834                     v = JS_ToStringFree(ctx, v);
  45835                     if (JS_IsException(v))
  45836                         goto exception;
  45837                 } else if (!JS_IsString(v)) {
  45838                     JS_FreeValue(ctx, v);
  45839                     continue;
  45840                 }
  45841                 present = js_array_includes(ctx, jsc->property_list,
  45842                                             1, (JSValueConst *)&v);
  45843                 if (JS_IsException(present)) {
  45844                     JS_FreeValue(ctx, v);
  45845                     goto exception;
  45846                 }
  45847                 if (!JS_ToBoolFree(ctx, present)) {
  45848                     JS_SetPropertyInt64(ctx, jsc->property_list, j++, v);
  45849                 } else {
  45850                     JS_FreeValue(ctx, v);
  45851                 }
  45852             }
  45853         }
  45854     }
  45855     space = JS_DupValue(ctx, space0);
  45856     if (JS_IsObject(space)) {
  45857         JSObject *p = JS_VALUE_GET_OBJ(space);
  45858         if (p->class_id == JS_CLASS_NUMBER) {
  45859             space = JS_ToNumberFree(ctx, space);
  45860         } else if (p->class_id == JS_CLASS_STRING) {
  45861             space = JS_ToStringFree(ctx, space);
  45862         }
  45863         if (JS_IsException(space)) {
  45864             JS_FreeValue(ctx, space);
  45865             goto exception;
  45866         }
  45867     }
  45868     if (JS_IsNumber(space)) {
  45869         int n;
  45870         if (JS_ToInt32Clamp(ctx, &n, space, 0, 10, 0))
  45871             goto exception;
  45872         jsc->gap = JS_NewStringLen(ctx, "          ", n);
  45873     } else if (JS_IsString(space)) {
  45874         JSString *p = JS_VALUE_GET_STRING(space);
  45875         jsc->gap = js_sub_string(ctx, p, 0, min_int(p->len, 10));
  45876     } else {
  45877         jsc->gap = JS_DupValue(ctx, jsc->empty);
  45878     }
  45879     JS_FreeValue(ctx, space);
  45880     if (JS_IsException(jsc->gap))
  45881         goto exception;
  45882     wrapper = JS_NewObject(ctx);
  45883     if (JS_IsException(wrapper))
  45884         goto exception;
  45885     if (JS_DefinePropertyValue(ctx, wrapper, JS_ATOM_empty_string,
  45886                                JS_DupValue(ctx, obj), JS_PROP_C_W_E) < 0)
  45887         goto exception;
  45888     val = JS_DupValue(ctx, obj);
  45889 
  45890     val = js_json_check(ctx, jsc, wrapper, val, jsc->empty);
  45891     if (JS_IsException(val))
  45892         goto exception;
  45893     if (JS_IsUndefined(val)) {
  45894         ret = JS_UNDEFINED;
  45895         goto done1;
  45896     }
  45897     if (js_json_to_str(ctx, jsc, wrapper, val, jsc->empty))
  45898         goto exception;
  45899 
  45900     ret = string_buffer_end(jsc->b);
  45901     goto done;
  45902 
  45903 exception:
  45904     ret = JS_EXCEPTION;
  45905 done1:
  45906     string_buffer_free(jsc->b);
  45907 done:
  45908     JS_FreeValue(ctx, wrapper);
  45909     JS_FreeValue(ctx, jsc->empty);
  45910     JS_FreeValue(ctx, jsc->gap);
  45911     JS_FreeValue(ctx, jsc->property_list);
  45912     JS_FreeValue(ctx, jsc->stack);
  45913     return ret;
  45914 }
  45915 
  45916 static JSValue js_json_stringify(JSContext *ctx, JSValueConst this_val,
  45917                                  int argc, JSValueConst *argv)
  45918 {
  45919     // stringify(val, replacer, space)
  45920     return JS_JSONStringify(ctx, argv[0], argv[1], argv[2]);
  45921 }
  45922 
  45923 static const JSCFunctionListEntry js_json_funcs[] = {
  45924     JS_CFUNC_DEF("parse", 2, js_json_parse ),
  45925     JS_CFUNC_DEF("stringify", 3, js_json_stringify ),
  45926     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "JSON", JS_PROP_CONFIGURABLE ),
  45927 };
  45928 
  45929 static const JSCFunctionListEntry js_json_obj[] = {
  45930     JS_OBJECT_DEF("JSON", js_json_funcs, countof(js_json_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
  45931 };
  45932 
  45933 void JS_AddIntrinsicJSON(JSContext *ctx)
  45934 {
  45935     /* add JSON as autoinit object */
  45936     JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_json_obj, countof(js_json_obj));
  45937 }
  45938 
  45939 /* Reflect */
  45940 
  45941 static JSValue js_reflect_apply(JSContext *ctx, JSValueConst this_val,
  45942                                 int argc, JSValueConst *argv)
  45943 {
  45944     return js_function_apply(ctx, argv[0], max_int(0, argc - 1), argv + 1, 2);
  45945 }
  45946 
  45947 static JSValue js_reflect_construct(JSContext *ctx, JSValueConst this_val,
  45948                                     int argc, JSValueConst *argv)
  45949 {
  45950     JSValueConst func, array_arg, new_target;
  45951     JSValue *tab, ret;
  45952     uint32_t len;
  45953 
  45954     func = argv[0];
  45955     array_arg = argv[1];
  45956     if (argc > 2) {
  45957         new_target = argv[2];
  45958         if (!JS_IsConstructor(ctx, new_target))
  45959             return JS_ThrowTypeError(ctx, "not a constructor");
  45960     } else {
  45961         new_target = func;
  45962     }
  45963     tab = build_arg_list(ctx, &len, array_arg);
  45964     if (!tab)
  45965         return JS_EXCEPTION;
  45966     ret = JS_CallConstructor2(ctx, func, new_target, len, (JSValueConst *)tab);
  45967     free_arg_list(ctx, tab, len);
  45968     return ret;
  45969 }
  45970 
  45971 static JSValue js_reflect_deleteProperty(JSContext *ctx, JSValueConst this_val,
  45972                                          int argc, JSValueConst *argv)
  45973 {
  45974     JSValueConst obj;
  45975     JSAtom atom;
  45976     int ret;
  45977 
  45978     obj = argv[0];
  45979     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
  45980         return JS_ThrowTypeErrorNotAnObject(ctx);
  45981     atom = JS_ValueToAtom(ctx, argv[1]);
  45982     if (unlikely(atom == JS_ATOM_NULL))
  45983         return JS_EXCEPTION;
  45984     ret = JS_DeleteProperty(ctx, obj, atom, 0);
  45985     JS_FreeAtom(ctx, atom);
  45986     if (ret < 0)
  45987         return JS_EXCEPTION;
  45988     else
  45989         return JS_NewBool(ctx, ret);
  45990 }
  45991 
  45992 static JSValue js_reflect_get(JSContext *ctx, JSValueConst this_val,
  45993                               int argc, JSValueConst *argv)
  45994 {
  45995     JSValueConst obj, prop, receiver;
  45996     JSAtom atom;
  45997     JSValue ret;
  45998 
  45999     obj = argv[0];
  46000     prop = argv[1];
  46001     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
  46002         return JS_ThrowTypeErrorNotAnObject(ctx);
  46003     if (argc > 2)
  46004         receiver = argv[2];
  46005     else
  46006         receiver = obj;
  46007     atom = JS_ValueToAtom(ctx, prop);
  46008     if (unlikely(atom == JS_ATOM_NULL))
  46009         return JS_EXCEPTION;
  46010     ret = JS_GetPropertyInternal(ctx, obj, atom, receiver, FALSE);
  46011     JS_FreeAtom(ctx, atom);
  46012     return ret;
  46013 }
  46014 
  46015 static JSValue js_reflect_has(JSContext *ctx, JSValueConst this_val,
  46016                               int argc, JSValueConst *argv)
  46017 {
  46018     JSValueConst obj, prop;
  46019     JSAtom atom;
  46020     int ret;
  46021 
  46022     obj = argv[0];
  46023     prop = argv[1];
  46024     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
  46025         return JS_ThrowTypeErrorNotAnObject(ctx);
  46026     atom = JS_ValueToAtom(ctx, prop);
  46027     if (unlikely(atom == JS_ATOM_NULL))
  46028         return JS_EXCEPTION;
  46029     ret = JS_HasProperty(ctx, obj, atom);
  46030     JS_FreeAtom(ctx, atom);
  46031     if (ret < 0)
  46032         return JS_EXCEPTION;
  46033     else
  46034         return JS_NewBool(ctx, ret);
  46035 }
  46036 
  46037 static JSValue js_reflect_set(JSContext *ctx, JSValueConst this_val,
  46038                               int argc, JSValueConst *argv)
  46039 {
  46040     JSValueConst obj, prop, val, receiver;
  46041     int ret;
  46042     JSAtom atom;
  46043 
  46044     obj = argv[0];
  46045     prop = argv[1];
  46046     val = argv[2];
  46047     if (argc > 3)
  46048         receiver = argv[3];
  46049     else
  46050         receiver = obj;
  46051     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
  46052         return JS_ThrowTypeErrorNotAnObject(ctx);
  46053     atom = JS_ValueToAtom(ctx, prop);
  46054     if (unlikely(atom == JS_ATOM_NULL))
  46055         return JS_EXCEPTION;
  46056     ret = JS_SetPropertyInternal(ctx, obj, atom,
  46057                                  JS_DupValue(ctx, val), receiver, 0);
  46058     JS_FreeAtom(ctx, atom);
  46059     if (ret < 0)
  46060         return JS_EXCEPTION;
  46061     else
  46062         return JS_NewBool(ctx, ret);
  46063 }
  46064 
  46065 static JSValue js_reflect_setPrototypeOf(JSContext *ctx, JSValueConst this_val,
  46066                                          int argc, JSValueConst *argv)
  46067 {
  46068     int ret;
  46069     ret = JS_SetPrototypeInternal(ctx, argv[0], argv[1], FALSE);
  46070     if (ret < 0)
  46071         return JS_EXCEPTION;
  46072     else
  46073         return JS_NewBool(ctx, ret);
  46074 }
  46075 
  46076 static JSValue js_reflect_ownKeys(JSContext *ctx, JSValueConst this_val,
  46077                                   int argc, JSValueConst *argv)
  46078 {
  46079     if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT)
  46080         return JS_ThrowTypeErrorNotAnObject(ctx);
  46081     return JS_GetOwnPropertyNames2(ctx, argv[0],
  46082                                    JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK,
  46083                                    JS_ITERATOR_KIND_KEY);
  46084 }
  46085 
  46086 static const JSCFunctionListEntry js_reflect_funcs[] = {
  46087     JS_CFUNC_DEF("apply", 3, js_reflect_apply ),
  46088     JS_CFUNC_DEF("construct", 2, js_reflect_construct ),
  46089     JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 1 ),
  46090     JS_CFUNC_DEF("deleteProperty", 2, js_reflect_deleteProperty ),
  46091     JS_CFUNC_DEF("get", 2, js_reflect_get ),
  46092     JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 1 ),
  46093     JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 1 ),
  46094     JS_CFUNC_DEF("has", 2, js_reflect_has ),
  46095     JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 1 ),
  46096     JS_CFUNC_DEF("ownKeys", 1, js_reflect_ownKeys ),
  46097     JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 1 ),
  46098     JS_CFUNC_DEF("set", 3, js_reflect_set ),
  46099     JS_CFUNC_DEF("setPrototypeOf", 2, js_reflect_setPrototypeOf ),
  46100     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Reflect", JS_PROP_CONFIGURABLE ),
  46101 };
  46102 
  46103 static const JSCFunctionListEntry js_reflect_obj[] = {
  46104     JS_OBJECT_DEF("Reflect", js_reflect_funcs, countof(js_reflect_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
  46105 };
  46106 
  46107 /* Proxy */
  46108 
  46109 static void js_proxy_finalizer(JSRuntime *rt, JSValue val)
  46110 {
  46111     JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY);
  46112     if (s) {
  46113         JS_FreeValueRT(rt, s->target);
  46114         JS_FreeValueRT(rt, s->handler);
  46115         js_free_rt(rt, s);
  46116     }
  46117 }
  46118 
  46119 static void js_proxy_mark(JSRuntime *rt, JSValueConst val,
  46120                           JS_MarkFunc *mark_func)
  46121 {
  46122     JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY);
  46123     if (s) {
  46124         JS_MarkValue(rt, s->target, mark_func);
  46125         JS_MarkValue(rt, s->handler, mark_func);
  46126     }
  46127 }
  46128 
  46129 static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx)
  46130 {
  46131     return JS_ThrowTypeError(ctx, "revoked proxy");
  46132 }
  46133 
  46134 static JSProxyData *get_proxy_method(JSContext *ctx, JSValue *pmethod,
  46135                                      JSValueConst obj, JSAtom name)
  46136 {
  46137     JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY);
  46138     JSValue method;
  46139 
  46140     /* safer to test recursion in all proxy methods */
  46141     if (js_check_stack_overflow(ctx->rt, 0)) {
  46142         JS_ThrowStackOverflow(ctx);
  46143         return NULL;
  46144     }
  46145 
  46146     /* 's' should never be NULL */
  46147     if (s->is_revoked) {
  46148         JS_ThrowTypeErrorRevokedProxy(ctx);
  46149         return NULL;
  46150     }
  46151     method = JS_GetProperty(ctx, s->handler, name);
  46152     if (JS_IsException(method))
  46153         return NULL;
  46154     if (JS_IsNull(method))
  46155         method = JS_UNDEFINED;
  46156     *pmethod = method;
  46157     return s;
  46158 }
  46159 
  46160 static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj)
  46161 {
  46162     JSProxyData *s;
  46163     JSValue method, ret, proto1;
  46164     int res;
  46165 
  46166     s = get_proxy_method(ctx, &method, obj, JS_ATOM_getPrototypeOf);
  46167     if (!s)
  46168         return JS_EXCEPTION;
  46169     if (JS_IsUndefined(method))
  46170         return JS_GetPrototype(ctx, s->target);
  46171     ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
  46172     if (JS_IsException(ret))
  46173         return ret;
  46174     if (JS_VALUE_GET_TAG(ret) != JS_TAG_NULL &&
  46175         JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {
  46176         goto fail;
  46177     }
  46178     res = JS_IsExtensible(ctx, s->target);
  46179     if (res < 0) {
  46180         JS_FreeValue(ctx, ret);
  46181         return JS_EXCEPTION;
  46182     }
  46183     if (!res) {
  46184         /* check invariant */
  46185         proto1 = JS_GetPrototype(ctx, s->target);
  46186         if (JS_IsException(proto1)) {
  46187             JS_FreeValue(ctx, ret);
  46188             return JS_EXCEPTION;
  46189         }
  46190         if (JS_VALUE_GET_OBJ(proto1) != JS_VALUE_GET_OBJ(ret)) {
  46191             JS_FreeValue(ctx, proto1);
  46192         fail:
  46193             JS_FreeValue(ctx, ret);
  46194             return JS_ThrowTypeError(ctx, "proxy: inconsistent prototype");
  46195         }
  46196         JS_FreeValue(ctx, proto1);
  46197     }
  46198     return ret;
  46199 }
  46200 
  46201 static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,
  46202                                    JSValueConst proto_val, BOOL throw_flag)
  46203 {
  46204     JSProxyData *s;
  46205     JSValue method, ret, proto1;
  46206     JSValueConst args[2];
  46207     BOOL res;
  46208     int res2;
  46209 
  46210     s = get_proxy_method(ctx, &method, obj, JS_ATOM_setPrototypeOf);
  46211     if (!s)
  46212         return -1;
  46213     if (JS_IsUndefined(method))
  46214         return JS_SetPrototypeInternal(ctx, s->target, proto_val, throw_flag);
  46215     args[0] = s->target;
  46216     args[1] = proto_val;
  46217     ret = JS_CallFree(ctx, method, s->handler, 2, args);
  46218     if (JS_IsException(ret))
  46219         return -1;
  46220     res = JS_ToBoolFree(ctx, ret);
  46221     if (!res) {
  46222         if (throw_flag) {
  46223             JS_ThrowTypeError(ctx, "proxy: bad prototype");
  46224             return -1;
  46225         } else {
  46226             return FALSE;
  46227         }
  46228     }
  46229     res2 = JS_IsExtensible(ctx, s->target);
  46230     if (res2 < 0)
  46231         return -1;
  46232     if (!res2) {
  46233         proto1 = JS_GetPrototype(ctx, s->target);
  46234         if (JS_IsException(proto1))
  46235             return -1;
  46236         if (JS_VALUE_GET_OBJ(proto_val) != JS_VALUE_GET_OBJ(proto1)) {
  46237             JS_FreeValue(ctx, proto1);
  46238             JS_ThrowTypeError(ctx, "proxy: inconsistent prototype");
  46239             return -1;
  46240         }
  46241         JS_FreeValue(ctx, proto1);
  46242     }
  46243     return TRUE;
  46244 }
  46245 
  46246 static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj)
  46247 {
  46248     JSProxyData *s;
  46249     JSValue method, ret;
  46250     BOOL res;
  46251     int res2;
  46252 
  46253     s = get_proxy_method(ctx, &method, obj, JS_ATOM_isExtensible);
  46254     if (!s)
  46255         return -1;
  46256     if (JS_IsUndefined(method))
  46257         return JS_IsExtensible(ctx, s->target);
  46258     ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
  46259     if (JS_IsException(ret))
  46260         return -1;
  46261     res = JS_ToBoolFree(ctx, ret);
  46262     res2 = JS_IsExtensible(ctx, s->target);
  46263     if (res2 < 0)
  46264         return res2;
  46265     if (res != res2) {
  46266         JS_ThrowTypeError(ctx, "proxy: inconsistent isExtensible");
  46267         return -1;
  46268     }
  46269     return res;
  46270 }
  46271 
  46272 static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj)
  46273 {
  46274     JSProxyData *s;
  46275     JSValue method, ret;
  46276     BOOL res;
  46277     int res2;
  46278 
  46279     s = get_proxy_method(ctx, &method, obj, JS_ATOM_preventExtensions);
  46280     if (!s)
  46281         return -1;
  46282     if (JS_IsUndefined(method))
  46283         return JS_PreventExtensions(ctx, s->target);
  46284     ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
  46285     if (JS_IsException(ret))
  46286         return -1;
  46287     res = JS_ToBoolFree(ctx, ret);
  46288     if (res) {
  46289         res2 = JS_IsExtensible(ctx, s->target);
  46290         if (res2 < 0)
  46291             return res2;
  46292         if (res2) {
  46293             JS_ThrowTypeError(ctx, "proxy: inconsistent preventExtensions");
  46294             return -1;
  46295         }
  46296     }
  46297     return res;
  46298 }
  46299 
  46300 static int js_proxy_has(JSContext *ctx, JSValueConst obj, JSAtom atom)
  46301 {
  46302     JSProxyData *s;
  46303     JSValue method, ret1, atom_val;
  46304     int ret, res;
  46305     JSObject *p;
  46306     JSValueConst args[2];
  46307     BOOL res2;
  46308 
  46309     s = get_proxy_method(ctx, &method, obj, JS_ATOM_has);
  46310     if (!s)
  46311         return -1;
  46312     if (JS_IsUndefined(method))
  46313         return JS_HasProperty(ctx, s->target, atom);
  46314     atom_val = JS_AtomToValue(ctx, atom);
  46315     if (JS_IsException(atom_val)) {
  46316         JS_FreeValue(ctx, method);
  46317         return -1;
  46318     }
  46319     args[0] = s->target;
  46320     args[1] = atom_val;
  46321     ret1 = JS_CallFree(ctx, method, s->handler, 2, args);
  46322     JS_FreeValue(ctx, atom_val);
  46323     if (JS_IsException(ret1))
  46324         return -1;
  46325     ret = JS_ToBoolFree(ctx, ret1);
  46326     if (!ret) {
  46327         JSPropertyDescriptor desc;
  46328         p = JS_VALUE_GET_OBJ(s->target);
  46329         res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);
  46330         if (res < 0)
  46331             return -1;
  46332         if (res) {
  46333             res2 = !(desc.flags & JS_PROP_CONFIGURABLE);
  46334             js_free_desc(ctx, &desc);
  46335             if (res2 || !p->extensible) {
  46336                 JS_ThrowTypeError(ctx, "proxy: inconsistent has");
  46337                 return -1;
  46338             }
  46339         }
  46340     }
  46341     return ret;
  46342 }
  46343 
  46344 static JSValue js_proxy_get(JSContext *ctx, JSValueConst obj, JSAtom atom,
  46345                             JSValueConst receiver)
  46346 {
  46347     JSProxyData *s;
  46348     JSValue method, ret, atom_val;
  46349     int res;
  46350     JSValueConst args[3];
  46351     JSPropertyDescriptor desc;
  46352 
  46353     s = get_proxy_method(ctx, &method, obj, JS_ATOM_get);
  46354     if (!s)
  46355         return JS_EXCEPTION;
  46356     /* Note: recursion is possible thru the prototype of s->target */
  46357     if (JS_IsUndefined(method))
  46358         return JS_GetPropertyInternal(ctx, s->target, atom, receiver, FALSE);
  46359     atom_val = JS_AtomToValue(ctx, atom);
  46360     if (JS_IsException(atom_val)) {
  46361         JS_FreeValue(ctx, method);
  46362         return JS_EXCEPTION;
  46363     }
  46364     args[0] = s->target;
  46365     args[1] = atom_val;
  46366     args[2] = receiver;
  46367     ret = JS_CallFree(ctx, method, s->handler, 3, args);
  46368     JS_FreeValue(ctx, atom_val);
  46369     if (JS_IsException(ret))
  46370         return JS_EXCEPTION;
  46371     res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);
  46372     if (res < 0) {
  46373         JS_FreeValue(ctx, ret);
  46374         return JS_EXCEPTION;
  46375     }
  46376     if (res) {
  46377         if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) {
  46378             if (!js_same_value(ctx, desc.value, ret)) {
  46379                 goto fail;
  46380             }
  46381         } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET) {
  46382             if (JS_IsUndefined(desc.getter) && !JS_IsUndefined(ret)) {
  46383             fail:
  46384                 js_free_desc(ctx, &desc);
  46385                 JS_FreeValue(ctx, ret);
  46386                 return JS_ThrowTypeError(ctx, "proxy: inconsistent get");
  46387             }
  46388         }
  46389         js_free_desc(ctx, &desc);
  46390     }
  46391     return ret;
  46392 }
  46393 
  46394 static int js_proxy_set(JSContext *ctx, JSValueConst obj, JSAtom atom,
  46395                         JSValueConst value, JSValueConst receiver, int flags)
  46396 {
  46397     JSProxyData *s;
  46398     JSValue method, ret1, atom_val;
  46399     int ret, res;
  46400     JSValueConst args[4];
  46401 
  46402     s = get_proxy_method(ctx, &method, obj, JS_ATOM_set);
  46403     if (!s)
  46404         return -1;
  46405     if (JS_IsUndefined(method)) {
  46406         return JS_SetPropertyInternal(ctx, s->target, atom,
  46407                                       JS_DupValue(ctx, value), receiver,
  46408                                       flags);
  46409     }
  46410     atom_val = JS_AtomToValue(ctx, atom);
  46411     if (JS_IsException(atom_val)) {
  46412         JS_FreeValue(ctx, method);
  46413         return -1;
  46414     }
  46415     args[0] = s->target;
  46416     args[1] = atom_val;
  46417     args[2] = value;
  46418     args[3] = receiver;
  46419     ret1 = JS_CallFree(ctx, method, s->handler, 4, args);
  46420     JS_FreeValue(ctx, atom_val);
  46421     if (JS_IsException(ret1))
  46422         return -1;
  46423     ret = JS_ToBoolFree(ctx, ret1);
  46424     if (ret) {
  46425         JSPropertyDescriptor desc;
  46426         res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);
  46427         if (res < 0)
  46428             return -1;
  46429         if (res) {
  46430             if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) {
  46431                 if (!js_same_value(ctx, desc.value, value)) {
  46432                     goto fail;
  46433                 }
  46434             } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET && JS_IsUndefined(desc.setter)) {
  46435                 fail:
  46436                     js_free_desc(ctx, &desc);
  46437                     JS_ThrowTypeError(ctx, "proxy: inconsistent set");
  46438                     return -1;
  46439             }
  46440             js_free_desc(ctx, &desc);
  46441         }
  46442     } else {
  46443         if ((flags & JS_PROP_THROW) ||
  46444             ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
  46445             JS_ThrowTypeError(ctx, "proxy: cannot set property");
  46446             return -1;
  46447         }
  46448     }
  46449     return ret;
  46450 }
  46451 
  46452 static JSValue js_create_desc(JSContext *ctx, JSValueConst val,
  46453                               JSValueConst getter, JSValueConst setter,
  46454                               int flags)
  46455 {
  46456     JSValue ret;
  46457     ret = JS_NewObject(ctx);
  46458     if (JS_IsException(ret))
  46459         return ret;
  46460     if (flags & JS_PROP_HAS_GET) {
  46461         JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, getter),
  46462                                JS_PROP_C_W_E);
  46463     }
  46464     if (flags & JS_PROP_HAS_SET) {
  46465         JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, setter),
  46466                                JS_PROP_C_W_E);
  46467     }
  46468     if (flags & JS_PROP_HAS_VALUE) {
  46469         JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, val),
  46470                                JS_PROP_C_W_E);
  46471     }
  46472     if (flags & JS_PROP_HAS_WRITABLE) {
  46473         JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable,
  46474                                JS_NewBool(ctx, flags & JS_PROP_WRITABLE),
  46475                                JS_PROP_C_W_E);
  46476     }
  46477     if (flags & JS_PROP_HAS_ENUMERABLE) {
  46478         JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable,
  46479                                JS_NewBool(ctx, flags & JS_PROP_ENUMERABLE),
  46480                                JS_PROP_C_W_E);
  46481     }
  46482     if (flags & JS_PROP_HAS_CONFIGURABLE) {
  46483         JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable,
  46484                                JS_NewBool(ctx, flags & JS_PROP_CONFIGURABLE),
  46485                                JS_PROP_C_W_E);
  46486     }
  46487     return ret;
  46488 }
  46489 
  46490 static int js_proxy_get_own_property(JSContext *ctx, JSPropertyDescriptor *pdesc,
  46491                                      JSValueConst obj, JSAtom prop)
  46492 {
  46493     JSProxyData *s;
  46494     JSValue method, trap_result_obj, prop_val;
  46495     int res, target_desc_ret, ret;
  46496     JSObject *p;
  46497     JSValueConst args[2];
  46498     JSPropertyDescriptor result_desc, target_desc;
  46499 
  46500     s = get_proxy_method(ctx, &method, obj, JS_ATOM_getOwnPropertyDescriptor);
  46501     if (!s)
  46502         return -1;
  46503     p = JS_VALUE_GET_OBJ(s->target);
  46504     if (JS_IsUndefined(method)) {
  46505         return JS_GetOwnPropertyInternal(ctx, pdesc, p, prop);
  46506     }
  46507     prop_val = JS_AtomToValue(ctx, prop);
  46508     if (JS_IsException(prop_val)) {
  46509         JS_FreeValue(ctx, method);
  46510         return -1;
  46511     }
  46512     args[0] = s->target;
  46513     args[1] = prop_val;
  46514     trap_result_obj = JS_CallFree(ctx, method, s->handler, 2, args);
  46515     JS_FreeValue(ctx, prop_val);
  46516     if (JS_IsException(trap_result_obj))
  46517         return -1;
  46518     if (!JS_IsObject(trap_result_obj) && !JS_IsUndefined(trap_result_obj)) {
  46519         JS_FreeValue(ctx, trap_result_obj);
  46520         goto fail;
  46521     }
  46522     target_desc_ret = JS_GetOwnPropertyInternal(ctx, &target_desc, p, prop);
  46523     if (target_desc_ret < 0) {
  46524         JS_FreeValue(ctx, trap_result_obj);
  46525         return -1;
  46526     }
  46527     if (target_desc_ret)
  46528         js_free_desc(ctx, &target_desc);
  46529     if (JS_IsUndefined(trap_result_obj)) {
  46530         if (target_desc_ret) {
  46531             if (!(target_desc.flags & JS_PROP_CONFIGURABLE) || !p->extensible)
  46532                 goto fail;
  46533         }
  46534         ret = FALSE;
  46535     } else {
  46536         int flags1, extensible_target;
  46537         extensible_target = JS_IsExtensible(ctx, s->target);
  46538         if (extensible_target < 0) {
  46539             JS_FreeValue(ctx, trap_result_obj);
  46540             return -1;
  46541         }
  46542         res = js_obj_to_desc(ctx, &result_desc, trap_result_obj);
  46543         JS_FreeValue(ctx, trap_result_obj);
  46544         if (res < 0)
  46545             return -1;
  46546 
  46547         if (target_desc_ret) {
  46548             /* convert result_desc.flags to defineProperty flags */
  46549             flags1 = result_desc.flags | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE;
  46550             if (result_desc.flags & JS_PROP_GETSET)
  46551                 flags1 |= JS_PROP_HAS_GET | JS_PROP_HAS_SET;
  46552             else
  46553                 flags1 |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE;
  46554             /* XXX: not complete check: need to compare value &
  46555                getter/setter as in defineproperty */
  46556             if (!check_define_prop_flags(target_desc.flags, flags1))
  46557                 goto fail1;
  46558         } else {
  46559             if (!extensible_target)
  46560                 goto fail1;
  46561         }
  46562         if (!(result_desc.flags & JS_PROP_CONFIGURABLE)) {
  46563             if (!target_desc_ret || (target_desc.flags & JS_PROP_CONFIGURABLE))
  46564                 goto fail1;
  46565             if ((result_desc.flags &
  46566                  (JS_PROP_GETSET | JS_PROP_WRITABLE)) == 0 &&
  46567                 target_desc_ret &&
  46568                 (target_desc.flags & JS_PROP_WRITABLE) != 0) {
  46569                 /* proxy-missing-checks */
  46570             fail1:
  46571                 js_free_desc(ctx, &result_desc);
  46572             fail:
  46573                 JS_ThrowTypeError(ctx, "proxy: inconsistent getOwnPropertyDescriptor");
  46574                 return -1;
  46575             }
  46576         }
  46577         ret = TRUE;
  46578         if (pdesc) {
  46579             *pdesc = result_desc;
  46580         } else {
  46581             js_free_desc(ctx, &result_desc);
  46582         }
  46583     }
  46584     return ret;
  46585 }
  46586 
  46587 static int js_proxy_define_own_property(JSContext *ctx, JSValueConst obj,
  46588                                         JSAtom prop, JSValueConst val,
  46589                                         JSValueConst getter, JSValueConst setter,
  46590                                         int flags)
  46591 {
  46592     JSProxyData *s;
  46593     JSValue method, ret1, prop_val, desc_val;
  46594     int res, ret;
  46595     JSObject *p;
  46596     JSValueConst args[3];
  46597     JSPropertyDescriptor desc;
  46598     BOOL setting_not_configurable;
  46599 
  46600     s = get_proxy_method(ctx, &method, obj, JS_ATOM_defineProperty);
  46601     if (!s)
  46602         return -1;
  46603     if (JS_IsUndefined(method)) {
  46604         return JS_DefineProperty(ctx, s->target, prop, val, getter, setter, flags);
  46605     }
  46606     prop_val = JS_AtomToValue(ctx, prop);
  46607     if (JS_IsException(prop_val)) {
  46608         JS_FreeValue(ctx, method);
  46609         return -1;
  46610     }
  46611     desc_val = js_create_desc(ctx, val, getter, setter, flags);
  46612     if (JS_IsException(desc_val)) {
  46613         JS_FreeValue(ctx, prop_val);
  46614         JS_FreeValue(ctx, method);
  46615         return -1;
  46616     }
  46617     args[0] = s->target;
  46618     args[1] = prop_val;
  46619     args[2] = desc_val;
  46620     ret1 = JS_CallFree(ctx, method, s->handler, 3, args);
  46621     JS_FreeValue(ctx, prop_val);
  46622     JS_FreeValue(ctx, desc_val);
  46623     if (JS_IsException(ret1))
  46624         return -1;
  46625     ret = JS_ToBoolFree(ctx, ret1);
  46626     if (!ret) {
  46627         if (flags & JS_PROP_THROW) {
  46628             JS_ThrowTypeError(ctx, "proxy: defineProperty exception");
  46629             return -1;
  46630         } else {
  46631             return 0;
  46632         }
  46633     }
  46634     p = JS_VALUE_GET_OBJ(s->target);
  46635     res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
  46636     if (res < 0)
  46637         return -1;
  46638     setting_not_configurable = ((flags & (JS_PROP_HAS_CONFIGURABLE |
  46639                                           JS_PROP_CONFIGURABLE)) ==
  46640                                 JS_PROP_HAS_CONFIGURABLE);
  46641     if (!res) {
  46642         if (!p->extensible || setting_not_configurable)
  46643             goto fail;
  46644     } else {
  46645         if (!check_define_prop_flags(desc.flags, flags) ||
  46646             ((desc.flags & JS_PROP_CONFIGURABLE) && setting_not_configurable)) {
  46647             goto fail1;
  46648         }
  46649         if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
  46650             if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) ==
  46651                 JS_PROP_GETSET) {
  46652                 if ((flags & JS_PROP_HAS_GET) &&
  46653                     !js_same_value(ctx, getter, desc.getter)) {
  46654                     goto fail1;
  46655                 }
  46656                 if ((flags & JS_PROP_HAS_SET) &&
  46657                     !js_same_value(ctx, setter, desc.setter)) {
  46658                     goto fail1;
  46659                 }
  46660             }
  46661         } else if (flags & JS_PROP_HAS_VALUE) {
  46662             if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) ==
  46663                 JS_PROP_WRITABLE && !(flags & JS_PROP_WRITABLE)) {
  46664                 /* missing-proxy-check feature */
  46665                 goto fail1;
  46666             } else if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 &&
  46667                 !js_same_value(ctx, val, desc.value)) {
  46668                 goto fail1;
  46669             }
  46670         }
  46671         if (flags & JS_PROP_HAS_WRITABLE) {
  46672             if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE |
  46673                                JS_PROP_WRITABLE)) == JS_PROP_WRITABLE) {
  46674                 /* proxy-missing-checks */
  46675             fail1:
  46676                 js_free_desc(ctx, &desc);
  46677             fail:
  46678                 JS_ThrowTypeError(ctx, "proxy: inconsistent defineProperty");
  46679                 return -1;
  46680             }
  46681         }
  46682         js_free_desc(ctx, &desc);
  46683     }
  46684     return 1;
  46685 }
  46686 
  46687 static int js_proxy_delete_property(JSContext *ctx, JSValueConst obj,
  46688                                     JSAtom atom)
  46689 {
  46690     JSProxyData *s;
  46691     JSValue method, ret, atom_val;
  46692     int res, res2, is_extensible;
  46693     JSValueConst args[2];
  46694 
  46695     s = get_proxy_method(ctx, &method, obj, JS_ATOM_deleteProperty);
  46696     if (!s)
  46697         return -1;
  46698     if (JS_IsUndefined(method)) {
  46699         return JS_DeleteProperty(ctx, s->target, atom, 0);
  46700     }
  46701     atom_val = JS_AtomToValue(ctx, atom);;
  46702     if (JS_IsException(atom_val)) {
  46703         JS_FreeValue(ctx, method);
  46704         return -1;
  46705     }
  46706     args[0] = s->target;
  46707     args[1] = atom_val;
  46708     ret = JS_CallFree(ctx, method, s->handler, 2, args);
  46709     JS_FreeValue(ctx, atom_val);
  46710     if (JS_IsException(ret))
  46711         return -1;
  46712     res = JS_ToBoolFree(ctx, ret);
  46713     if (res) {
  46714         JSPropertyDescriptor desc;
  46715         res2 = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);
  46716         if (res2 < 0)
  46717             return -1;
  46718         if (res2) {
  46719             if (!(desc.flags & JS_PROP_CONFIGURABLE))
  46720                 goto fail;
  46721             is_extensible = JS_IsExtensible(ctx, s->target);
  46722             if (is_extensible < 0)
  46723                 goto fail1;
  46724             if (!is_extensible) {
  46725                 /* proxy-missing-checks */
  46726             fail:
  46727                 JS_ThrowTypeError(ctx, "proxy: inconsistent deleteProperty");
  46728             fail1:
  46729                 js_free_desc(ctx, &desc);
  46730                 return -1;
  46731             }
  46732             js_free_desc(ctx, &desc);
  46733         }
  46734     }
  46735     return res;
  46736 }
  46737 
  46738 /* return the index of the property or -1 if not found */
  46739 static int find_prop_key(const JSPropertyEnum *tab, int n, JSAtom atom)
  46740 {
  46741     int i;
  46742     for(i = 0; i < n; i++) {
  46743         if (tab[i].atom == atom)
  46744             return i;
  46745     }
  46746     return -1;
  46747 }
  46748 
  46749 static int js_proxy_get_own_property_names(JSContext *ctx,
  46750                                            JSPropertyEnum **ptab,
  46751                                            uint32_t *plen,
  46752                                            JSValueConst obj)
  46753 {
  46754     JSProxyData *s;
  46755     JSValue method, prop_array, val;
  46756     uint32_t len, i, len2;
  46757     JSPropertyEnum *tab, *tab2;
  46758     JSAtom atom;
  46759     JSPropertyDescriptor desc;
  46760     int res, is_extensible, idx;
  46761 
  46762     s = get_proxy_method(ctx, &method, obj, JS_ATOM_ownKeys);
  46763     if (!s)
  46764         return -1;
  46765     if (JS_IsUndefined(method)) {
  46766         return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen,
  46767                                       JS_VALUE_GET_OBJ(s->target),
  46768                                       JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK);
  46769     }
  46770     prop_array = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
  46771     if (JS_IsException(prop_array))
  46772         return -1;
  46773     tab = NULL;
  46774     len = 0;
  46775     tab2 = NULL;
  46776     len2 = 0;
  46777     if (js_get_length32(ctx, &len, prop_array))
  46778         goto fail;
  46779     if (len > 0) {
  46780         tab = js_mallocz(ctx, sizeof(tab[0]) * len);
  46781         if (!tab)
  46782             goto fail;
  46783     }
  46784     for(i = 0; i < len; i++) {
  46785         val = JS_GetPropertyUint32(ctx, prop_array, i);
  46786         if (JS_IsException(val))
  46787             goto fail;
  46788         if (!JS_IsString(val) && !JS_IsSymbol(val)) {
  46789             JS_FreeValue(ctx, val);
  46790             JS_ThrowTypeError(ctx, "proxy: properties must be strings or symbols");
  46791             goto fail;
  46792         }
  46793         atom = JS_ValueToAtom(ctx, val);
  46794         JS_FreeValue(ctx, val);
  46795         if (atom == JS_ATOM_NULL)
  46796             goto fail;
  46797         tab[i].atom = atom;
  46798         tab[i].is_enumerable = FALSE; /* XXX: redundant? */
  46799     }
  46800 
  46801     /* check duplicate properties (XXX: inefficient, could store the
  46802      * properties an a temporary object to use the hash) */
  46803     for(i = 1; i < len; i++) {
  46804         if (find_prop_key(tab, i, tab[i].atom) >= 0) {
  46805             JS_ThrowTypeError(ctx, "proxy: duplicate property");
  46806             goto fail;
  46807         }
  46808     }
  46809 
  46810     is_extensible = JS_IsExtensible(ctx, s->target);
  46811     if (is_extensible < 0)
  46812         goto fail;
  46813 
  46814     /* check if there are non configurable properties */
  46815     if (s->is_revoked) {
  46816         JS_ThrowTypeErrorRevokedProxy(ctx);
  46817         goto fail;
  46818     }
  46819     if (JS_GetOwnPropertyNamesInternal(ctx, &tab2, &len2, JS_VALUE_GET_OBJ(s->target),
  46820                                JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK))
  46821         goto fail;
  46822     for(i = 0; i < len2; i++) {
  46823         if (s->is_revoked) {
  46824             JS_ThrowTypeErrorRevokedProxy(ctx);
  46825             goto fail;
  46826         }
  46827         res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target),
  46828                                 tab2[i].atom);
  46829         if (res < 0)
  46830             goto fail;
  46831         if (res) {  /* safety, property should be found */
  46832             js_free_desc(ctx, &desc);
  46833             if (!(desc.flags & JS_PROP_CONFIGURABLE) || !is_extensible) {
  46834                 idx = find_prop_key(tab, len, tab2[i].atom);
  46835                 if (idx < 0) {
  46836                     JS_ThrowTypeError(ctx, "proxy: target property must be present in proxy ownKeys");
  46837                     goto fail;
  46838                 }
  46839                 /* mark the property as found */
  46840                 if (!is_extensible)
  46841                     tab[idx].is_enumerable = TRUE;
  46842             }
  46843         }
  46844     }
  46845     if (!is_extensible) {
  46846         /* check that all property in 'tab' were checked */
  46847         for(i = 0; i < len; i++) {
  46848             if (!tab[i].is_enumerable) {
  46849                 JS_ThrowTypeError(ctx, "proxy: property not present in target were returned by non extensible proxy");
  46850                 goto fail;
  46851             }
  46852         }
  46853     }
  46854 
  46855     js_free_prop_enum(ctx, tab2, len2);
  46856     JS_FreeValue(ctx, prop_array);
  46857     *ptab = tab;
  46858     *plen = len;
  46859     return 0;
  46860  fail:
  46861     js_free_prop_enum(ctx, tab2, len2);
  46862     js_free_prop_enum(ctx, tab, len);
  46863     JS_FreeValue(ctx, prop_array);
  46864     return -1;
  46865 }
  46866 
  46867 static JSValue js_proxy_call_constructor(JSContext *ctx, JSValueConst func_obj,
  46868                                          JSValueConst new_target,
  46869                                          int argc, JSValueConst *argv)
  46870 {
  46871     JSProxyData *s;
  46872     JSValue method, arg_array, ret;
  46873     JSValueConst args[3];
  46874 
  46875     s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_construct);
  46876     if (!s)
  46877         return JS_EXCEPTION;
  46878     if (!JS_IsConstructor(ctx, s->target))
  46879         return JS_ThrowTypeError(ctx, "not a constructor");
  46880     if (JS_IsUndefined(method))
  46881         return JS_CallConstructor2(ctx, s->target, new_target, argc, argv);
  46882     arg_array = js_create_array(ctx, argc, argv);
  46883     if (JS_IsException(arg_array)) {
  46884         ret = JS_EXCEPTION;
  46885         goto fail;
  46886     }
  46887     args[0] = s->target;
  46888     args[1] = arg_array;
  46889     args[2] = new_target;
  46890     ret = JS_Call(ctx, method, s->handler, 3, args);
  46891     if (!JS_IsException(ret) && JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {
  46892         JS_FreeValue(ctx, ret);
  46893         ret = JS_ThrowTypeErrorNotAnObject(ctx);
  46894     }
  46895  fail:
  46896     JS_FreeValue(ctx, method);
  46897     JS_FreeValue(ctx, arg_array);
  46898     return ret;
  46899 }
  46900 
  46901 static JSValue js_proxy_call(JSContext *ctx, JSValueConst func_obj,
  46902                              JSValueConst this_obj,
  46903                              int argc, JSValueConst *argv, int flags)
  46904 {
  46905     JSProxyData *s;
  46906     JSValue method, arg_array, ret;
  46907     JSValueConst args[3];
  46908 
  46909     if (flags & JS_CALL_FLAG_CONSTRUCTOR)
  46910         return js_proxy_call_constructor(ctx, func_obj, this_obj, argc, argv);
  46911 
  46912     s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_apply);
  46913     if (!s)
  46914         return JS_EXCEPTION;
  46915     if (!s->is_func) {
  46916         JS_FreeValue(ctx, method);
  46917         return JS_ThrowTypeError(ctx, "not a function");
  46918     }
  46919     if (JS_IsUndefined(method))
  46920         return JS_Call(ctx, s->target, this_obj, argc, argv);
  46921     arg_array = js_create_array(ctx, argc, argv);
  46922     if (JS_IsException(arg_array)) {
  46923         ret = JS_EXCEPTION;
  46924         goto fail;
  46925     }
  46926     args[0] = s->target;
  46927     args[1] = this_obj;
  46928     args[2] = arg_array;
  46929     ret = JS_Call(ctx, method, s->handler, 3, args);
  46930  fail:
  46931     JS_FreeValue(ctx, method);
  46932     JS_FreeValue(ctx, arg_array);
  46933     return ret;
  46934 }
  46935 
  46936 /* `js_resolve_proxy`: resolve the proxy chain
  46937    `*pval` is updated with to ultimate proxy target
  46938    `throw_exception` controls whether exceptions are thown or not
  46939    - return -1 in case of error
  46940    - otherwise return 0
  46941  */
  46942 static int js_resolve_proxy(JSContext *ctx, JSValueConst *pval, BOOL throw_exception) {
  46943     int depth = 0;
  46944     JSObject *p;
  46945     JSProxyData *s;
  46946 
  46947     while (JS_VALUE_GET_TAG(*pval) == JS_TAG_OBJECT) {
  46948         p = JS_VALUE_GET_OBJ(*pval);
  46949         if (p->class_id != JS_CLASS_PROXY)
  46950             break;
  46951         if (depth++ > 1000) {
  46952             if (throw_exception)
  46953                 JS_ThrowStackOverflow(ctx);
  46954             return -1;
  46955         }
  46956         s = p->u.opaque;
  46957         if (s->is_revoked) {
  46958             if (throw_exception)
  46959                 JS_ThrowTypeErrorRevokedProxy(ctx);
  46960             return -1;
  46961         }
  46962         *pval = s->target;
  46963     }
  46964     return 0;
  46965 }
  46966 
  46967 static const JSClassExoticMethods js_proxy_exotic_methods = {
  46968     .get_own_property = js_proxy_get_own_property,
  46969     .define_own_property = js_proxy_define_own_property,
  46970     .delete_property = js_proxy_delete_property,
  46971     .get_own_property_names = js_proxy_get_own_property_names,
  46972     .has_property = js_proxy_has,
  46973     .get_property = js_proxy_get,
  46974     .set_property = js_proxy_set,
  46975 };
  46976 
  46977 static JSValue js_proxy_constructor(JSContext *ctx, JSValueConst this_val,
  46978                                     int argc, JSValueConst *argv)
  46979 {
  46980     JSValueConst target, handler;
  46981     JSValue obj;
  46982     JSProxyData *s;
  46983 
  46984     target = argv[0];
  46985     handler = argv[1];
  46986     if (JS_VALUE_GET_TAG(target) != JS_TAG_OBJECT ||
  46987         JS_VALUE_GET_TAG(handler) != JS_TAG_OBJECT)
  46988         return JS_ThrowTypeErrorNotAnObject(ctx);
  46989 
  46990     obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_PROXY);
  46991     if (JS_IsException(obj))
  46992         return obj;
  46993     s = js_malloc(ctx, sizeof(JSProxyData));
  46994     if (!s) {
  46995         JS_FreeValue(ctx, obj);
  46996         return JS_EXCEPTION;
  46997     }
  46998     s->target = JS_DupValue(ctx, target);
  46999     s->handler = JS_DupValue(ctx, handler);
  47000     s->is_func = JS_IsFunction(ctx, target);
  47001     s->is_revoked = FALSE;
  47002     JS_SetOpaque(obj, s);
  47003     JS_SetConstructorBit(ctx, obj, JS_IsConstructor(ctx, target));
  47004     return obj;
  47005 }
  47006 
  47007 static JSValue js_proxy_revoke(JSContext *ctx, JSValueConst this_val,
  47008                                int argc, JSValueConst *argv, int magic,
  47009                                JSValue *func_data)
  47010 {
  47011     JSProxyData *s = JS_GetOpaque(func_data[0], JS_CLASS_PROXY);
  47012     if (s) {
  47013         /* We do not free the handler and target in case they are
  47014            referenced as constants in the C call stack */
  47015         s->is_revoked = TRUE;
  47016         JS_FreeValue(ctx, func_data[0]);
  47017         func_data[0] = JS_NULL;
  47018     }
  47019     return JS_UNDEFINED;
  47020 }
  47021 
  47022 static JSValue js_proxy_revoke_constructor(JSContext *ctx,
  47023                                            JSValueConst proxy_obj)
  47024 {
  47025     return JS_NewCFunctionData(ctx, js_proxy_revoke, 0, 0, 1, &proxy_obj);
  47026 }
  47027 
  47028 static JSValue js_proxy_revocable(JSContext *ctx, JSValueConst this_val,
  47029                                  int argc, JSValueConst *argv)
  47030 {
  47031     JSValue proxy_obj, revoke_obj = JS_UNDEFINED, obj;
  47032 
  47033     proxy_obj = js_proxy_constructor(ctx, JS_UNDEFINED, argc, argv);
  47034     if (JS_IsException(proxy_obj))
  47035         goto fail;
  47036     revoke_obj = js_proxy_revoke_constructor(ctx, proxy_obj);
  47037     if (JS_IsException(revoke_obj))
  47038         goto fail;
  47039     obj = JS_NewObject(ctx);
  47040     if (JS_IsException(obj))
  47041         goto fail;
  47042     // XXX: exceptions?
  47043     JS_DefinePropertyValue(ctx, obj, JS_ATOM_proxy, proxy_obj, JS_PROP_C_W_E);
  47044     JS_DefinePropertyValue(ctx, obj, JS_ATOM_revoke, revoke_obj, JS_PROP_C_W_E);
  47045     return obj;
  47046  fail:
  47047     JS_FreeValue(ctx, proxy_obj);
  47048     JS_FreeValue(ctx, revoke_obj);
  47049     return JS_EXCEPTION;
  47050 }
  47051 
  47052 static const JSCFunctionListEntry js_proxy_funcs[] = {
  47053     JS_CFUNC_DEF("revocable", 2, js_proxy_revocable ),
  47054 };
  47055 
  47056 static const JSClassShortDef js_proxy_class_def[] = {
  47057     { JS_ATOM_Object, js_proxy_finalizer, js_proxy_mark }, /* JS_CLASS_PROXY */
  47058 };
  47059 
  47060 void JS_AddIntrinsicProxy(JSContext *ctx)
  47061 {
  47062     JSRuntime *rt = ctx->rt;
  47063     JSValue obj1;
  47064 
  47065     if (!JS_IsRegisteredClass(rt, JS_CLASS_PROXY)) {
  47066         init_class_range(rt, js_proxy_class_def, JS_CLASS_PROXY,
  47067                          countof(js_proxy_class_def));
  47068         rt->class_array[JS_CLASS_PROXY].exotic = &js_proxy_exotic_methods;
  47069         rt->class_array[JS_CLASS_PROXY].call = js_proxy_call;
  47070     }
  47071 
  47072     obj1 = JS_NewCFunction2(ctx, js_proxy_constructor, "Proxy", 2,
  47073                             JS_CFUNC_constructor, 0);
  47074     JS_SetConstructorBit(ctx, obj1, TRUE);
  47075     JS_SetPropertyFunctionList(ctx, obj1, js_proxy_funcs,
  47076                                countof(js_proxy_funcs));
  47077     JS_DefinePropertyValueStr(ctx, ctx->global_obj, "Proxy",
  47078                               obj1, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
  47079 }
  47080 
  47081 /* Symbol */
  47082 
  47083 static JSValue js_symbol_constructor(JSContext *ctx, JSValueConst new_target,
  47084                                      int argc, JSValueConst *argv)
  47085 {
  47086     JSValue str;
  47087     JSString *p;
  47088 
  47089     if (!JS_IsUndefined(new_target))
  47090         return JS_ThrowTypeError(ctx, "not a constructor");
  47091     if (argc == 0 || JS_IsUndefined(argv[0])) {
  47092         p = NULL;
  47093     } else {
  47094         str = JS_ToString(ctx, argv[0]);
  47095         if (JS_IsException(str))
  47096             return JS_EXCEPTION;
  47097         p = JS_VALUE_GET_STRING(str);
  47098     }
  47099     return JS_NewSymbol(ctx, p, JS_ATOM_TYPE_SYMBOL);
  47100 }
  47101 
  47102 static JSValue js_thisSymbolValue(JSContext *ctx, JSValueConst this_val)
  47103 {
  47104     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_SYMBOL)
  47105         return JS_DupValue(ctx, this_val);
  47106 
  47107     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
  47108         JSObject *p = JS_VALUE_GET_OBJ(this_val);
  47109         if (p->class_id == JS_CLASS_SYMBOL) {
  47110             if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_SYMBOL)
  47111                 return JS_DupValue(ctx, p->u.object_data);
  47112         }
  47113     }
  47114     return JS_ThrowTypeError(ctx, "not a symbol");
  47115 }
  47116 
  47117 static JSValue js_symbol_toString(JSContext *ctx, JSValueConst this_val,
  47118                                   int argc, JSValueConst *argv)
  47119 {
  47120     JSValue val, ret;
  47121     val = js_thisSymbolValue(ctx, this_val);
  47122     if (JS_IsException(val))
  47123         return val;
  47124     /* XXX: use JS_ToStringInternal() with a flags */
  47125     ret = js_string_constructor(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val);
  47126     JS_FreeValue(ctx, val);
  47127     return ret;
  47128 }
  47129 
  47130 static JSValue js_symbol_valueOf(JSContext *ctx, JSValueConst this_val,
  47131                                  int argc, JSValueConst *argv)
  47132 {
  47133     return js_thisSymbolValue(ctx, this_val);
  47134 }
  47135 
  47136 static JSValue js_symbol_get_description(JSContext *ctx, JSValueConst this_val)
  47137 {
  47138     JSValue val, ret;
  47139     JSAtomStruct *p;
  47140 
  47141     val = js_thisSymbolValue(ctx, this_val);
  47142     if (JS_IsException(val))
  47143         return val;
  47144     p = JS_VALUE_GET_PTR(val);
  47145     if (p->len == 0 && p->is_wide_char != 0) {
  47146         ret = JS_UNDEFINED;
  47147     } else {
  47148         ret = JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p));
  47149     }
  47150     JS_FreeValue(ctx, val);
  47151     return ret;
  47152 }
  47153 
  47154 static const JSCFunctionListEntry js_symbol_proto_funcs[] = {
  47155     JS_CFUNC_DEF("toString", 0, js_symbol_toString ),
  47156     JS_CFUNC_DEF("valueOf", 0, js_symbol_valueOf ),
  47157     // XXX: should have writable: false
  47158     JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_symbol_valueOf ),
  47159     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Symbol", JS_PROP_CONFIGURABLE ),
  47160     JS_CGETSET_DEF("description", js_symbol_get_description, NULL ),
  47161 };
  47162 
  47163 static JSValue js_symbol_for(JSContext *ctx, JSValueConst this_val,
  47164                              int argc, JSValueConst *argv)
  47165 {
  47166     JSValue str;
  47167 
  47168     str = JS_ToString(ctx, argv[0]);
  47169     if (JS_IsException(str))
  47170         return JS_EXCEPTION;
  47171     return JS_NewSymbol(ctx, JS_VALUE_GET_STRING(str), JS_ATOM_TYPE_GLOBAL_SYMBOL);
  47172 }
  47173 
  47174 static JSValue js_symbol_keyFor(JSContext *ctx, JSValueConst this_val,
  47175                                 int argc, JSValueConst *argv)
  47176 {
  47177     JSAtomStruct *p;
  47178 
  47179     if (!JS_IsSymbol(argv[0]))
  47180         return JS_ThrowTypeError(ctx, "not a symbol");
  47181     p = JS_VALUE_GET_PTR(argv[0]);
  47182     if (p->atom_type != JS_ATOM_TYPE_GLOBAL_SYMBOL)
  47183         return JS_UNDEFINED;
  47184     return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
  47185 }
  47186 
  47187 static const JSCFunctionListEntry js_symbol_funcs[] = {
  47188     JS_CFUNC_DEF("for", 1, js_symbol_for ),
  47189     JS_CFUNC_DEF("keyFor", 1, js_symbol_keyFor ),
  47190 };
  47191 
  47192 /* Set/Map/WeakSet/WeakMap */
  47193 
  47194 typedef struct JSMapRecord {
  47195     int ref_count; /* used during enumeration to avoid freeing the record */
  47196     BOOL empty; /* TRUE if the record is deleted */
  47197     struct JSMapState *map;
  47198     struct JSMapRecord *next_weak_ref;
  47199     struct list_head link;
  47200     struct list_head hash_link;
  47201     JSValue key;
  47202     JSValue value;
  47203 } JSMapRecord;
  47204 
  47205 typedef struct JSMapState {
  47206     BOOL is_weak; /* TRUE if WeakSet/WeakMap */
  47207     struct list_head records; /* list of JSMapRecord.link */
  47208     uint32_t record_count;
  47209     struct list_head *hash_table;
  47210     uint32_t hash_size; /* must be a power of two */
  47211     uint32_t record_count_threshold; /* count at which a hash table
  47212                                         resize is needed */
  47213 } JSMapState;
  47214 
  47215 #define MAGIC_SET (1 << 0)
  47216 #define MAGIC_WEAK (1 << 1)
  47217 
  47218 static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target,
  47219                                   int argc, JSValueConst *argv, int magic)
  47220 {
  47221     JSMapState *s;
  47222     JSValue obj, adder = JS_UNDEFINED, iter = JS_UNDEFINED, next_method = JS_UNDEFINED;
  47223     JSValueConst arr;
  47224     BOOL is_set, is_weak;
  47225 
  47226     is_set = magic & MAGIC_SET;
  47227     is_weak = ((magic & MAGIC_WEAK) != 0);
  47228     obj = js_create_from_ctor(ctx, new_target, JS_CLASS_MAP + magic);
  47229     if (JS_IsException(obj))
  47230         return JS_EXCEPTION;
  47231     s = js_mallocz(ctx, sizeof(*s));
  47232     if (!s)
  47233         goto fail;
  47234     init_list_head(&s->records);
  47235     s->is_weak = is_weak;
  47236     JS_SetOpaque(obj, s);
  47237     s->hash_size = 1;
  47238     s->hash_table = js_malloc(ctx, sizeof(s->hash_table[0]) * s->hash_size);
  47239     if (!s->hash_table)
  47240         goto fail;
  47241     init_list_head(&s->hash_table[0]);
  47242     s->record_count_threshold = 4;
  47243 
  47244     arr = JS_UNDEFINED;
  47245     if (argc > 0)
  47246         arr = argv[0];
  47247     if (!JS_IsUndefined(arr) && !JS_IsNull(arr)) {
  47248         JSValue item, ret;
  47249         BOOL done;
  47250 
  47251         adder = JS_GetProperty(ctx, obj, is_set ? JS_ATOM_add : JS_ATOM_set);
  47252         if (JS_IsException(adder))
  47253             goto fail;
  47254         if (!JS_IsFunction(ctx, adder)) {
  47255             JS_ThrowTypeError(ctx, "set/add is not a function");
  47256             goto fail;
  47257         }
  47258 
  47259         iter = JS_GetIterator(ctx, arr, FALSE);
  47260         if (JS_IsException(iter))
  47261             goto fail;
  47262         next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
  47263         if (JS_IsException(next_method))
  47264             goto fail;
  47265 
  47266         for(;;) {
  47267             item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
  47268             if (JS_IsException(item))
  47269                 goto fail;
  47270             if (done) {
  47271                 JS_FreeValue(ctx, item);
  47272                 break;
  47273             }
  47274             if (is_set) {
  47275                 ret = JS_Call(ctx, adder, obj, 1, (JSValueConst *)&item);
  47276                 if (JS_IsException(ret)) {
  47277                     JS_FreeValue(ctx, item);
  47278                     goto fail;
  47279                 }
  47280             } else {
  47281                 JSValue key, value;
  47282                 JSValueConst args[2];
  47283                 key = JS_UNDEFINED;
  47284                 value = JS_UNDEFINED;
  47285                 if (!JS_IsObject(item)) {
  47286                     JS_ThrowTypeErrorNotAnObject(ctx);
  47287                     goto fail1;
  47288                 }
  47289                 key = JS_GetPropertyUint32(ctx, item, 0);
  47290                 if (JS_IsException(key))
  47291                     goto fail1;
  47292                 value = JS_GetPropertyUint32(ctx, item, 1);
  47293                 if (JS_IsException(value))
  47294                     goto fail1;
  47295                 args[0] = key;
  47296                 args[1] = value;
  47297                 ret = JS_Call(ctx, adder, obj, 2, args);
  47298                 if (JS_IsException(ret)) {
  47299                 fail1:
  47300                     JS_FreeValue(ctx, item);
  47301                     JS_FreeValue(ctx, key);
  47302                     JS_FreeValue(ctx, value);
  47303                     goto fail;
  47304                 }
  47305                 JS_FreeValue(ctx, key);
  47306                 JS_FreeValue(ctx, value);
  47307             }
  47308             JS_FreeValue(ctx, ret);
  47309             JS_FreeValue(ctx, item);
  47310         }
  47311         JS_FreeValue(ctx, next_method);
  47312         JS_FreeValue(ctx, iter);
  47313         JS_FreeValue(ctx, adder);
  47314     }
  47315     return obj;
  47316  fail:
  47317     if (JS_IsObject(iter)) {
  47318         /* close the iterator object, preserving pending exception */
  47319         JS_IteratorClose(ctx, iter, TRUE);
  47320     }
  47321     JS_FreeValue(ctx, next_method);
  47322     JS_FreeValue(ctx, iter);
  47323     JS_FreeValue(ctx, adder);
  47324     JS_FreeValue(ctx, obj);
  47325     return JS_EXCEPTION;
  47326 }
  47327 
  47328 /* XXX: could normalize strings to speed up comparison */
  47329 static JSValueConst map_normalize_key(JSContext *ctx, JSValueConst key)
  47330 {
  47331     uint32_t tag = JS_VALUE_GET_TAG(key);
  47332     /* convert -0.0 to +0.0 */
  47333     if (JS_TAG_IS_FLOAT64(tag) && JS_VALUE_GET_FLOAT64(key) == 0.0) {
  47334         key = JS_NewInt32(ctx, 0);
  47335     }
  47336     return key;
  47337 }
  47338 
  47339 /* XXX: better hash ? */
  47340 static uint32_t map_hash_key(JSContext *ctx, JSValueConst key)
  47341 {
  47342     uint32_t tag = JS_VALUE_GET_NORM_TAG(key);
  47343     uint32_t h;
  47344     double d;
  47345     JSFloat64Union u;
  47346 
  47347     switch(tag) {
  47348     case JS_TAG_BOOL:
  47349         h = JS_VALUE_GET_INT(key);
  47350         break;
  47351     case JS_TAG_STRING:
  47352         h = hash_string(JS_VALUE_GET_STRING(key), 0);
  47353         break;
  47354     case JS_TAG_OBJECT:
  47355     case JS_TAG_SYMBOL:
  47356         h = (uintptr_t)JS_VALUE_GET_PTR(key) * 3163;
  47357         break;
  47358     case JS_TAG_INT:
  47359         d = JS_VALUE_GET_INT(key);
  47360         goto hash_float64;
  47361     case JS_TAG_FLOAT64:
  47362         d = JS_VALUE_GET_FLOAT64(key);
  47363         /* normalize the NaN */
  47364         if (isnan(d))
  47365             d = JS_FLOAT64_NAN;
  47366     hash_float64:
  47367         u.d = d;
  47368         h = (u.u32[0] ^ u.u32[1]) * 3163;
  47369         return h ^= JS_TAG_FLOAT64;
  47370     default:
  47371         h = 0; /* XXX: bignum support */
  47372         break;
  47373     }
  47374     h ^= tag;
  47375     return h;
  47376 }
  47377 
  47378 static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s,
  47379                                     JSValueConst key)
  47380 {
  47381     struct list_head *el;
  47382     JSMapRecord *mr;
  47383     uint32_t h;
  47384     h = map_hash_key(ctx, key) & (s->hash_size - 1);
  47385     list_for_each(el, &s->hash_table[h]) {
  47386         mr = list_entry(el, JSMapRecord, hash_link);
  47387         if (js_same_value_zero(ctx, mr->key, key))
  47388             return mr;
  47389     }
  47390     return NULL;
  47391 }
  47392 
  47393 static void map_hash_resize(JSContext *ctx, JSMapState *s)
  47394 {
  47395     uint32_t new_hash_size, i, h;
  47396     size_t slack;
  47397     struct list_head *new_hash_table, *el;
  47398     JSMapRecord *mr;
  47399 
  47400     /* XXX: no reporting of memory allocation failure */
  47401     if (s->hash_size == 1)
  47402         new_hash_size = 4;
  47403     else
  47404         new_hash_size = s->hash_size * 2;
  47405     new_hash_table = js_realloc2(ctx, s->hash_table,
  47406                                  sizeof(new_hash_table[0]) * new_hash_size, &slack);
  47407     if (!new_hash_table)
  47408         return;
  47409     new_hash_size += slack / sizeof(*new_hash_table);
  47410 
  47411     for(i = 0; i < new_hash_size; i++)
  47412         init_list_head(&new_hash_table[i]);
  47413 
  47414     list_for_each(el, &s->records) {
  47415         mr = list_entry(el, JSMapRecord, link);
  47416         if (!mr->empty) {
  47417             h = map_hash_key(ctx, mr->key) & (new_hash_size - 1);
  47418             list_add_tail(&mr->hash_link, &new_hash_table[h]);
  47419         }
  47420     }
  47421     s->hash_table = new_hash_table;
  47422     s->hash_size = new_hash_size;
  47423     s->record_count_threshold = new_hash_size * 2;
  47424 }
  47425 
  47426 static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s,
  47427                                    JSValueConst key)
  47428 {
  47429     uint32_t h;
  47430     JSMapRecord *mr;
  47431 
  47432     mr = js_malloc(ctx, sizeof(*mr));
  47433     if (!mr)
  47434         return NULL;
  47435     mr->ref_count = 1;
  47436     mr->map = s;
  47437     mr->empty = FALSE;
  47438     if (s->is_weak) {
  47439         JSObject *p = JS_VALUE_GET_OBJ(key);
  47440         /* Add the weak reference */
  47441         mr->next_weak_ref = p->first_weak_ref;
  47442         p->first_weak_ref = mr;
  47443     } else {
  47444         JS_DupValue(ctx, key);
  47445     }
  47446     mr->key = (JSValue)key;
  47447     h = map_hash_key(ctx, key) & (s->hash_size - 1);
  47448     list_add_tail(&mr->hash_link, &s->hash_table[h]);
  47449     list_add_tail(&mr->link, &s->records);
  47450     s->record_count++;
  47451     if (s->record_count >= s->record_count_threshold) {
  47452         map_hash_resize(ctx, s);
  47453     }
  47454     return mr;
  47455 }
  47456 
  47457 /* Remove the weak reference from the object weak
  47458    reference list. we don't use a doubly linked list to
  47459    save space, assuming a given object has few weak
  47460        references to it */
  47461 static void delete_weak_ref(JSRuntime *rt, JSMapRecord *mr)
  47462 {
  47463     JSMapRecord **pmr, *mr1;
  47464     JSObject *p;
  47465 
  47466     p = JS_VALUE_GET_OBJ(mr->key);
  47467     pmr = &p->first_weak_ref;
  47468     for(;;) {
  47469         mr1 = *pmr;
  47470         assert(mr1 != NULL);
  47471         if (mr1 == mr)
  47472             break;
  47473         pmr = &mr1->next_weak_ref;
  47474     }
  47475     *pmr = mr1->next_weak_ref;
  47476 }
  47477 
  47478 static void map_delete_record(JSRuntime *rt, JSMapState *s, JSMapRecord *mr)
  47479 {
  47480     if (mr->empty)
  47481         return;
  47482     list_del(&mr->hash_link);
  47483     if (s->is_weak) {
  47484         delete_weak_ref(rt, mr);
  47485     } else {
  47486         JS_FreeValueRT(rt, mr->key);
  47487     }
  47488     JS_FreeValueRT(rt, mr->value);
  47489     if (--mr->ref_count == 0) {
  47490         list_del(&mr->link);
  47491         js_free_rt(rt, mr);
  47492     } else {
  47493         /* keep a zombie record for iterators */
  47494         mr->empty = TRUE;
  47495         mr->key = JS_UNDEFINED;
  47496         mr->value = JS_UNDEFINED;
  47497     }
  47498     s->record_count--;
  47499 }
  47500 
  47501 static void map_decref_record(JSRuntime *rt, JSMapRecord *mr)
  47502 {
  47503     if (--mr->ref_count == 0) {
  47504         /* the record can be safely removed */
  47505         assert(mr->empty);
  47506         list_del(&mr->link);
  47507         js_free_rt(rt, mr);
  47508     }
  47509 }
  47510 
  47511 static void reset_weak_ref(JSRuntime *rt, JSObject *p)
  47512 {
  47513     JSMapRecord *mr, *mr_next;
  47514     JSMapState *s;
  47515 
  47516     /* first pass to remove the records from the WeakMap/WeakSet
  47517        lists */
  47518     for(mr = p->first_weak_ref; mr != NULL; mr = mr->next_weak_ref) {
  47519         s = mr->map;
  47520         assert(s->is_weak);
  47521         assert(!mr->empty); /* no iterator on WeakMap/WeakSet */
  47522         list_del(&mr->hash_link);
  47523         list_del(&mr->link);
  47524     }
  47525 
  47526     /* second pass to free the values to avoid modifying the weak
  47527        reference list while traversing it. */
  47528     for(mr = p->first_weak_ref; mr != NULL; mr = mr_next) {
  47529         mr_next = mr->next_weak_ref;
  47530         JS_FreeValueRT(rt, mr->value);
  47531         js_free_rt(rt, mr);
  47532     }
  47533 
  47534     p->first_weak_ref = NULL; /* fail safe */
  47535 }
  47536 
  47537 static JSValue js_map_set(JSContext *ctx, JSValueConst this_val,
  47538                           int argc, JSValueConst *argv, int magic)
  47539 {
  47540     JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
  47541     JSMapRecord *mr;
  47542     JSValueConst key, value;
  47543 
  47544     if (!s)
  47545         return JS_EXCEPTION;
  47546     key = map_normalize_key(ctx, argv[0]);
  47547     if (s->is_weak && !JS_IsObject(key))
  47548         return JS_ThrowTypeErrorNotAnObject(ctx);
  47549     if (magic & MAGIC_SET)
  47550         value = JS_UNDEFINED;
  47551     else
  47552         value = argv[1];
  47553     mr = map_find_record(ctx, s, key);
  47554     if (mr) {
  47555         JS_FreeValue(ctx, mr->value);
  47556     } else {
  47557         mr = map_add_record(ctx, s, key);
  47558         if (!mr)
  47559             return JS_EXCEPTION;
  47560     }
  47561     mr->value = JS_DupValue(ctx, value);
  47562     return JS_DupValue(ctx, this_val);
  47563 }
  47564 
  47565 static JSValue js_map_get(JSContext *ctx, JSValueConst this_val,
  47566                           int argc, JSValueConst *argv, int magic)
  47567 {
  47568     JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
  47569     JSMapRecord *mr;
  47570     JSValueConst key;
  47571 
  47572     if (!s)
  47573         return JS_EXCEPTION;
  47574     key = map_normalize_key(ctx, argv[0]);
  47575     mr = map_find_record(ctx, s, key);
  47576     if (!mr)
  47577         return JS_UNDEFINED;
  47578     else
  47579         return JS_DupValue(ctx, mr->value);
  47580 }
  47581 
  47582 static JSValue js_map_has(JSContext *ctx, JSValueConst this_val,
  47583                           int argc, JSValueConst *argv, int magic)
  47584 {
  47585     JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
  47586     JSMapRecord *mr;
  47587     JSValueConst key;
  47588 
  47589     if (!s)
  47590         return JS_EXCEPTION;
  47591     key = map_normalize_key(ctx, argv[0]);
  47592     mr = map_find_record(ctx, s, key);
  47593     return JS_NewBool(ctx, mr != NULL);
  47594 }
  47595 
  47596 static JSValue js_map_delete(JSContext *ctx, JSValueConst this_val,
  47597                              int argc, JSValueConst *argv, int magic)
  47598 {
  47599     JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
  47600     JSMapRecord *mr;
  47601     JSValueConst key;
  47602 
  47603     if (!s)
  47604         return JS_EXCEPTION;
  47605     key = map_normalize_key(ctx, argv[0]);
  47606     mr = map_find_record(ctx, s, key);
  47607     if (!mr)
  47608         return JS_FALSE;
  47609     map_delete_record(ctx->rt, s, mr);
  47610     return JS_TRUE;
  47611 }
  47612 
  47613 static JSValue js_map_clear(JSContext *ctx, JSValueConst this_val,
  47614                             int argc, JSValueConst *argv, int magic)
  47615 {
  47616     JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
  47617     struct list_head *el, *el1;
  47618     JSMapRecord *mr;
  47619 
  47620     if (!s)
  47621         return JS_EXCEPTION;
  47622     list_for_each_safe(el, el1, &s->records) {
  47623         mr = list_entry(el, JSMapRecord, link);
  47624         map_delete_record(ctx->rt, s, mr);
  47625     }
  47626     return JS_UNDEFINED;
  47627 }
  47628 
  47629 static JSValue js_map_get_size(JSContext *ctx, JSValueConst this_val, int magic)
  47630 {
  47631     JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
  47632     if (!s)
  47633         return JS_EXCEPTION;
  47634     return JS_NewUint32(ctx, s->record_count);
  47635 }
  47636 
  47637 static JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val,
  47638                               int argc, JSValueConst *argv, int magic)
  47639 {
  47640     JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
  47641     JSValueConst func, this_arg;
  47642     JSValue ret, args[3];
  47643     struct list_head *el;
  47644     JSMapRecord *mr;
  47645 
  47646     if (!s)
  47647         return JS_EXCEPTION;
  47648     func = argv[0];
  47649     if (argc > 1)
  47650         this_arg = argv[1];
  47651     else
  47652         this_arg = JS_UNDEFINED;
  47653     if (check_function(ctx, func))
  47654         return JS_EXCEPTION;
  47655     /* Note: the list can be modified while traversing it, but the
  47656        current element is locked */
  47657     el = s->records.next;
  47658     while (el != &s->records) {
  47659         mr = list_entry(el, JSMapRecord, link);
  47660         if (!mr->empty) {
  47661             mr->ref_count++;
  47662             /* must duplicate in case the record is deleted */
  47663             args[1] = JS_DupValue(ctx, mr->key);
  47664             if (magic)
  47665                 args[0] = args[1];
  47666             else
  47667                 args[0] = JS_DupValue(ctx, mr->value);
  47668             args[2] = (JSValue)this_val;
  47669             ret = JS_Call(ctx, func, this_arg, 3, (JSValueConst *)args);
  47670             JS_FreeValue(ctx, args[0]);
  47671             if (!magic)
  47672                 JS_FreeValue(ctx, args[1]);
  47673             el = el->next;
  47674             map_decref_record(ctx->rt, mr);
  47675             if (JS_IsException(ret))
  47676                 return ret;
  47677             JS_FreeValue(ctx, ret);
  47678         } else {
  47679             el = el->next;
  47680         }
  47681     }
  47682     return JS_UNDEFINED;
  47683 }
  47684 
  47685 static JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val,
  47686                                  int argc, JSValueConst *argv, int is_map)
  47687 {
  47688     JSValueConst cb, args[2];
  47689     JSValue res, iter, next, groups, key, v, prop;
  47690     JSAtom key_atom = JS_ATOM_NULL;
  47691     int64_t idx;
  47692     BOOL done;
  47693 
  47694     // "is function?" check must be observed before argv[0] is accessed
  47695     cb = argv[1];
  47696     if (check_function(ctx, cb))
  47697         return JS_EXCEPTION;
  47698 
  47699     iter = JS_GetIterator(ctx, argv[0], /*is_async*/FALSE);
  47700     if (JS_IsException(iter))
  47701         return JS_EXCEPTION;
  47702 
  47703     key = JS_UNDEFINED;
  47704     key_atom = JS_ATOM_NULL;
  47705     v = JS_UNDEFINED;
  47706     prop = JS_UNDEFINED;
  47707     groups = JS_UNDEFINED;
  47708 
  47709     next = JS_GetProperty(ctx, iter, JS_ATOM_next);
  47710     if (JS_IsException(next))
  47711         goto exception;
  47712 
  47713     if (is_map) {
  47714         groups = js_map_constructor(ctx, JS_UNDEFINED, 0, NULL, 0);
  47715     } else {
  47716         groups = JS_NewObjectProto(ctx, JS_NULL);
  47717     }
  47718     if (JS_IsException(groups))
  47719         goto exception;
  47720 
  47721     for (idx = 0; ; idx++) {
  47722         if (idx >= MAX_SAFE_INTEGER) {
  47723             JS_ThrowTypeError(ctx, "too many elements");
  47724             goto iterator_close_exception;
  47725         }
  47726         v = JS_IteratorNext(ctx, iter, next, 0, NULL, &done);
  47727         if (JS_IsException(v))
  47728             goto exception;
  47729         if (done)
  47730             break; // v is JS_UNDEFINED
  47731 
  47732         args[0] = v;
  47733         args[1] = JS_NewInt64(ctx, idx);
  47734         key = JS_Call(ctx, cb, ctx->global_obj, 2, args);
  47735         if (JS_IsException(key))
  47736             goto iterator_close_exception;
  47737 
  47738         if (is_map) {
  47739             prop = js_map_get(ctx, groups, 1, (JSValueConst *)&key, 0);
  47740         } else {
  47741             key_atom = JS_ValueToAtom(ctx, key);
  47742             JS_FreeValue(ctx, key);
  47743             key = JS_UNDEFINED;
  47744             if (key_atom == JS_ATOM_NULL)
  47745                 goto iterator_close_exception;
  47746             prop = JS_GetProperty(ctx, groups, key_atom);
  47747         }
  47748         if (JS_IsException(prop))
  47749             goto exception;
  47750 
  47751         if (JS_IsUndefined(prop)) {
  47752             prop = JS_NewArray(ctx);
  47753             if (JS_IsException(prop))
  47754                 goto exception;
  47755             if (is_map) {
  47756                 args[0] = key;
  47757                 args[1] = prop;
  47758                 res = js_map_set(ctx, groups, 2, args, 0);
  47759                 if (JS_IsException(res))
  47760                     goto exception;
  47761                 JS_FreeValue(ctx, res);
  47762             } else {
  47763                 prop = JS_DupValue(ctx, prop);
  47764                 if (JS_DefinePropertyValue(ctx, groups, key_atom, prop,
  47765                                            JS_PROP_C_W_E) < 0) {
  47766                     goto exception;
  47767                 }
  47768             }
  47769         }
  47770         res = js_array_push(ctx, prop, 1, (JSValueConst *)&v, /*unshift*/0);
  47771         if (JS_IsException(res))
  47772             goto exception;
  47773         // res is an int64
  47774 
  47775         JS_FreeValue(ctx, prop);
  47776         JS_FreeValue(ctx, key);
  47777         JS_FreeAtom(ctx, key_atom);
  47778         JS_FreeValue(ctx, v);
  47779         prop = JS_UNDEFINED;
  47780         key = JS_UNDEFINED;
  47781         key_atom = JS_ATOM_NULL;
  47782         v = JS_UNDEFINED;
  47783     }
  47784 
  47785     JS_FreeValue(ctx, iter);
  47786     JS_FreeValue(ctx, next);
  47787     return groups;
  47788 
  47789  iterator_close_exception:
  47790     JS_IteratorClose(ctx, iter, TRUE);
  47791  exception:
  47792     JS_FreeAtom(ctx, key_atom);
  47793     JS_FreeValue(ctx, prop);
  47794     JS_FreeValue(ctx, key);
  47795     JS_FreeValue(ctx, v);
  47796     JS_FreeValue(ctx, groups);
  47797     JS_FreeValue(ctx, iter);
  47798     JS_FreeValue(ctx, next);
  47799     return JS_EXCEPTION;
  47800 }
  47801 
  47802 static void js_map_finalizer(JSRuntime *rt, JSValue val)
  47803 {
  47804     JSObject *p;
  47805     JSMapState *s;
  47806     struct list_head *el, *el1;
  47807     JSMapRecord *mr;
  47808 
  47809     p = JS_VALUE_GET_OBJ(val);
  47810     s = p->u.map_state;
  47811     if (s) {
  47812         /* if the object is deleted we are sure that no iterator is
  47813            using it */
  47814         list_for_each_safe(el, el1, &s->records) {
  47815             mr = list_entry(el, JSMapRecord, link);
  47816             if (!mr->empty) {
  47817                 if (s->is_weak)
  47818                     delete_weak_ref(rt, mr);
  47819                 else
  47820                     JS_FreeValueRT(rt, mr->key);
  47821                 JS_FreeValueRT(rt, mr->value);
  47822             }
  47823             js_free_rt(rt, mr);
  47824         }
  47825         js_free_rt(rt, s->hash_table);
  47826         js_free_rt(rt, s);
  47827     }
  47828 }
  47829 
  47830 static void js_map_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
  47831 {
  47832     JSObject *p = JS_VALUE_GET_OBJ(val);
  47833     JSMapState *s;
  47834     struct list_head *el;
  47835     JSMapRecord *mr;
  47836 
  47837     s = p->u.map_state;
  47838     if (s) {
  47839         list_for_each(el, &s->records) {
  47840             mr = list_entry(el, JSMapRecord, link);
  47841             if (!s->is_weak)
  47842                 JS_MarkValue(rt, mr->key, mark_func);
  47843             JS_MarkValue(rt, mr->value, mark_func);
  47844         }
  47845     }
  47846 }
  47847 
  47848 /* Map Iterator */
  47849 
  47850 typedef struct JSMapIteratorData {
  47851     JSValue obj;
  47852     JSIteratorKindEnum kind;
  47853     JSMapRecord *cur_record;
  47854 } JSMapIteratorData;
  47855 
  47856 static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val)
  47857 {
  47858     JSObject *p;
  47859     JSMapIteratorData *it;
  47860 
  47861     p = JS_VALUE_GET_OBJ(val);
  47862     it = p->u.map_iterator_data;
  47863     if (it) {
  47864         /* During the GC sweep phase the Map finalizer may be
  47865            called before the Map iterator finalizer */
  47866         if (JS_IsLiveObject(rt, it->obj) && it->cur_record) {
  47867             map_decref_record(rt, it->cur_record);
  47868         }
  47869         JS_FreeValueRT(rt, it->obj);
  47870         js_free_rt(rt, it);
  47871     }
  47872 }
  47873 
  47874 static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val,
  47875                                  JS_MarkFunc *mark_func)
  47876 {
  47877     JSObject *p = JS_VALUE_GET_OBJ(val);
  47878     JSMapIteratorData *it;
  47879     it = p->u.map_iterator_data;
  47880     if (it) {
  47881         /* the record is already marked by the object */
  47882         JS_MarkValue(rt, it->obj, mark_func);
  47883     }
  47884 }
  47885 
  47886 static JSValue js_create_map_iterator(JSContext *ctx, JSValueConst this_val,
  47887                                       int argc, JSValueConst *argv, int magic)
  47888 {
  47889     JSIteratorKindEnum kind;
  47890     JSMapState *s;
  47891     JSMapIteratorData *it;
  47892     JSValue enum_obj;
  47893 
  47894     kind = magic >> 2;
  47895     magic &= 3;
  47896     s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
  47897     if (!s)
  47898         return JS_EXCEPTION;
  47899     enum_obj = JS_NewObjectClass(ctx, JS_CLASS_MAP_ITERATOR + magic);
  47900     if (JS_IsException(enum_obj))
  47901         goto fail;
  47902     it = js_malloc(ctx, sizeof(*it));
  47903     if (!it) {
  47904         JS_FreeValue(ctx, enum_obj);
  47905         goto fail;
  47906     }
  47907     it->obj = JS_DupValue(ctx, this_val);
  47908     it->kind = kind;
  47909     it->cur_record = NULL;
  47910     JS_SetOpaque(enum_obj, it);
  47911     return enum_obj;
  47912  fail:
  47913     return JS_EXCEPTION;
  47914 }
  47915 
  47916 static JSValue js_map_iterator_next(JSContext *ctx, JSValueConst this_val,
  47917                                     int argc, JSValueConst *argv,
  47918                                     BOOL *pdone, int magic)
  47919 {
  47920     JSMapIteratorData *it;
  47921     JSMapState *s;
  47922     JSMapRecord *mr;
  47923     struct list_head *el;
  47924 
  47925     it = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP_ITERATOR + magic);
  47926     if (!it) {
  47927         *pdone = FALSE;
  47928         return JS_EXCEPTION;
  47929     }
  47930     if (JS_IsUndefined(it->obj))
  47931         goto done;
  47932     s = JS_GetOpaque(it->obj, JS_CLASS_MAP + magic);
  47933     assert(s != NULL);
  47934     if (!it->cur_record) {
  47935         el = s->records.next;
  47936     } else {
  47937         mr = it->cur_record;
  47938         el = mr->link.next;
  47939         map_decref_record(ctx->rt, mr); /* the record can be freed here */
  47940     }
  47941     for(;;) {
  47942         if (el == &s->records) {
  47943             /* no more record  */
  47944             it->cur_record = NULL;
  47945             JS_FreeValue(ctx, it->obj);
  47946             it->obj = JS_UNDEFINED;
  47947         done:
  47948             /* end of enumeration */
  47949             *pdone = TRUE;
  47950             return JS_UNDEFINED;
  47951         }
  47952         mr = list_entry(el, JSMapRecord, link);
  47953         if (!mr->empty)
  47954             break;
  47955         /* get the next record */
  47956         el = mr->link.next;
  47957     }
  47958 
  47959     /* lock the record so that it won't be freed */
  47960     mr->ref_count++;
  47961     it->cur_record = mr;
  47962     *pdone = FALSE;
  47963 
  47964     if (it->kind == JS_ITERATOR_KIND_KEY) {
  47965         return JS_DupValue(ctx, mr->key);
  47966     } else {
  47967         JSValueConst args[2];
  47968         args[0] = mr->key;
  47969         if (magic)
  47970             args[1] = mr->key;
  47971         else
  47972             args[1] = mr->value;
  47973         if (it->kind == JS_ITERATOR_KIND_VALUE) {
  47974             return JS_DupValue(ctx, args[1]);
  47975         } else {
  47976             return js_create_array(ctx, 2, args);
  47977         }
  47978     }
  47979 }
  47980 
  47981 static const JSCFunctionListEntry js_map_funcs[] = {
  47982     JS_CFUNC_MAGIC_DEF("groupBy", 2, js_object_groupBy, 1 ),
  47983     JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
  47984 };
  47985 
  47986 static const JSCFunctionListEntry js_map_proto_funcs[] = {
  47987     JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, 0 ),
  47988     JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, 0 ),
  47989     JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, 0 ),
  47990     JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, 0 ),
  47991     JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, 0 ),
  47992     JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, 0),
  47993     JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, 0 ),
  47994     JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_VALUE << 2) | 0 ),
  47995     JS_CFUNC_MAGIC_DEF("keys", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | 0 ),
  47996     JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | 0 ),
  47997     JS_ALIAS_DEF("[Symbol.iterator]", "entries" ),
  47998     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map", JS_PROP_CONFIGURABLE ),
  47999 };
  48000 
  48001 static const JSCFunctionListEntry js_map_iterator_proto_funcs[] = {
  48002     JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, 0 ),
  48003     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map Iterator", JS_PROP_CONFIGURABLE ),
  48004 };
  48005 
  48006 static const JSCFunctionListEntry js_set_proto_funcs[] = {
  48007     JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET ),
  48008     JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET ),
  48009     JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET ),
  48010     JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, MAGIC_SET ),
  48011     JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, MAGIC_SET ),
  48012     JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, MAGIC_SET ),
  48013     JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | MAGIC_SET ),
  48014     JS_ALIAS_DEF("keys", "values" ),
  48015     JS_ALIAS_DEF("[Symbol.iterator]", "values" ),
  48016     JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | MAGIC_SET ),
  48017     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set", JS_PROP_CONFIGURABLE ),
  48018 };
  48019 
  48020 static const JSCFunctionListEntry js_set_iterator_proto_funcs[] = {
  48021     JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, MAGIC_SET ),
  48022     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set Iterator", JS_PROP_CONFIGURABLE ),
  48023 };
  48024 
  48025 static const JSCFunctionListEntry js_weak_map_proto_funcs[] = {
  48026     JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, MAGIC_WEAK ),
  48027     JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, MAGIC_WEAK ),
  48028     JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_WEAK ),
  48029     JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_WEAK ),
  48030     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakMap", JS_PROP_CONFIGURABLE ),
  48031 };
  48032 
  48033 static const JSCFunctionListEntry js_weak_set_proto_funcs[] = {
  48034     JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET | MAGIC_WEAK ),
  48035     JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET | MAGIC_WEAK ),
  48036     JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET | MAGIC_WEAK ),
  48037     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakSet", JS_PROP_CONFIGURABLE ),
  48038 };
  48039 
  48040 static const JSCFunctionListEntry * const js_map_proto_funcs_ptr[6] = {
  48041     js_map_proto_funcs,
  48042     js_set_proto_funcs,
  48043     js_weak_map_proto_funcs,
  48044     js_weak_set_proto_funcs,
  48045     js_map_iterator_proto_funcs,
  48046     js_set_iterator_proto_funcs,
  48047 };
  48048 
  48049 static const uint8_t js_map_proto_funcs_count[6] = {
  48050     countof(js_map_proto_funcs),
  48051     countof(js_set_proto_funcs),
  48052     countof(js_weak_map_proto_funcs),
  48053     countof(js_weak_set_proto_funcs),
  48054     countof(js_map_iterator_proto_funcs),
  48055     countof(js_set_iterator_proto_funcs),
  48056 };
  48057 
  48058 void JS_AddIntrinsicMapSet(JSContext *ctx)
  48059 {
  48060     int i;
  48061     JSValue obj1;
  48062     char buf[ATOM_GET_STR_BUF_SIZE];
  48063 
  48064     for(i = 0; i < 4; i++) {
  48065         const char *name = JS_AtomGetStr(ctx, buf, sizeof(buf),
  48066                                          JS_ATOM_Map + i);
  48067         ctx->class_proto[JS_CLASS_MAP + i] = JS_NewObject(ctx);
  48068         JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP + i],
  48069                                    js_map_proto_funcs_ptr[i],
  48070                                    js_map_proto_funcs_count[i]);
  48071         obj1 = JS_NewCFunctionMagic(ctx, js_map_constructor, name, 0,
  48072                                     JS_CFUNC_constructor_magic, i);
  48073         if (i < 2) {
  48074             JS_SetPropertyFunctionList(ctx, obj1, js_map_funcs,
  48075                                        countof(js_map_funcs));
  48076         }
  48077         JS_NewGlobalCConstructor2(ctx, obj1, name, ctx->class_proto[JS_CLASS_MAP + i]);
  48078     }
  48079 
  48080     for(i = 0; i < 2; i++) {
  48081         ctx->class_proto[JS_CLASS_MAP_ITERATOR + i] =
  48082             JS_NewObjectProto(ctx, ctx->iterator_proto);
  48083         JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP_ITERATOR + i],
  48084                                    js_map_proto_funcs_ptr[i + 4],
  48085                                    js_map_proto_funcs_count[i + 4]);
  48086     }
  48087 }
  48088 
  48089 /* Generator */
  48090 static const JSCFunctionListEntry js_generator_function_proto_funcs[] = {
  48091     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "GeneratorFunction", JS_PROP_CONFIGURABLE),
  48092 };
  48093 
  48094 static const JSCFunctionListEntry js_generator_proto_funcs[] = {
  48095     JS_ITERATOR_NEXT_DEF("next", 1, js_generator_next, GEN_MAGIC_NEXT ),
  48096     JS_ITERATOR_NEXT_DEF("return", 1, js_generator_next, GEN_MAGIC_RETURN ),
  48097     JS_ITERATOR_NEXT_DEF("throw", 1, js_generator_next, GEN_MAGIC_THROW ),
  48098     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Generator", JS_PROP_CONFIGURABLE),
  48099 };
  48100 
  48101 /* Promise */
  48102 
  48103 typedef struct JSPromiseData {
  48104     JSPromiseStateEnum promise_state;
  48105     /* 0=fulfill, 1=reject, list of JSPromiseReactionData.link */
  48106     struct list_head promise_reactions[2];
  48107     BOOL is_handled; /* Note: only useful to debug */
  48108     JSValue promise_result;
  48109 } JSPromiseData;
  48110 
  48111 typedef struct JSPromiseFunctionDataResolved {
  48112     int ref_count;
  48113     BOOL already_resolved;
  48114 } JSPromiseFunctionDataResolved;
  48115 
  48116 typedef struct JSPromiseFunctionData {
  48117     JSValue promise;
  48118     JSPromiseFunctionDataResolved *presolved;
  48119 } JSPromiseFunctionData;
  48120 
  48121 typedef struct JSPromiseReactionData {
  48122     struct list_head link; /* not used in promise_reaction_job */
  48123     JSValue resolving_funcs[2];
  48124     JSValue handler;
  48125 } JSPromiseReactionData;
  48126 
  48127 JSPromiseStateEnum JS_PromiseState(JSContext *ctx, JSValue promise)
  48128 {
  48129     JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);
  48130     if (!s)
  48131         return -1;
  48132     return s->promise_state;
  48133 }
  48134 
  48135 JSValue JS_PromiseResult(JSContext *ctx, JSValue promise)
  48136 {
  48137     JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);
  48138     if (!s)
  48139         return JS_UNDEFINED;
  48140     return JS_DupValue(ctx, s->promise_result);
  48141 }
  48142 
  48143 static int js_create_resolving_functions(JSContext *ctx, JSValue *args,
  48144                                          JSValueConst promise);
  48145 
  48146 static void promise_reaction_data_free(JSRuntime *rt,
  48147                                        JSPromiseReactionData *rd)
  48148 {
  48149     JS_FreeValueRT(rt, rd->resolving_funcs[0]);
  48150     JS_FreeValueRT(rt, rd->resolving_funcs[1]);
  48151     JS_FreeValueRT(rt, rd->handler);
  48152     js_free_rt(rt, rd);
  48153 }
  48154 
  48155 static JSValue promise_reaction_job(JSContext *ctx, int argc,
  48156                                     JSValueConst *argv)
  48157 {
  48158     JSValueConst handler, arg, func;
  48159     JSValue res, res2;
  48160     BOOL is_reject;
  48161 
  48162     assert(argc == 5);
  48163     handler = argv[2];
  48164     is_reject = JS_ToBool(ctx, argv[3]);
  48165     arg = argv[4];
  48166 #ifdef DUMP_PROMISE
  48167     printf("promise_reaction_job: is_reject=%d\n", is_reject);
  48168 #endif
  48169 
  48170     if (JS_IsUndefined(handler)) {
  48171         if (is_reject) {
  48172             res = JS_Throw(ctx, JS_DupValue(ctx, arg));
  48173         } else {
  48174             res = JS_DupValue(ctx, arg);
  48175         }
  48176     } else {
  48177         res = JS_Call(ctx, handler, JS_UNDEFINED, 1, &arg);
  48178     }
  48179     is_reject = JS_IsException(res);
  48180     if (is_reject)
  48181         res = JS_GetException(ctx);
  48182     func = argv[is_reject];
  48183     /* as an extension, we support undefined as value to avoid
  48184        creating a dummy promise in the 'await' implementation of async
  48185        functions */
  48186     if (!JS_IsUndefined(func)) {
  48187         res2 = JS_Call(ctx, func, JS_UNDEFINED,
  48188                        1, (JSValueConst *)&res);
  48189     } else {
  48190         res2 = JS_UNDEFINED;
  48191     }
  48192     JS_FreeValue(ctx, res);
  48193 
  48194     return res2;
  48195 }
  48196 
  48197 void JS_SetHostPromiseRejectionTracker(JSRuntime *rt,
  48198                                        JSHostPromiseRejectionTracker *cb,
  48199                                        void *opaque)
  48200 {
  48201     rt->host_promise_rejection_tracker = cb;
  48202     rt->host_promise_rejection_tracker_opaque = opaque;
  48203 }
  48204 
  48205 static void fulfill_or_reject_promise(JSContext *ctx, JSValueConst promise,
  48206                                       JSValueConst value, BOOL is_reject)
  48207 {
  48208     JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);
  48209     struct list_head *el, *el1;
  48210     JSPromiseReactionData *rd;
  48211     JSValueConst args[5];
  48212 
  48213     if (!s || s->promise_state != JS_PROMISE_PENDING)
  48214         return; /* should never happen */
  48215     set_value(ctx, &s->promise_result, JS_DupValue(ctx, value));
  48216     s->promise_state = JS_PROMISE_FULFILLED + is_reject;
  48217 #ifdef DUMP_PROMISE
  48218     printf("fulfill_or_reject_promise: is_reject=%d\n", is_reject);
  48219 #endif
  48220     if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) {
  48221         JSRuntime *rt = ctx->rt;
  48222         if (rt->host_promise_rejection_tracker) {
  48223             rt->host_promise_rejection_tracker(ctx, promise, value, FALSE,
  48224                                                rt->host_promise_rejection_tracker_opaque);
  48225         }
  48226     }
  48227 
  48228     list_for_each_safe(el, el1, &s->promise_reactions[is_reject]) {
  48229         rd = list_entry(el, JSPromiseReactionData, link);
  48230         args[0] = rd->resolving_funcs[0];
  48231         args[1] = rd->resolving_funcs[1];
  48232         args[2] = rd->handler;
  48233         args[3] = JS_NewBool(ctx, is_reject);
  48234         args[4] = value;
  48235         JS_EnqueueJob(ctx, promise_reaction_job, 5, args);
  48236         list_del(&rd->link);
  48237         promise_reaction_data_free(ctx->rt, rd);
  48238     }
  48239 
  48240     list_for_each_safe(el, el1, &s->promise_reactions[1 - is_reject]) {
  48241         rd = list_entry(el, JSPromiseReactionData, link);
  48242         list_del(&rd->link);
  48243         promise_reaction_data_free(ctx->rt, rd);
  48244     }
  48245 }
  48246 
  48247 static void reject_promise(JSContext *ctx, JSValueConst promise,
  48248                            JSValueConst value)
  48249 {
  48250     fulfill_or_reject_promise(ctx, promise, value, TRUE);
  48251 }
  48252 
  48253 static JSValue js_promise_resolve_thenable_job(JSContext *ctx,
  48254                                                int argc, JSValueConst *argv)
  48255 {
  48256     JSValueConst promise, thenable, then;
  48257     JSValue args[2], res;
  48258 
  48259 #ifdef DUMP_PROMISE
  48260     printf("js_promise_resolve_thenable_job\n");
  48261 #endif
  48262     assert(argc == 3);
  48263     promise = argv[0];
  48264     thenable = argv[1];
  48265     then = argv[2];
  48266     if (js_create_resolving_functions(ctx, args, promise) < 0)
  48267         return JS_EXCEPTION;
  48268     res = JS_Call(ctx, then, thenable, 2, (JSValueConst *)args);
  48269     if (JS_IsException(res)) {
  48270         JSValue error = JS_GetException(ctx);
  48271         res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error);
  48272         JS_FreeValue(ctx, error);
  48273     }
  48274     JS_FreeValue(ctx, args[0]);
  48275     JS_FreeValue(ctx, args[1]);
  48276     return res;
  48277 }
  48278 
  48279 static void js_promise_resolve_function_free_resolved(JSRuntime *rt,
  48280                                                       JSPromiseFunctionDataResolved *sr)
  48281 {
  48282     if (--sr->ref_count == 0) {
  48283         js_free_rt(rt, sr);
  48284     }
  48285 }
  48286 
  48287 static int js_create_resolving_functions(JSContext *ctx,
  48288                                          JSValue *resolving_funcs,
  48289                                          JSValueConst promise)
  48290 
  48291 {
  48292     JSValue obj;
  48293     JSPromiseFunctionData *s;
  48294     JSPromiseFunctionDataResolved *sr;
  48295     int i, ret;
  48296 
  48297     sr = js_malloc(ctx, sizeof(*sr));
  48298     if (!sr)
  48299         return -1;
  48300     sr->ref_count = 1;
  48301     sr->already_resolved = FALSE; /* must be shared between the two functions */
  48302     ret = 0;
  48303     for(i = 0; i < 2; i++) {
  48304         obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
  48305                                      JS_CLASS_PROMISE_RESOLVE_FUNCTION + i);
  48306         if (JS_IsException(obj))
  48307             goto fail;
  48308         s = js_malloc(ctx, sizeof(*s));
  48309         if (!s) {
  48310             JS_FreeValue(ctx, obj);
  48311         fail:
  48312 
  48313             if (i != 0)
  48314                 JS_FreeValue(ctx, resolving_funcs[0]);
  48315             ret = -1;
  48316             break;
  48317         }
  48318         sr->ref_count++;
  48319         s->presolved = sr;
  48320         s->promise = JS_DupValue(ctx, promise);
  48321         JS_SetOpaque(obj, s);
  48322         js_function_set_properties(ctx, obj, JS_ATOM_empty_string, 1);
  48323         resolving_funcs[i] = obj;
  48324     }
  48325     js_promise_resolve_function_free_resolved(ctx->rt, sr);
  48326     return ret;
  48327 }
  48328 
  48329 static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val)
  48330 {
  48331     JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data;
  48332     if (s) {
  48333         js_promise_resolve_function_free_resolved(rt, s->presolved);
  48334         JS_FreeValueRT(rt, s->promise);
  48335         js_free_rt(rt, s);
  48336     }
  48337 }
  48338 
  48339 static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,
  48340                                              JS_MarkFunc *mark_func)
  48341 {
  48342     JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data;
  48343     if (s) {
  48344         JS_MarkValue(rt, s->promise, mark_func);
  48345     }
  48346 }
  48347 
  48348 static JSValue js_promise_resolve_function_call(JSContext *ctx,
  48349                                                 JSValueConst func_obj,
  48350                                                 JSValueConst this_val,
  48351                                                 int argc, JSValueConst *argv,
  48352                                                 int flags)
  48353 {
  48354     JSObject *p = JS_VALUE_GET_OBJ(func_obj);
  48355     JSPromiseFunctionData *s;
  48356     JSValueConst resolution, args[3];
  48357     JSValue then;
  48358     BOOL is_reject;
  48359 
  48360     s = p->u.promise_function_data;
  48361     if (!s || s->presolved->already_resolved)
  48362         return JS_UNDEFINED;
  48363     s->presolved->already_resolved = TRUE;
  48364     is_reject = p->class_id - JS_CLASS_PROMISE_RESOLVE_FUNCTION;
  48365     if (argc > 0)
  48366         resolution = argv[0];
  48367     else
  48368         resolution = JS_UNDEFINED;
  48369 #ifdef DUMP_PROMISE
  48370     printf("js_promise_resolving_function_call: is_reject=%d resolution=", is_reject);
  48371     JS_DumpValue(ctx, resolution);
  48372     printf("\n");
  48373 #endif
  48374     if (is_reject || !JS_IsObject(resolution)) {
  48375         goto done;
  48376     } else if (js_same_value(ctx, resolution, s->promise)) {
  48377         JS_ThrowTypeError(ctx, "promise self resolution");
  48378         goto fail_reject;
  48379     }
  48380     then = JS_GetProperty(ctx, resolution, JS_ATOM_then);
  48381     if (JS_IsException(then)) {
  48382         JSValue error;
  48383     fail_reject:
  48384         error = JS_GetException(ctx);
  48385         reject_promise(ctx, s->promise, error);
  48386         JS_FreeValue(ctx, error);
  48387     } else if (!JS_IsFunction(ctx, then)) {
  48388         JS_FreeValue(ctx, then);
  48389     done:
  48390         fulfill_or_reject_promise(ctx, s->promise, resolution, is_reject);
  48391     } else {
  48392         args[0] = s->promise;
  48393         args[1] = resolution;
  48394         args[2] = then;
  48395         JS_EnqueueJob(ctx, js_promise_resolve_thenable_job, 3, args);
  48396         JS_FreeValue(ctx, then);
  48397     }
  48398     return JS_UNDEFINED;
  48399 }
  48400 
  48401 static void js_promise_finalizer(JSRuntime *rt, JSValue val)
  48402 {
  48403     JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE);
  48404     struct list_head *el, *el1;
  48405     int i;
  48406 
  48407     if (!s)
  48408         return;
  48409     for(i = 0; i < 2; i++) {
  48410         list_for_each_safe(el, el1, &s->promise_reactions[i]) {
  48411             JSPromiseReactionData *rd =
  48412                 list_entry(el, JSPromiseReactionData, link);
  48413             promise_reaction_data_free(rt, rd);
  48414         }
  48415     }
  48416     JS_FreeValueRT(rt, s->promise_result);
  48417     js_free_rt(rt, s);
  48418 }
  48419 
  48420 static void js_promise_mark(JSRuntime *rt, JSValueConst val,
  48421                             JS_MarkFunc *mark_func)
  48422 {
  48423     JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE);
  48424     struct list_head *el;
  48425     int i;
  48426 
  48427     if (!s)
  48428         return;
  48429     for(i = 0; i < 2; i++) {
  48430         list_for_each(el, &s->promise_reactions[i]) {
  48431             JSPromiseReactionData *rd =
  48432                 list_entry(el, JSPromiseReactionData, link);
  48433             JS_MarkValue(rt, rd->resolving_funcs[0], mark_func);
  48434             JS_MarkValue(rt, rd->resolving_funcs[1], mark_func);
  48435             JS_MarkValue(rt, rd->handler, mark_func);
  48436         }
  48437     }
  48438     JS_MarkValue(rt, s->promise_result, mark_func);
  48439 }
  48440 
  48441 static JSValue js_promise_constructor(JSContext *ctx, JSValueConst new_target,
  48442                                       int argc, JSValueConst *argv)
  48443 {
  48444     JSValueConst executor;
  48445     JSValue obj;
  48446     JSPromiseData *s;
  48447     JSValue args[2], ret;
  48448     int i;
  48449 
  48450     executor = argv[0];
  48451     if (check_function(ctx, executor))
  48452         return JS_EXCEPTION;
  48453     obj = js_create_from_ctor(ctx, new_target, JS_CLASS_PROMISE);
  48454     if (JS_IsException(obj))
  48455         return JS_EXCEPTION;
  48456     s = js_mallocz(ctx, sizeof(*s));
  48457     if (!s)
  48458         goto fail;
  48459     s->promise_state = JS_PROMISE_PENDING;
  48460     s->is_handled = FALSE;
  48461     for(i = 0; i < 2; i++)
  48462         init_list_head(&s->promise_reactions[i]);
  48463     s->promise_result = JS_UNDEFINED;
  48464     JS_SetOpaque(obj, s);
  48465     if (js_create_resolving_functions(ctx, args, obj))
  48466         goto fail;
  48467     ret = JS_Call(ctx, executor, JS_UNDEFINED, 2, (JSValueConst *)args);
  48468     if (JS_IsException(ret)) {
  48469         JSValue ret2, error;
  48470         error = JS_GetException(ctx);
  48471         ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error);
  48472         JS_FreeValue(ctx, error);
  48473         if (JS_IsException(ret2))
  48474             goto fail1;
  48475         JS_FreeValue(ctx, ret2);
  48476     }
  48477     JS_FreeValue(ctx, ret);
  48478     JS_FreeValue(ctx, args[0]);
  48479     JS_FreeValue(ctx, args[1]);
  48480     return obj;
  48481  fail1:
  48482     JS_FreeValue(ctx, args[0]);
  48483     JS_FreeValue(ctx, args[1]);
  48484  fail:
  48485     JS_FreeValue(ctx, obj);
  48486     return JS_EXCEPTION;
  48487 }
  48488 
  48489 static JSValue js_promise_executor(JSContext *ctx,
  48490                                    JSValueConst this_val,
  48491                                    int argc, JSValueConst *argv,
  48492                                    int magic, JSValue *func_data)
  48493 {
  48494     int i;
  48495 
  48496     for(i = 0; i < 2; i++) {
  48497         if (!JS_IsUndefined(func_data[i]))
  48498             return JS_ThrowTypeError(ctx, "resolving function already set");
  48499         func_data[i] = JS_DupValue(ctx, argv[i]);
  48500     }
  48501     return JS_UNDEFINED;
  48502 }
  48503 
  48504 static JSValue js_promise_executor_new(JSContext *ctx)
  48505 {
  48506     JSValueConst func_data[2];
  48507 
  48508     func_data[0] = JS_UNDEFINED;
  48509     func_data[1] = JS_UNDEFINED;
  48510     return JS_NewCFunctionData(ctx, js_promise_executor, 2,
  48511                                0, 2, func_data);
  48512 }
  48513 
  48514 static JSValue js_new_promise_capability(JSContext *ctx,
  48515                                          JSValue *resolving_funcs,
  48516                                          JSValueConst ctor)
  48517 {
  48518     JSValue executor, result_promise;
  48519     JSCFunctionDataRecord *s;
  48520     int i;
  48521 
  48522     executor = js_promise_executor_new(ctx);
  48523     if (JS_IsException(executor))
  48524         return executor;
  48525 
  48526     if (JS_IsUndefined(ctor)) {
  48527         result_promise = js_promise_constructor(ctx, ctor, 1,
  48528                                                 (JSValueConst *)&executor);
  48529     } else {
  48530         result_promise = JS_CallConstructor(ctx, ctor, 1,
  48531                                             (JSValueConst *)&executor);
  48532     }
  48533     if (JS_IsException(result_promise))
  48534         goto fail;
  48535     s = JS_GetOpaque(executor, JS_CLASS_C_FUNCTION_DATA);
  48536     for(i = 0; i < 2; i++) {
  48537         if (check_function(ctx, s->data[i]))
  48538             goto fail;
  48539     }
  48540     for(i = 0; i < 2; i++)
  48541         resolving_funcs[i] = JS_DupValue(ctx, s->data[i]);
  48542     JS_FreeValue(ctx, executor);
  48543     return result_promise;
  48544  fail:
  48545     JS_FreeValue(ctx, executor);
  48546     JS_FreeValue(ctx, result_promise);
  48547     return JS_EXCEPTION;
  48548 }
  48549 
  48550 JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs)
  48551 {
  48552     return js_new_promise_capability(ctx, resolving_funcs, JS_UNDEFINED);
  48553 }
  48554 
  48555 static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val,
  48556                                   int argc, JSValueConst *argv, int magic)
  48557 {
  48558     JSValue result_promise, resolving_funcs[2], ret;
  48559     BOOL is_reject = magic;
  48560 
  48561     if (!JS_IsObject(this_val))
  48562         return JS_ThrowTypeErrorNotAnObject(ctx);
  48563     if (!is_reject && JS_GetOpaque(argv[0], JS_CLASS_PROMISE)) {
  48564         JSValue ctor;
  48565         BOOL is_same;
  48566         ctor = JS_GetProperty(ctx, argv[0], JS_ATOM_constructor);
  48567         if (JS_IsException(ctor))
  48568             return ctor;
  48569         is_same = js_same_value(ctx, ctor, this_val);
  48570         JS_FreeValue(ctx, ctor);
  48571         if (is_same)
  48572             return JS_DupValue(ctx, argv[0]);
  48573     }
  48574     result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
  48575     if (JS_IsException(result_promise))
  48576         return result_promise;
  48577     ret = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 1, argv);
  48578     JS_FreeValue(ctx, resolving_funcs[0]);
  48579     JS_FreeValue(ctx, resolving_funcs[1]);
  48580     if (JS_IsException(ret)) {
  48581         JS_FreeValue(ctx, result_promise);
  48582         return ret;
  48583     }
  48584     JS_FreeValue(ctx, ret);
  48585     return result_promise;
  48586 }
  48587 
  48588 static JSValue js_promise_withResolvers(JSContext *ctx,
  48589                                         JSValueConst this_val,
  48590                                         int argc, JSValueConst *argv)
  48591 {
  48592     JSValue result_promise, resolving_funcs[2], obj;
  48593     if (!JS_IsObject(this_val))
  48594         return JS_ThrowTypeErrorNotAnObject(ctx);
  48595     result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
  48596     if (JS_IsException(result_promise))
  48597         return result_promise;
  48598     obj = JS_NewObject(ctx);
  48599     if (JS_IsException(obj)) {
  48600         JS_FreeValue(ctx, resolving_funcs[0]);
  48601         JS_FreeValue(ctx, resolving_funcs[1]);
  48602         JS_FreeValue(ctx, result_promise);
  48603         return JS_EXCEPTION;
  48604     }
  48605     JS_DefinePropertyValue(ctx, obj, JS_ATOM_promise, result_promise, JS_PROP_C_W_E);
  48606     JS_DefinePropertyValue(ctx, obj, JS_ATOM_resolve, resolving_funcs[0], JS_PROP_C_W_E);
  48607     JS_DefinePropertyValue(ctx, obj, JS_ATOM_reject, resolving_funcs[1], JS_PROP_C_W_E);
  48608     return obj;
  48609 }
  48610 
  48611 static __exception int remainingElementsCount_add(JSContext *ctx,
  48612                                                   JSValueConst resolve_element_env,
  48613                                                   int addend)
  48614 {
  48615     JSValue val;
  48616     int remainingElementsCount;
  48617 
  48618     val = JS_GetPropertyUint32(ctx, resolve_element_env, 0);
  48619     if (JS_IsException(val))
  48620         return -1;
  48621     if (JS_ToInt32Free(ctx, &remainingElementsCount, val))
  48622         return -1;
  48623     remainingElementsCount += addend;
  48624     if (JS_SetPropertyUint32(ctx, resolve_element_env, 0,
  48625                              JS_NewInt32(ctx, remainingElementsCount)) < 0)
  48626         return -1;
  48627     return (remainingElementsCount == 0);
  48628 }
  48629 
  48630 #define PROMISE_MAGIC_all        0
  48631 #define PROMISE_MAGIC_allSettled 1
  48632 #define PROMISE_MAGIC_any        2
  48633 
  48634 static JSValue js_promise_all_resolve_element(JSContext *ctx,
  48635                                               JSValueConst this_val,
  48636                                               int argc, JSValueConst *argv,
  48637                                               int magic,
  48638                                               JSValue *func_data)
  48639 {
  48640     int resolve_type = magic & 3;
  48641     int is_reject = magic & 4;
  48642     BOOL alreadyCalled = JS_ToBool(ctx, func_data[0]);
  48643     JSValueConst values = func_data[2];
  48644     JSValueConst resolve = func_data[3];
  48645     JSValueConst resolve_element_env = func_data[4];
  48646     JSValue ret, obj;
  48647     int is_zero, index;
  48648 
  48649     if (JS_ToInt32(ctx, &index, func_data[1]))
  48650         return JS_EXCEPTION;
  48651     if (alreadyCalled)
  48652         return JS_UNDEFINED;
  48653     func_data[0] = JS_NewBool(ctx, TRUE);
  48654 
  48655     if (resolve_type == PROMISE_MAGIC_allSettled) {
  48656         JSValue str;
  48657 
  48658         obj = JS_NewObject(ctx);
  48659         if (JS_IsException(obj))
  48660             return JS_EXCEPTION;
  48661         str = JS_NewString(ctx, is_reject ? "rejected" : "fulfilled");
  48662         if (JS_IsException(str))
  48663             goto fail1;
  48664         if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_status,
  48665                                    str,
  48666                                    JS_PROP_C_W_E) < 0)
  48667             goto fail1;
  48668         if (JS_DefinePropertyValue(ctx, obj,
  48669                                    is_reject ? JS_ATOM_reason : JS_ATOM_value,
  48670                                    JS_DupValue(ctx, argv[0]),
  48671                                    JS_PROP_C_W_E) < 0) {
  48672         fail1:
  48673             JS_FreeValue(ctx, obj);
  48674             return JS_EXCEPTION;
  48675         }
  48676     } else {
  48677         obj = JS_DupValue(ctx, argv[0]);
  48678     }
  48679     if (JS_DefinePropertyValueUint32(ctx, values, index,
  48680                                      obj, JS_PROP_C_W_E) < 0)
  48681         return JS_EXCEPTION;
  48682 
  48683     is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1);
  48684     if (is_zero < 0)
  48685         return JS_EXCEPTION;
  48686     if (is_zero) {
  48687         if (resolve_type == PROMISE_MAGIC_any) {
  48688             JSValue error;
  48689             error = js_aggregate_error_constructor(ctx, values);
  48690             if (JS_IsException(error))
  48691                 return JS_EXCEPTION;
  48692             ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&error);
  48693             JS_FreeValue(ctx, error);
  48694         } else {
  48695             ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&values);
  48696         }
  48697         if (JS_IsException(ret))
  48698             return ret;
  48699         JS_FreeValue(ctx, ret);
  48700     }
  48701     return JS_UNDEFINED;
  48702 }
  48703 
  48704 /* magic = 0: Promise.all 1: Promise.allSettled */
  48705 static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val,
  48706                               int argc, JSValueConst *argv, int magic)
  48707 {
  48708     JSValue result_promise, resolving_funcs[2], item, next_promise, ret;
  48709     JSValue next_method = JS_UNDEFINED, values = JS_UNDEFINED;
  48710     JSValue resolve_element_env = JS_UNDEFINED, resolve_element, reject_element;
  48711     JSValue promise_resolve = JS_UNDEFINED, iter = JS_UNDEFINED;
  48712     JSValueConst then_args[2], resolve_element_data[5];
  48713     BOOL done;
  48714     int index, is_zero, is_promise_any = (magic == PROMISE_MAGIC_any);
  48715 
  48716     if (!JS_IsObject(this_val))
  48717         return JS_ThrowTypeErrorNotAnObject(ctx);
  48718     result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
  48719     if (JS_IsException(result_promise))
  48720         return result_promise;
  48721     promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve);
  48722     if (JS_IsException(promise_resolve) ||
  48723         check_function(ctx, promise_resolve))
  48724         goto fail_reject;
  48725     iter = JS_GetIterator(ctx, argv[0], FALSE);
  48726     if (JS_IsException(iter)) {
  48727         JSValue error;
  48728     fail_reject:
  48729         error = JS_GetException(ctx);
  48730         ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1,
  48731                        (JSValueConst *)&error);
  48732         JS_FreeValue(ctx, error);
  48733         if (JS_IsException(ret))
  48734             goto fail;
  48735         JS_FreeValue(ctx, ret);
  48736     } else {
  48737         next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
  48738         if (JS_IsException(next_method))
  48739             goto fail_reject;
  48740         values = JS_NewArray(ctx);
  48741         if (JS_IsException(values))
  48742             goto fail_reject;
  48743         resolve_element_env = JS_NewArray(ctx);
  48744         if (JS_IsException(resolve_element_env))
  48745             goto fail_reject;
  48746         /* remainingElementsCount field */
  48747         if (JS_DefinePropertyValueUint32(ctx, resolve_element_env, 0,
  48748                                          JS_NewInt32(ctx, 1),
  48749                                          JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0)
  48750             goto fail_reject;
  48751 
  48752         index = 0;
  48753         for(;;) {
  48754             /* XXX: conformance: should close the iterator if error on 'done'
  48755                access, but not on 'value' access */
  48756             item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
  48757             if (JS_IsException(item))
  48758                 goto fail_reject;
  48759             if (done)
  48760                 break;
  48761             next_promise = JS_Call(ctx, promise_resolve,
  48762                                    this_val, 1, (JSValueConst *)&item);
  48763             JS_FreeValue(ctx, item);
  48764             if (JS_IsException(next_promise)) {
  48765             fail_reject1:
  48766                 JS_IteratorClose(ctx, iter, TRUE);
  48767                 goto fail_reject;
  48768             }
  48769             resolve_element_data[0] = JS_NewBool(ctx, FALSE);
  48770             resolve_element_data[1] = (JSValueConst)JS_NewInt32(ctx, index);
  48771             resolve_element_data[2] = values;
  48772             resolve_element_data[3] = resolving_funcs[is_promise_any];
  48773             resolve_element_data[4] = resolve_element_env;
  48774             resolve_element =
  48775                 JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1,
  48776                                     magic, 5, resolve_element_data);
  48777             if (JS_IsException(resolve_element)) {
  48778                 JS_FreeValue(ctx, next_promise);
  48779                 goto fail_reject1;
  48780             }
  48781 
  48782             if (magic == PROMISE_MAGIC_allSettled) {
  48783                 reject_element =
  48784                     JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1,
  48785                                         magic | 4, 5, resolve_element_data);
  48786                 if (JS_IsException(reject_element)) {
  48787                     JS_FreeValue(ctx, next_promise);
  48788                     goto fail_reject1;
  48789                 }
  48790             } else if (magic == PROMISE_MAGIC_any) {
  48791                 if (JS_DefinePropertyValueUint32(ctx, values, index,
  48792                                                  JS_UNDEFINED, JS_PROP_C_W_E) < 0)
  48793                     goto fail_reject1;
  48794                 reject_element = resolve_element;
  48795                 resolve_element = JS_DupValue(ctx, resolving_funcs[0]);
  48796             } else {
  48797                 reject_element = JS_DupValue(ctx, resolving_funcs[1]);
  48798             }
  48799 
  48800             if (remainingElementsCount_add(ctx, resolve_element_env, 1) < 0) {
  48801                 JS_FreeValue(ctx, next_promise);
  48802                 JS_FreeValue(ctx, resolve_element);
  48803                 JS_FreeValue(ctx, reject_element);
  48804                 goto fail_reject1;
  48805             }
  48806 
  48807             then_args[0] = resolve_element;
  48808             then_args[1] = reject_element;
  48809             ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, then_args);
  48810             JS_FreeValue(ctx, resolve_element);
  48811             JS_FreeValue(ctx, reject_element);
  48812             if (check_exception_free(ctx, ret))
  48813                 goto fail_reject1;
  48814             index++;
  48815         }
  48816 
  48817         is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1);
  48818         if (is_zero < 0)
  48819             goto fail_reject;
  48820         if (is_zero) {
  48821             if (magic == PROMISE_MAGIC_any) {
  48822                 JSValue error;
  48823                 error = js_aggregate_error_constructor(ctx, values);
  48824                 if (JS_IsException(error))
  48825                     goto fail_reject;
  48826                 JS_FreeValue(ctx, values);
  48827                 values = error;
  48828             }
  48829             ret = JS_Call(ctx, resolving_funcs[is_promise_any], JS_UNDEFINED,
  48830                           1, (JSValueConst *)&values);
  48831             if (check_exception_free(ctx, ret))
  48832                 goto fail_reject;
  48833         }
  48834     }
  48835  done:
  48836     JS_FreeValue(ctx, promise_resolve);
  48837     JS_FreeValue(ctx, resolve_element_env);
  48838     JS_FreeValue(ctx, values);
  48839     JS_FreeValue(ctx, next_method);
  48840     JS_FreeValue(ctx, iter);
  48841     JS_FreeValue(ctx, resolving_funcs[0]);
  48842     JS_FreeValue(ctx, resolving_funcs[1]);
  48843     return result_promise;
  48844  fail:
  48845     JS_FreeValue(ctx, result_promise);
  48846     result_promise = JS_EXCEPTION;
  48847     goto done;
  48848 }
  48849 
  48850 static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val,
  48851                                int argc, JSValueConst *argv)
  48852 {
  48853     JSValue result_promise, resolving_funcs[2], item, next_promise, ret;
  48854     JSValue next_method = JS_UNDEFINED, iter = JS_UNDEFINED;
  48855     JSValue promise_resolve = JS_UNDEFINED;
  48856     BOOL done;
  48857 
  48858     if (!JS_IsObject(this_val))
  48859         return JS_ThrowTypeErrorNotAnObject(ctx);
  48860     result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
  48861     if (JS_IsException(result_promise))
  48862         return result_promise;
  48863     promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve);
  48864     if (JS_IsException(promise_resolve) ||
  48865         check_function(ctx, promise_resolve))
  48866         goto fail_reject;
  48867     iter = JS_GetIterator(ctx, argv[0], FALSE);
  48868     if (JS_IsException(iter)) {
  48869         JSValue error;
  48870     fail_reject:
  48871         error = JS_GetException(ctx);
  48872         ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1,
  48873                        (JSValueConst *)&error);
  48874         JS_FreeValue(ctx, error);
  48875         if (JS_IsException(ret))
  48876             goto fail;
  48877         JS_FreeValue(ctx, ret);
  48878     } else {
  48879         next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
  48880         if (JS_IsException(next_method))
  48881             goto fail_reject;
  48882 
  48883         for(;;) {
  48884             /* XXX: conformance: should close the iterator if error on 'done'
  48885                access, but not on 'value' access */
  48886             item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
  48887             if (JS_IsException(item))
  48888                 goto fail_reject;
  48889             if (done)
  48890                 break;
  48891             next_promise = JS_Call(ctx, promise_resolve,
  48892                                    this_val, 1, (JSValueConst *)&item);
  48893             JS_FreeValue(ctx, item);
  48894             if (JS_IsException(next_promise)) {
  48895             fail_reject1:
  48896                 JS_IteratorClose(ctx, iter, TRUE);
  48897                 goto fail_reject;
  48898             }
  48899             ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2,
  48900                                 (JSValueConst *)resolving_funcs);
  48901             if (check_exception_free(ctx, ret))
  48902                 goto fail_reject1;
  48903         }
  48904     }
  48905  done:
  48906     JS_FreeValue(ctx, promise_resolve);
  48907     JS_FreeValue(ctx, next_method);
  48908     JS_FreeValue(ctx, iter);
  48909     JS_FreeValue(ctx, resolving_funcs[0]);
  48910     JS_FreeValue(ctx, resolving_funcs[1]);
  48911     return result_promise;
  48912  fail:
  48913     //JS_FreeValue(ctx, next_method); // why not???
  48914     JS_FreeValue(ctx, result_promise);
  48915     result_promise = JS_EXCEPTION;
  48916     goto done;
  48917 }
  48918 
  48919 static __exception int perform_promise_then(JSContext *ctx,
  48920                                             JSValueConst promise,
  48921                                             JSValueConst *resolve_reject,
  48922                                             JSValueConst *cap_resolving_funcs)
  48923 {
  48924     JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);
  48925     JSPromiseReactionData *rd_array[2], *rd;
  48926     int i, j;
  48927 
  48928     rd_array[0] = NULL;
  48929     rd_array[1] = NULL;
  48930     for(i = 0; i < 2; i++) {
  48931         JSValueConst handler;
  48932         rd = js_mallocz(ctx, sizeof(*rd));
  48933         if (!rd) {
  48934             if (i == 1)
  48935                 promise_reaction_data_free(ctx->rt, rd_array[0]);
  48936             return -1;
  48937         }
  48938         for(j = 0; j < 2; j++)
  48939             rd->resolving_funcs[j] = JS_DupValue(ctx, cap_resolving_funcs[j]);
  48940         handler = resolve_reject[i];
  48941         if (!JS_IsFunction(ctx, handler))
  48942             handler = JS_UNDEFINED;
  48943         rd->handler = JS_DupValue(ctx, handler);
  48944         rd_array[i] = rd;
  48945     }
  48946 
  48947     if (s->promise_state == JS_PROMISE_PENDING) {
  48948         for(i = 0; i < 2; i++)
  48949             list_add_tail(&rd_array[i]->link, &s->promise_reactions[i]);
  48950     } else {
  48951         JSValueConst args[5];
  48952         if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) {
  48953             JSRuntime *rt = ctx->rt;
  48954             if (rt->host_promise_rejection_tracker) {
  48955                 rt->host_promise_rejection_tracker(ctx, promise, s->promise_result,
  48956                                                    TRUE, rt->host_promise_rejection_tracker_opaque);
  48957             }
  48958         }
  48959         i = s->promise_state - JS_PROMISE_FULFILLED;
  48960         rd = rd_array[i];
  48961         args[0] = rd->resolving_funcs[0];
  48962         args[1] = rd->resolving_funcs[1];
  48963         args[2] = rd->handler;
  48964         args[3] = JS_NewBool(ctx, i);
  48965         args[4] = s->promise_result;
  48966         JS_EnqueueJob(ctx, promise_reaction_job, 5, args);
  48967         for(i = 0; i < 2; i++)
  48968             promise_reaction_data_free(ctx->rt, rd_array[i]);
  48969     }
  48970     s->is_handled = TRUE;
  48971     return 0;
  48972 }
  48973 
  48974 static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val,
  48975                                int argc, JSValueConst *argv)
  48976 {
  48977     JSValue ctor, result_promise, resolving_funcs[2];
  48978     JSPromiseData *s;
  48979     int i, ret;
  48980 
  48981     s = JS_GetOpaque2(ctx, this_val, JS_CLASS_PROMISE);
  48982     if (!s)
  48983         return JS_EXCEPTION;
  48984 
  48985     ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);
  48986     if (JS_IsException(ctor))
  48987         return ctor;
  48988     result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor);
  48989     JS_FreeValue(ctx, ctor);
  48990     if (JS_IsException(result_promise))
  48991         return result_promise;
  48992     ret = perform_promise_then(ctx, this_val, argv,
  48993                                (JSValueConst *)resolving_funcs);
  48994     for(i = 0; i < 2; i++)
  48995         JS_FreeValue(ctx, resolving_funcs[i]);
  48996     if (ret) {
  48997         JS_FreeValue(ctx, result_promise);
  48998         return JS_EXCEPTION;
  48999     }
  49000     return result_promise;
  49001 }
  49002 
  49003 static JSValue js_promise_catch(JSContext *ctx, JSValueConst this_val,
  49004                                 int argc, JSValueConst *argv)
  49005 {
  49006     JSValueConst args[2];
  49007     args[0] = JS_UNDEFINED;
  49008     args[1] = argv[0];
  49009     return JS_Invoke(ctx, this_val, JS_ATOM_then, 2, args);
  49010 }
  49011 
  49012 static JSValue js_promise_finally_value_thunk(JSContext *ctx, JSValueConst this_val,
  49013                                               int argc, JSValueConst *argv,
  49014                                               int magic, JSValue *func_data)
  49015 {
  49016     return JS_DupValue(ctx, func_data[0]);
  49017 }
  49018 
  49019 static JSValue js_promise_finally_thrower(JSContext *ctx, JSValueConst this_val,
  49020                                           int argc, JSValueConst *argv,
  49021                                           int magic, JSValue *func_data)
  49022 {
  49023     return JS_Throw(ctx, JS_DupValue(ctx, func_data[0]));
  49024 }
  49025 
  49026 static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_val,
  49027                                             int argc, JSValueConst *argv,
  49028                                             int magic, JSValue *func_data)
  49029 {
  49030     JSValueConst ctor = func_data[0];
  49031     JSValueConst onFinally = func_data[1];
  49032     JSValue res, promise, ret, then_func;
  49033 
  49034     res = JS_Call(ctx, onFinally, JS_UNDEFINED, 0, NULL);
  49035     if (JS_IsException(res))
  49036         return res;
  49037     promise = js_promise_resolve(ctx, ctor, 1, (JSValueConst *)&res, 0);
  49038     JS_FreeValue(ctx, res);
  49039     if (JS_IsException(promise))
  49040         return promise;
  49041     if (magic == 0) {
  49042         then_func = JS_NewCFunctionData(ctx, js_promise_finally_value_thunk, 0,
  49043                                         0, 1, argv);
  49044     } else {
  49045         then_func = JS_NewCFunctionData(ctx, js_promise_finally_thrower, 0,
  49046                                         0, 1, argv);
  49047     }
  49048     if (JS_IsException(then_func)) {
  49049         JS_FreeValue(ctx, promise);
  49050         return then_func;
  49051     }
  49052     ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, (JSValueConst *)&then_func);
  49053     JS_FreeValue(ctx, then_func);
  49054     return ret;
  49055 }
  49056 
  49057 static JSValue js_promise_finally(JSContext *ctx, JSValueConst this_val,
  49058                                   int argc, JSValueConst *argv)
  49059 {
  49060     JSValueConst onFinally = argv[0];
  49061     JSValue ctor, ret;
  49062     JSValue then_funcs[2];
  49063     JSValueConst func_data[2];
  49064     int i;
  49065 
  49066     ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);
  49067     if (JS_IsException(ctor))
  49068         return ctor;
  49069     if (!JS_IsFunction(ctx, onFinally)) {
  49070         then_funcs[0] = JS_DupValue(ctx, onFinally);
  49071         then_funcs[1] = JS_DupValue(ctx, onFinally);
  49072     } else {
  49073         func_data[0] = ctor;
  49074         func_data[1] = onFinally;
  49075         for(i = 0; i < 2; i++) {
  49076             then_funcs[i] = JS_NewCFunctionData(ctx, js_promise_then_finally_func, 1, i, 2, func_data);
  49077             if (JS_IsException(then_funcs[i])) {
  49078                 if (i == 1)
  49079                     JS_FreeValue(ctx, then_funcs[0]);
  49080                 JS_FreeValue(ctx, ctor);
  49081                 return JS_EXCEPTION;
  49082             }
  49083         }
  49084     }
  49085     JS_FreeValue(ctx, ctor);
  49086     ret = JS_Invoke(ctx, this_val, JS_ATOM_then, 2, (JSValueConst *)then_funcs);
  49087     JS_FreeValue(ctx, then_funcs[0]);
  49088     JS_FreeValue(ctx, then_funcs[1]);
  49089     return ret;
  49090 }
  49091 
  49092 static const JSCFunctionListEntry js_promise_funcs[] = {
  49093     JS_CFUNC_MAGIC_DEF("resolve", 1, js_promise_resolve, 0 ),
  49094     JS_CFUNC_MAGIC_DEF("reject", 1, js_promise_resolve, 1 ),
  49095     JS_CFUNC_MAGIC_DEF("all", 1, js_promise_all, PROMISE_MAGIC_all ),
  49096     JS_CFUNC_MAGIC_DEF("allSettled", 1, js_promise_all, PROMISE_MAGIC_allSettled ),
  49097     JS_CFUNC_MAGIC_DEF("any", 1, js_promise_all, PROMISE_MAGIC_any ),
  49098     JS_CFUNC_DEF("race", 1, js_promise_race ),
  49099     JS_CFUNC_DEF("withResolvers", 0, js_promise_withResolvers ),
  49100     JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL),
  49101 };
  49102 
  49103 static const JSCFunctionListEntry js_promise_proto_funcs[] = {
  49104     JS_CFUNC_DEF("then", 2, js_promise_then ),
  49105     JS_CFUNC_DEF("catch", 1, js_promise_catch ),
  49106     JS_CFUNC_DEF("finally", 1, js_promise_finally ),
  49107     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Promise", JS_PROP_CONFIGURABLE ),
  49108 };
  49109 
  49110 /* AsyncFunction */
  49111 static const JSCFunctionListEntry js_async_function_proto_funcs[] = {
  49112     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncFunction", JS_PROP_CONFIGURABLE ),
  49113 };
  49114 
  49115 static JSValue js_async_from_sync_iterator_unwrap(JSContext *ctx,
  49116                                                   JSValueConst this_val,
  49117                                                   int argc, JSValueConst *argv,
  49118                                                   int magic, JSValue *func_data)
  49119 {
  49120     return js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]),
  49121                                      JS_ToBool(ctx, func_data[0]));
  49122 }
  49123 
  49124 static JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx,
  49125                                                               BOOL done)
  49126 {
  49127     JSValueConst func_data[1];
  49128 
  49129     func_data[0] = (JSValueConst)JS_NewBool(ctx, done);
  49130     return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_unwrap,
  49131                                1, 0, 1, func_data);
  49132 }
  49133 
  49134 /* AsyncIteratorPrototype */
  49135 
  49136 static const JSCFunctionListEntry js_async_iterator_proto_funcs[] = {
  49137     JS_CFUNC_DEF("[Symbol.asyncIterator]", 0, js_iterator_proto_iterator ),
  49138 };
  49139 
  49140 /* AsyncFromSyncIteratorPrototype */
  49141 
  49142 typedef struct JSAsyncFromSyncIteratorData {
  49143     JSValue sync_iter;
  49144     JSValue next_method;
  49145 } JSAsyncFromSyncIteratorData;
  49146 
  49147 static void js_async_from_sync_iterator_finalizer(JSRuntime *rt, JSValue val)
  49148 {
  49149     JSAsyncFromSyncIteratorData *s =
  49150         JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
  49151     if (s) {
  49152         JS_FreeValueRT(rt, s->sync_iter);
  49153         JS_FreeValueRT(rt, s->next_method);
  49154         js_free_rt(rt, s);
  49155     }
  49156 }
  49157 
  49158 static void js_async_from_sync_iterator_mark(JSRuntime *rt, JSValueConst val,
  49159                                              JS_MarkFunc *mark_func)
  49160 {
  49161     JSAsyncFromSyncIteratorData *s =
  49162         JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
  49163     if (s) {
  49164         JS_MarkValue(rt, s->sync_iter, mark_func);
  49165         JS_MarkValue(rt, s->next_method, mark_func);
  49166     }
  49167 }
  49168 
  49169 static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx,
  49170                                               JSValueConst sync_iter)
  49171 {
  49172     JSValue async_iter, next_method;
  49173     JSAsyncFromSyncIteratorData *s;
  49174 
  49175     next_method = JS_GetProperty(ctx, sync_iter, JS_ATOM_next);
  49176     if (JS_IsException(next_method))
  49177         return JS_EXCEPTION;
  49178     async_iter = JS_NewObjectClass(ctx, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
  49179     if (JS_IsException(async_iter)) {
  49180         JS_FreeValue(ctx, next_method);
  49181         return async_iter;
  49182     }
  49183     s = js_mallocz(ctx, sizeof(*s));
  49184     if (!s) {
  49185         JS_FreeValue(ctx, async_iter);
  49186         JS_FreeValue(ctx, next_method);
  49187         return JS_EXCEPTION;
  49188     }
  49189     s->sync_iter = JS_DupValue(ctx, sync_iter);
  49190     s->next_method = next_method;
  49191     JS_SetOpaque(async_iter, s);
  49192     return async_iter;
  49193 }
  49194 
  49195 static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst this_val,
  49196                                                 int argc, JSValueConst *argv,
  49197                                                 int magic)
  49198 {
  49199     JSValue promise, resolving_funcs[2], value, err, method;
  49200     JSAsyncFromSyncIteratorData *s;
  49201     int done;
  49202     int is_reject;
  49203 
  49204     promise = JS_NewPromiseCapability(ctx, resolving_funcs);
  49205     if (JS_IsException(promise))
  49206         return JS_EXCEPTION;
  49207     s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
  49208     if (!s) {
  49209         JS_ThrowTypeError(ctx, "not an Async-from-Sync Iterator");
  49210         goto reject;
  49211     }
  49212 
  49213     if (magic == GEN_MAGIC_NEXT) {
  49214         method = JS_DupValue(ctx, s->next_method);
  49215     } else {
  49216         method = JS_GetProperty(ctx, s->sync_iter,
  49217                                 magic == GEN_MAGIC_RETURN ? JS_ATOM_return :
  49218                                 JS_ATOM_throw);
  49219         if (JS_IsException(method))
  49220             goto reject;
  49221         if (JS_IsUndefined(method) || JS_IsNull(method)) {
  49222             if (magic == GEN_MAGIC_RETURN) {
  49223                 err = js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), TRUE);
  49224                 is_reject = 0;
  49225             } else {
  49226                 err = JS_DupValue(ctx, argv[0]);
  49227                 is_reject = 1;
  49228             }
  49229             goto done_resolve;
  49230         }
  49231     }
  49232     value = JS_IteratorNext2(ctx, s->sync_iter, method,
  49233                              argc >= 1 ? 1 : 0, argv, &done);
  49234     JS_FreeValue(ctx, method);
  49235     if (JS_IsException(value))
  49236         goto reject;
  49237     if (done == 2) {
  49238         JSValue obj = value;
  49239         value = JS_IteratorGetCompleteValue(ctx, obj, &done);
  49240         JS_FreeValue(ctx, obj);
  49241         if (JS_IsException(value))
  49242             goto reject;
  49243     }
  49244 
  49245     if (JS_IsException(value)) {
  49246         JSValue res2;
  49247     reject:
  49248         err = JS_GetException(ctx);
  49249         is_reject = 1;
  49250     done_resolve:
  49251         res2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED,
  49252                        1, (JSValueConst *)&err);
  49253         JS_FreeValue(ctx, err);
  49254         JS_FreeValue(ctx, res2);
  49255         JS_FreeValue(ctx, resolving_funcs[0]);
  49256         JS_FreeValue(ctx, resolving_funcs[1]);
  49257         return promise;
  49258     }
  49259     {
  49260         JSValue value_wrapper_promise, resolve_reject[2];
  49261         int res;
  49262 
  49263         value_wrapper_promise = js_promise_resolve(ctx, ctx->promise_ctor,
  49264                                                    1, (JSValueConst *)&value, 0);
  49265         if (JS_IsException(value_wrapper_promise)) {
  49266             JS_FreeValue(ctx, value);
  49267             goto reject;
  49268         }
  49269 
  49270         resolve_reject[0] =
  49271             js_async_from_sync_iterator_unwrap_func_create(ctx, done);
  49272         if (JS_IsException(resolve_reject[0])) {
  49273             JS_FreeValue(ctx, value_wrapper_promise);
  49274             goto fail;
  49275         }
  49276         JS_FreeValue(ctx, value);
  49277         resolve_reject[1] = JS_UNDEFINED;
  49278 
  49279         res = perform_promise_then(ctx, value_wrapper_promise,
  49280                                    (JSValueConst *)resolve_reject,
  49281                                    (JSValueConst *)resolving_funcs);
  49282         JS_FreeValue(ctx, resolve_reject[0]);
  49283         JS_FreeValue(ctx, value_wrapper_promise);
  49284         JS_FreeValue(ctx, resolving_funcs[0]);
  49285         JS_FreeValue(ctx, resolving_funcs[1]);
  49286         if (res) {
  49287             JS_FreeValue(ctx, promise);
  49288             return JS_EXCEPTION;
  49289         }
  49290     }
  49291     return promise;
  49292  fail:
  49293     JS_FreeValue(ctx, value);
  49294     JS_FreeValue(ctx, resolving_funcs[0]);
  49295     JS_FreeValue(ctx, resolving_funcs[1]);
  49296     JS_FreeValue(ctx, promise);
  49297     return JS_EXCEPTION;
  49298 }
  49299 
  49300 static const JSCFunctionListEntry js_async_from_sync_iterator_proto_funcs[] = {
  49301     JS_CFUNC_MAGIC_DEF("next", 1, js_async_from_sync_iterator_next, GEN_MAGIC_NEXT ),
  49302     JS_CFUNC_MAGIC_DEF("return", 1, js_async_from_sync_iterator_next, GEN_MAGIC_RETURN ),
  49303     JS_CFUNC_MAGIC_DEF("throw", 1, js_async_from_sync_iterator_next, GEN_MAGIC_THROW ),
  49304 };
  49305 
  49306 /* AsyncGeneratorFunction */
  49307 
  49308 static const JSCFunctionListEntry js_async_generator_function_proto_funcs[] = {
  49309     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGeneratorFunction", JS_PROP_CONFIGURABLE ),
  49310 };
  49311 
  49312 /* AsyncGenerator prototype */
  49313 
  49314 static const JSCFunctionListEntry js_async_generator_proto_funcs[] = {
  49315     JS_CFUNC_MAGIC_DEF("next", 1, js_async_generator_next, GEN_MAGIC_NEXT ),
  49316     JS_CFUNC_MAGIC_DEF("return", 1, js_async_generator_next, GEN_MAGIC_RETURN ),
  49317     JS_CFUNC_MAGIC_DEF("throw", 1, js_async_generator_next, GEN_MAGIC_THROW ),
  49318     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGenerator", JS_PROP_CONFIGURABLE ),
  49319 };
  49320 
  49321 static JSClassShortDef const js_async_class_def[] = {
  49322     { JS_ATOM_Promise, js_promise_finalizer, js_promise_mark },                      /* JS_CLASS_PROMISE */
  49323     { JS_ATOM_PromiseResolveFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_RESOLVE_FUNCTION */
  49324     { JS_ATOM_PromiseRejectFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_REJECT_FUNCTION */
  49325     { JS_ATOM_AsyncFunction, js_bytecode_function_finalizer, js_bytecode_function_mark },  /* JS_CLASS_ASYNC_FUNCTION */
  49326     { JS_ATOM_AsyncFunctionResolve, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_RESOLVE */
  49327     { JS_ATOM_AsyncFunctionReject, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_REJECT */
  49328     { JS_ATOM_empty_string, js_async_from_sync_iterator_finalizer, js_async_from_sync_iterator_mark }, /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */
  49329     { JS_ATOM_AsyncGeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark },  /* JS_CLASS_ASYNC_GENERATOR_FUNCTION */
  49330     { JS_ATOM_AsyncGenerator, js_async_generator_finalizer, js_async_generator_mark },  /* JS_CLASS_ASYNC_GENERATOR */
  49331 };
  49332 
  49333 void JS_AddIntrinsicPromise(JSContext *ctx)
  49334 {
  49335     JSRuntime *rt = ctx->rt;
  49336     JSValue obj1;
  49337 
  49338     if (!JS_IsRegisteredClass(rt, JS_CLASS_PROMISE)) {
  49339         init_class_range(rt, js_async_class_def, JS_CLASS_PROMISE,
  49340                          countof(js_async_class_def));
  49341         rt->class_array[JS_CLASS_PROMISE_RESOLVE_FUNCTION].call = js_promise_resolve_function_call;
  49342         rt->class_array[JS_CLASS_PROMISE_REJECT_FUNCTION].call = js_promise_resolve_function_call;
  49343         rt->class_array[JS_CLASS_ASYNC_FUNCTION].call = js_async_function_call;
  49344         rt->class_array[JS_CLASS_ASYNC_FUNCTION_RESOLVE].call = js_async_function_resolve_call;
  49345         rt->class_array[JS_CLASS_ASYNC_FUNCTION_REJECT].call = js_async_function_resolve_call;
  49346         rt->class_array[JS_CLASS_ASYNC_GENERATOR_FUNCTION].call = js_async_generator_function_call;
  49347     }
  49348 
  49349     /* Promise */
  49350     ctx->class_proto[JS_CLASS_PROMISE] = JS_NewObject(ctx);
  49351     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_PROMISE],
  49352                                js_promise_proto_funcs,
  49353                                countof(js_promise_proto_funcs));
  49354     obj1 = JS_NewCFunction2(ctx, js_promise_constructor, "Promise", 1,
  49355                             JS_CFUNC_constructor, 0);
  49356     ctx->promise_ctor = JS_DupValue(ctx, obj1);
  49357     JS_SetPropertyFunctionList(ctx, obj1,
  49358                                js_promise_funcs,
  49359                                countof(js_promise_funcs));
  49360     JS_NewGlobalCConstructor2(ctx, obj1, "Promise",
  49361                               ctx->class_proto[JS_CLASS_PROMISE]);
  49362 
  49363     /* AsyncFunction */
  49364     ctx->class_proto[JS_CLASS_ASYNC_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto);
  49365     obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor,
  49366                             "AsyncFunction", 1,
  49367                             JS_CFUNC_constructor_or_func_magic, JS_FUNC_ASYNC,
  49368                             ctx->function_ctor);
  49369     JS_SetPropertyFunctionList(ctx,
  49370                                ctx->class_proto[JS_CLASS_ASYNC_FUNCTION],
  49371                                js_async_function_proto_funcs,
  49372                                countof(js_async_function_proto_funcs));
  49373     JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_FUNCTION],
  49374                        0, JS_PROP_CONFIGURABLE);
  49375     JS_FreeValue(ctx, obj1);
  49376 
  49377     /* AsyncIteratorPrototype */
  49378     ctx->async_iterator_proto = JS_NewObject(ctx);
  49379     JS_SetPropertyFunctionList(ctx, ctx->async_iterator_proto,
  49380                                js_async_iterator_proto_funcs,
  49381                                countof(js_async_iterator_proto_funcs));
  49382 
  49383     /* AsyncFromSyncIteratorPrototype */
  49384     ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR] =
  49385         JS_NewObjectProto(ctx, ctx->async_iterator_proto);
  49386     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR],
  49387                                js_async_from_sync_iterator_proto_funcs,
  49388                                countof(js_async_from_sync_iterator_proto_funcs));
  49389 
  49390     /* AsyncGeneratorPrototype */
  49391     ctx->class_proto[JS_CLASS_ASYNC_GENERATOR] =
  49392         JS_NewObjectProto(ctx, ctx->async_iterator_proto);
  49393     JS_SetPropertyFunctionList(ctx,
  49394                                ctx->class_proto[JS_CLASS_ASYNC_GENERATOR],
  49395                                js_async_generator_proto_funcs,
  49396                                countof(js_async_generator_proto_funcs));
  49397 
  49398     /* AsyncGeneratorFunction */
  49399     ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION] =
  49400         JS_NewObjectProto(ctx, ctx->function_proto);
  49401     obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor,
  49402                             "AsyncGeneratorFunction", 1,
  49403                             JS_CFUNC_constructor_or_func_magic,
  49404                             JS_FUNC_ASYNC_GENERATOR,
  49405                             ctx->function_ctor);
  49406     JS_SetPropertyFunctionList(ctx,
  49407                                ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],
  49408                                js_async_generator_function_proto_funcs,
  49409                                countof(js_async_generator_function_proto_funcs));
  49410     JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],
  49411                        ctx->class_proto[JS_CLASS_ASYNC_GENERATOR],
  49412                        JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE);
  49413     JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],
  49414                        0, JS_PROP_CONFIGURABLE);
  49415     JS_FreeValue(ctx, obj1);
  49416 }
  49417 
  49418 /* URI handling */
  49419 
  49420 static int string_get_hex(JSString *p, int k, int n) {
  49421     int c = 0, h;
  49422     while (n-- > 0) {
  49423         if ((h = from_hex(string_get(p, k++))) < 0)
  49424             return -1;
  49425         c = (c << 4) | h;
  49426     }
  49427     return c;
  49428 }
  49429 
  49430 static int isURIReserved(int c) {
  49431     return c < 0x100 && memchr(";/?:@&=+$,#", c, sizeof(";/?:@&=+$,#") - 1) != NULL;
  49432 }
  49433 
  49434 static int __attribute__((format(printf, 2, 3))) js_throw_URIError(JSContext *ctx, const char *fmt, ...)
  49435 {
  49436     va_list ap;
  49437 
  49438     va_start(ap, fmt);
  49439     JS_ThrowError(ctx, JS_URI_ERROR, fmt, ap);
  49440     va_end(ap);
  49441     return -1;
  49442 }
  49443 
  49444 static int hex_decode(JSContext *ctx, JSString *p, int k) {
  49445     int c;
  49446 
  49447     if (k >= p->len || string_get(p, k) != '%')
  49448         return js_throw_URIError(ctx, "expecting %%");
  49449     if (k + 2 >= p->len || (c = string_get_hex(p, k + 1, 2)) < 0)
  49450         return js_throw_URIError(ctx, "expecting hex digit");
  49451 
  49452     return c;
  49453 }
  49454 
  49455 static JSValue js_global_decodeURI(JSContext *ctx, JSValueConst this_val,
  49456                                    int argc, JSValueConst *argv, int isComponent)
  49457 {
  49458     JSValue str;
  49459     StringBuffer b_s, *b = &b_s;
  49460     JSString *p;
  49461     int k, c, c1, n, c_min;
  49462 
  49463     str = JS_ToString(ctx, argv[0]);
  49464     if (JS_IsException(str))
  49465         return str;
  49466 
  49467     string_buffer_init(ctx, b, 0);
  49468 
  49469     p = JS_VALUE_GET_STRING(str);
  49470     for (k = 0; k < p->len;) {
  49471         c = string_get(p, k);
  49472         if (c == '%') {
  49473             c = hex_decode(ctx, p, k);
  49474             if (c < 0)
  49475                 goto fail;
  49476             k += 3;
  49477             if (c < 0x80) {
  49478                 if (!isComponent && isURIReserved(c)) {
  49479                     c = '%';
  49480                     k -= 2;
  49481                 }
  49482             } else {
  49483                 /* Decode URI-encoded UTF-8 sequence */
  49484                 if (c >= 0xc0 && c <= 0xdf) {
  49485                     n = 1;
  49486                     c_min = 0x80;
  49487                     c &= 0x1f;
  49488                 } else if (c >= 0xe0 && c <= 0xef) {
  49489                     n = 2;
  49490                     c_min = 0x800;
  49491                     c &= 0xf;
  49492                 } else if (c >= 0xf0 && c <= 0xf7) {
  49493                     n = 3;
  49494                     c_min = 0x10000;
  49495                     c &= 0x7;
  49496                 } else {
  49497                     n = 0;
  49498                     c_min = 1;
  49499                     c = 0;
  49500                 }
  49501                 while (n-- > 0) {
  49502                     c1 = hex_decode(ctx, p, k);
  49503                     if (c1 < 0)
  49504                         goto fail;
  49505                     k += 3;
  49506                     if ((c1 & 0xc0) != 0x80) {
  49507                         c = 0;
  49508                         break;
  49509                     }
  49510                     c = (c << 6) | (c1 & 0x3f);
  49511                 }
  49512                 if (c < c_min || c > 0x10FFFF || is_surrogate(c)) {
  49513                     js_throw_URIError(ctx, "malformed UTF-8");
  49514                     goto fail;
  49515                 }
  49516             }
  49517         } else {
  49518             k++;
  49519         }
  49520         string_buffer_putc(b, c);
  49521     }
  49522     JS_FreeValue(ctx, str);
  49523     return string_buffer_end(b);
  49524 
  49525 fail:
  49526     JS_FreeValue(ctx, str);
  49527     string_buffer_free(b);
  49528     return JS_EXCEPTION;
  49529 }
  49530 
  49531 static int isUnescaped(int c) {
  49532     static char const unescaped_chars[] =
  49533         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  49534         "abcdefghijklmnopqrstuvwxyz"
  49535         "0123456789"
  49536         "@*_+-./";
  49537     return c < 0x100 &&
  49538         memchr(unescaped_chars, c, sizeof(unescaped_chars) - 1);
  49539 }
  49540 
  49541 static int isURIUnescaped(int c, int isComponent) {
  49542     return c < 0x100 &&
  49543         ((c >= 0x61 && c <= 0x7a) ||
  49544          (c >= 0x41 && c <= 0x5a) ||
  49545          (c >= 0x30 && c <= 0x39) ||
  49546          memchr("-_.!~*'()", c, sizeof("-_.!~*'()") - 1) != NULL ||
  49547          (!isComponent && isURIReserved(c)));
  49548 }
  49549 
  49550 static int encodeURI_hex(StringBuffer *b, int c) {
  49551     uint8_t buf[6];
  49552     int n = 0;
  49553     const char *hex = "0123456789ABCDEF";
  49554 
  49555     buf[n++] = '%';
  49556     if (c >= 256) {
  49557         buf[n++] = 'u';
  49558         buf[n++] = hex[(c >> 12) & 15];
  49559         buf[n++] = hex[(c >>  8) & 15];
  49560     }
  49561     buf[n++] = hex[(c >> 4) & 15];
  49562     buf[n++] = hex[(c >> 0) & 15];
  49563     return string_buffer_write8(b, buf, n);
  49564 }
  49565 
  49566 static JSValue js_global_encodeURI(JSContext *ctx, JSValueConst this_val,
  49567                                    int argc, JSValueConst *argv,
  49568                                    int isComponent)
  49569 {
  49570     JSValue str;
  49571     StringBuffer b_s, *b = &b_s;
  49572     JSString *p;
  49573     int k, c, c1;
  49574 
  49575     str = JS_ToString(ctx, argv[0]);
  49576     if (JS_IsException(str))
  49577         return str;
  49578 
  49579     p = JS_VALUE_GET_STRING(str);
  49580     string_buffer_init(ctx, b, p->len);
  49581     for (k = 0; k < p->len;) {
  49582         c = string_get(p, k);
  49583         k++;
  49584         if (isURIUnescaped(c, isComponent)) {
  49585             string_buffer_putc16(b, c);
  49586         } else {
  49587             if (is_lo_surrogate(c)) {
  49588                 js_throw_URIError(ctx, "invalid character");
  49589                 goto fail;
  49590             } else if (is_hi_surrogate(c)) {
  49591                 if (k >= p->len) {
  49592                     js_throw_URIError(ctx, "expecting surrogate pair");
  49593                     goto fail;
  49594                 }
  49595                 c1 = string_get(p, k);
  49596                 k++;
  49597                 if (!is_lo_surrogate(c1)) {
  49598                     js_throw_URIError(ctx, "expecting surrogate pair");
  49599                     goto fail;
  49600                 }
  49601                 c = from_surrogate(c, c1);
  49602             }
  49603             if (c < 0x80) {
  49604                 encodeURI_hex(b, c);
  49605             } else {
  49606                 /* XXX: use C UTF-8 conversion ? */
  49607                 if (c < 0x800) {
  49608                     encodeURI_hex(b, (c >> 6) | 0xc0);
  49609                 } else {
  49610                     if (c < 0x10000) {
  49611                         encodeURI_hex(b, (c >> 12) | 0xe0);
  49612                     } else {
  49613                         encodeURI_hex(b, (c >> 18) | 0xf0);
  49614                         encodeURI_hex(b, ((c >> 12) & 0x3f) | 0x80);
  49615                     }
  49616                     encodeURI_hex(b, ((c >> 6) & 0x3f) | 0x80);
  49617                 }
  49618                 encodeURI_hex(b, (c & 0x3f) | 0x80);
  49619             }
  49620         }
  49621     }
  49622     JS_FreeValue(ctx, str);
  49623     return string_buffer_end(b);
  49624 
  49625 fail:
  49626     JS_FreeValue(ctx, str);
  49627     string_buffer_free(b);
  49628     return JS_EXCEPTION;
  49629 }
  49630 
  49631 static JSValue js_global_escape(JSContext *ctx, JSValueConst this_val,
  49632                                 int argc, JSValueConst *argv)
  49633 {
  49634     JSValue str;
  49635     StringBuffer b_s, *b = &b_s;
  49636     JSString *p;
  49637     int i, len, c;
  49638 
  49639     str = JS_ToString(ctx, argv[0]);
  49640     if (JS_IsException(str))
  49641         return str;
  49642 
  49643     p = JS_VALUE_GET_STRING(str);
  49644     string_buffer_init(ctx, b, p->len);
  49645     for (i = 0, len = p->len; i < len; i++) {
  49646         c = string_get(p, i);
  49647         if (isUnescaped(c)) {
  49648             string_buffer_putc16(b, c);
  49649         } else {
  49650             encodeURI_hex(b, c);
  49651         }
  49652     }
  49653     JS_FreeValue(ctx, str);
  49654     return string_buffer_end(b);
  49655 }
  49656 
  49657 static JSValue js_global_unescape(JSContext *ctx, JSValueConst this_val,
  49658                                   int argc, JSValueConst *argv)
  49659 {
  49660     JSValue str;
  49661     StringBuffer b_s, *b = &b_s;
  49662     JSString *p;
  49663     int i, len, c, n;
  49664 
  49665     str = JS_ToString(ctx, argv[0]);
  49666     if (JS_IsException(str))
  49667         return str;
  49668 
  49669     string_buffer_init(ctx, b, 0);
  49670     p = JS_VALUE_GET_STRING(str);
  49671     for (i = 0, len = p->len; i < len; i++) {
  49672         c = string_get(p, i);
  49673         if (c == '%') {
  49674             if (i + 6 <= len
  49675             &&  string_get(p, i + 1) == 'u'
  49676             &&  (n = string_get_hex(p, i + 2, 4)) >= 0) {
  49677                 c = n;
  49678                 i += 6 - 1;
  49679             } else
  49680             if (i + 3 <= len
  49681             &&  (n = string_get_hex(p, i + 1, 2)) >= 0) {
  49682                 c = n;
  49683                 i += 3 - 1;
  49684             }
  49685         }
  49686         string_buffer_putc16(b, c);
  49687     }
  49688     JS_FreeValue(ctx, str);
  49689     return string_buffer_end(b);
  49690 }
  49691 
  49692 /* global object */
  49693 
  49694 static const JSCFunctionListEntry js_global_funcs[] = {
  49695     JS_CFUNC_DEF("parseInt", 2, js_parseInt ),
  49696     JS_CFUNC_DEF("parseFloat", 1, js_parseFloat ),
  49697     JS_CFUNC_DEF("isNaN", 1, js_global_isNaN ),
  49698     JS_CFUNC_DEF("isFinite", 1, js_global_isFinite ),
  49699 
  49700     JS_CFUNC_MAGIC_DEF("decodeURI", 1, js_global_decodeURI, 0 ),
  49701     JS_CFUNC_MAGIC_DEF("decodeURIComponent", 1, js_global_decodeURI, 1 ),
  49702     JS_CFUNC_MAGIC_DEF("encodeURI", 1, js_global_encodeURI, 0 ),
  49703     JS_CFUNC_MAGIC_DEF("encodeURIComponent", 1, js_global_encodeURI, 1 ),
  49704     JS_CFUNC_DEF("escape", 1, js_global_escape ),
  49705     JS_CFUNC_DEF("unescape", 1, js_global_unescape ),
  49706     JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ),
  49707     JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),
  49708     JS_PROP_UNDEFINED_DEF("undefined", 0 ),
  49709     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "global", JS_PROP_CONFIGURABLE ),
  49710 };
  49711 
  49712 /* Date */
  49713 
  49714 static int64_t math_mod(int64_t a, int64_t b) {
  49715     /* return positive modulo */
  49716     int64_t m = a % b;
  49717     return m + (m < 0) * b;
  49718 }
  49719 
  49720 static int64_t floor_div(int64_t a, int64_t b) {
  49721     /* integer division rounding toward -Infinity */
  49722     int64_t m = a % b;
  49723     return (a - (m + (m < 0) * b)) / b;
  49724 }
  49725 
  49726 static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
  49727                              int argc, JSValueConst *argv);
  49728 
  49729 static __exception int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst this_val)
  49730 {
  49731     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
  49732         JSObject *p = JS_VALUE_GET_OBJ(this_val);
  49733         if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data))
  49734             return JS_ToFloat64(ctx, valp, p->u.object_data);
  49735     }
  49736     JS_ThrowTypeError(ctx, "not a Date object");
  49737     return -1;
  49738 }
  49739 
  49740 static JSValue JS_SetThisTimeValue(JSContext *ctx, JSValueConst this_val, double v)
  49741 {
  49742     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
  49743         JSObject *p = JS_VALUE_GET_OBJ(this_val);
  49744         if (p->class_id == JS_CLASS_DATE) {
  49745             JS_FreeValue(ctx, p->u.object_data);
  49746             p->u.object_data = JS_NewFloat64(ctx, v);
  49747             return JS_DupValue(ctx, p->u.object_data);
  49748         }
  49749     }
  49750     return JS_ThrowTypeError(ctx, "not a Date object");
  49751 }
  49752 
  49753 static int64_t days_from_year(int64_t y) {
  49754     return 365 * (y - 1970) + floor_div(y - 1969, 4) -
  49755         floor_div(y - 1901, 100) + floor_div(y - 1601, 400);
  49756 }
  49757 
  49758 static int64_t days_in_year(int64_t y) {
  49759     return 365 + !(y % 4) - !(y % 100) + !(y % 400);
  49760 }
  49761 
  49762 /* return the year, update days */
  49763 static int64_t year_from_days(int64_t *days) {
  49764     int64_t y, d1, nd, d = *days;
  49765     y = floor_div(d * 10000, 3652425) + 1970;
  49766     /* the initial approximation is very good, so only a few
  49767        iterations are necessary */
  49768     for(;;) {
  49769         d1 = d - days_from_year(y);
  49770         if (d1 < 0) {
  49771             y--;
  49772             d1 += days_in_year(y);
  49773         } else {
  49774             nd = days_in_year(y);
  49775             if (d1 < nd)
  49776                 break;
  49777             d1 -= nd;
  49778             y++;
  49779         }
  49780     }
  49781     *days = d1;
  49782     return y;
  49783 }
  49784 
  49785 static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  49786 static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
  49787 static char const day_names[] = "SunMonTueWedThuFriSat";
  49788 
  49789 static __exception int get_date_fields(JSContext *ctx, JSValueConst obj,
  49790                                        double fields[minimum_length(9)], int is_local, int force)
  49791 {
  49792     double dval;
  49793     int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0;
  49794 
  49795     if (JS_ThisTimeValue(ctx, &dval, obj))
  49796         return -1;
  49797 
  49798     if (isnan(dval)) {
  49799         if (!force)
  49800             return FALSE; /* NaN */
  49801         d = 0;        /* initialize all fields to 0 */
  49802     } else {
  49803         d = dval;     /* assuming -8.64e15 <= dval <= -8.64e15 */
  49804         if (is_local) {
  49805             tz = -getTimezoneOffset(d);
  49806             d += tz * 60000;
  49807         }
  49808     }
  49809 
  49810     /* result is >= 0, we can use % */
  49811     h = math_mod(d, 86400000);
  49812     days = (d - h) / 86400000;
  49813     ms = h % 1000;
  49814     h = (h - ms) / 1000;
  49815     s = h % 60;
  49816     h = (h - s) / 60;
  49817     m = h % 60;
  49818     h = (h - m) / 60;
  49819     wd = math_mod(days + 4, 7); /* week day */
  49820     y = year_from_days(&days);
  49821 
  49822     for(i = 0; i < 11; i++) {
  49823         md = month_days[i];
  49824         if (i == 1)
  49825             md += days_in_year(y) - 365;
  49826         if (days < md)
  49827             break;
  49828         days -= md;
  49829     }
  49830     fields[0] = y;
  49831     fields[1] = i;
  49832     fields[2] = days + 1;
  49833     fields[3] = h;
  49834     fields[4] = m;
  49835     fields[5] = s;
  49836     fields[6] = ms;
  49837     fields[7] = wd;
  49838     fields[8] = tz;
  49839     return TRUE;
  49840 }
  49841 
  49842 static double time_clip(double t) {
  49843     if (t >= -8.64e15 && t <= 8.64e15)
  49844         return trunc(t) + 0.0;  /* convert -0 to +0 */
  49845     else
  49846         return NAN;
  49847 }
  49848 
  49849 /* The spec mandates the use of 'double' and it specifies the order
  49850    of the operations */
  49851 static double set_date_fields(double fields[minimum_length(7)], int is_local) {
  49852     double y, m, dt, ym, mn, day, h, s, milli, time, tv;
  49853     int yi, mi, i;
  49854     int64_t days;
  49855     volatile double temp;  /* enforce evaluation order */
  49856 
  49857     /* emulate 21.4.1.15 MakeDay ( year, month, date ) */
  49858     y = fields[0];
  49859     m = fields[1];
  49860     dt = fields[2];
  49861     ym = y + floor(m / 12);
  49862     mn = fmod(m, 12);
  49863     if (mn < 0)
  49864         mn += 12;
  49865     if (ym < -271821 || ym > 275760)
  49866         return NAN;
  49867 
  49868     yi = ym;
  49869     mi = mn;
  49870     days = days_from_year(yi);
  49871     for(i = 0; i < mi; i++) {
  49872         days += month_days[i];
  49873         if (i == 1)
  49874             days += days_in_year(yi) - 365;
  49875     }
  49876     day = days + dt - 1;
  49877 
  49878     /* emulate 21.4.1.14 MakeTime ( hour, min, sec, ms ) */
  49879     h = fields[3];
  49880     m = fields[4];
  49881     s = fields[5];
  49882     milli = fields[6];
  49883     /* Use a volatile intermediary variable to ensure order of evaluation
  49884      * as specified in ECMA. This fixes a test262 error on
  49885      * test262/test/built-ins/Date/UTC/fp-evaluation-order.js.
  49886      * Without the volatile qualifier, the compile can generate code
  49887      * that performs the computation in a different order or with instructions
  49888      * that produce a different result such as FMA (float multiply and add).
  49889      */
  49890     time = h * 3600000;
  49891     time += (temp = m * 60000);
  49892     time += (temp = s * 1000);
  49893     time += milli;
  49894 
  49895     /* emulate 21.4.1.16 MakeDate ( day, time ) */
  49896     tv = (temp = day * 86400000) + time;   /* prevent generation of FMA */
  49897     if (!isfinite(tv))
  49898         return NAN;
  49899 
  49900     /* adjust for local time and clip */
  49901     if (is_local) {
  49902         int64_t ti = tv < INT64_MIN ? INT64_MIN : tv >= 0x1p63 ? INT64_MAX : (int64_t)tv;
  49903         tv += getTimezoneOffset(ti) * 60000;
  49904     }
  49905     return time_clip(tv);
  49906 }
  49907 
  49908 static JSValue get_date_field(JSContext *ctx, JSValueConst this_val,
  49909                               int argc, JSValueConst *argv, int magic)
  49910 {
  49911     // get_date_field(obj, n, is_local)
  49912     double fields[9];
  49913     int res, n, is_local;
  49914 
  49915     is_local = magic & 0x0F;
  49916     n = (magic >> 4) & 0x0F;
  49917     res = get_date_fields(ctx, this_val, fields, is_local, 0);
  49918     if (res < 0)
  49919         return JS_EXCEPTION;
  49920     if (!res)
  49921         return JS_NAN;
  49922 
  49923     if (magic & 0x100) {    // getYear
  49924         fields[0] -= 1900;
  49925     }
  49926     return JS_NewFloat64(ctx, fields[n]);
  49927 }
  49928 
  49929 static JSValue set_date_field(JSContext *ctx, JSValueConst this_val,
  49930                               int argc, JSValueConst *argv, int magic)
  49931 {
  49932     // _field(obj, first_field, end_field, args, is_local)
  49933     double fields[9];
  49934     int res, first_field, end_field, is_local, i, n;
  49935     double d, a;
  49936 
  49937     d = NAN;
  49938     first_field = (magic >> 8) & 0x0F;
  49939     end_field = (magic >> 4) & 0x0F;
  49940     is_local = magic & 0x0F;
  49941 
  49942     res = get_date_fields(ctx, this_val, fields, is_local, first_field == 0);
  49943     if (res < 0)
  49944         return JS_EXCEPTION;
  49945 
  49946     // Argument coercion is observable and must be done unconditionally.
  49947     n = min_int(argc, end_field - first_field);
  49948     for(i = 0; i < n; i++) {
  49949         if (JS_ToFloat64(ctx, &a, argv[i]))
  49950             return JS_EXCEPTION;
  49951         if (!isfinite(a))
  49952             res = FALSE;
  49953         fields[first_field + i] = trunc(a);
  49954     }
  49955     if (res && argc > 0)
  49956         d = set_date_fields(fields, is_local);
  49957 
  49958     return JS_SetThisTimeValue(ctx, this_val, d);
  49959 }
  49960 
  49961 /* fmt:
  49962    0: toUTCString: "Tue, 02 Jan 2018 23:04:46 GMT"
  49963    1: toString: "Wed Jan 03 2018 00:05:22 GMT+0100 (CET)"
  49964    2: toISOString: "2018-01-02T23:02:56.927Z"
  49965    3: toLocaleString: "1/2/2018, 11:40:40 PM"
  49966    part: 1=date, 2=time 3=all
  49967    XXX: should use a variant of strftime().
  49968  */
  49969 static JSValue get_date_string(JSContext *ctx, JSValueConst this_val,
  49970                                int argc, JSValueConst *argv, int magic)
  49971 {
  49972     // _string(obj, fmt, part)
  49973     char buf[64];
  49974     double fields[9];
  49975     int res, fmt, part, pos;
  49976     int y, mon, d, h, m, s, ms, wd, tz;
  49977 
  49978     fmt = (magic >> 4) & 0x0F;
  49979     part = magic & 0x0F;
  49980 
  49981     res = get_date_fields(ctx, this_val, fields, fmt & 1, 0);
  49982     if (res < 0)
  49983         return JS_EXCEPTION;
  49984     if (!res) {
  49985         if (fmt == 2)
  49986             return JS_ThrowRangeError(ctx, "Date value is NaN");
  49987         else
  49988             return JS_NewString(ctx, "Invalid Date");
  49989     }
  49990 
  49991     y = fields[0];
  49992     mon = fields[1];
  49993     d = fields[2];
  49994     h = fields[3];
  49995     m = fields[4];
  49996     s = fields[5];
  49997     ms = fields[6];
  49998     wd = fields[7];
  49999     tz = fields[8];
  50000 
  50001     pos = 0;
  50002 
  50003     if (part & 1) { /* date part */
  50004         switch(fmt) {
  50005         case 0:
  50006             pos += snprintf(buf + pos, sizeof(buf) - pos,
  50007                             "%.3s, %02d %.3s %0*d ",
  50008                             day_names + wd * 3, d,
  50009                             month_names + mon * 3, 4 + (y < 0), y);
  50010             break;
  50011         case 1:
  50012             pos += snprintf(buf + pos, sizeof(buf) - pos,
  50013                             "%.3s %.3s %02d %0*d",
  50014                             day_names + wd * 3,
  50015                             month_names + mon * 3, d, 4 + (y < 0), y);
  50016             if (part == 3) {
  50017                 buf[pos++] = ' ';
  50018             }
  50019             break;
  50020         case 2:
  50021             if (y >= 0 && y <= 9999) {
  50022                 pos += snprintf(buf + pos, sizeof(buf) - pos,
  50023                                 "%04d", y);
  50024             } else {
  50025                 pos += snprintf(buf + pos, sizeof(buf) - pos,
  50026                                 "%+07d", y);
  50027             }
  50028             pos += snprintf(buf + pos, sizeof(buf) - pos,
  50029                             "-%02d-%02dT", mon + 1, d);
  50030             break;
  50031         case 3:
  50032             pos += snprintf(buf + pos, sizeof(buf) - pos,
  50033                             "%02d/%02d/%0*d", mon + 1, d, 4 + (y < 0), y);
  50034             if (part == 3) {
  50035                 buf[pos++] = ',';
  50036                 buf[pos++] = ' ';
  50037             }
  50038             break;
  50039         }
  50040     }
  50041     if (part & 2) { /* time part */
  50042         switch(fmt) {
  50043         case 0:
  50044             pos += snprintf(buf + pos, sizeof(buf) - pos,
  50045                             "%02d:%02d:%02d GMT", h, m, s);
  50046             break;
  50047         case 1:
  50048             pos += snprintf(buf + pos, sizeof(buf) - pos,
  50049                             "%02d:%02d:%02d GMT", h, m, s);
  50050             if (tz < 0) {
  50051                 buf[pos++] = '-';
  50052                 tz = -tz;
  50053             } else {
  50054                 buf[pos++] = '+';
  50055             }
  50056             /* tz is >= 0, can use % */
  50057             pos += snprintf(buf + pos, sizeof(buf) - pos,
  50058                             "%02d%02d", tz / 60, tz % 60);
  50059             /* XXX: tack the time zone code? */
  50060             break;
  50061         case 2:
  50062             pos += snprintf(buf + pos, sizeof(buf) - pos,
  50063                             "%02d:%02d:%02d.%03dZ", h, m, s, ms);
  50064             break;
  50065         case 3:
  50066             pos += snprintf(buf + pos, sizeof(buf) - pos,
  50067                             "%02d:%02d:%02d %cM", (h + 11) % 12 + 1, m, s,
  50068                             (h < 12) ? 'A' : 'P');
  50069             break;
  50070         }
  50071     }
  50072     return JS_NewStringLen(ctx, buf, pos);
  50073 }
  50074 
  50075 /* OS dependent: return the UTC time in ms since 1970. */
  50076 static int64_t date_now(void) {
  50077     struct timeval tv;
  50078     gettimeofday(&tv, NULL);
  50079     return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
  50080 }
  50081 
  50082 static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target,
  50083                                    int argc, JSValueConst *argv)
  50084 {
  50085     // Date(y, mon, d, h, m, s, ms)
  50086     JSValue rv;
  50087     int i, n;
  50088     double a, val;
  50089 
  50090     if (JS_IsUndefined(new_target)) {
  50091         /* invoked as function */
  50092         argc = 0;
  50093     }
  50094     n = argc;
  50095     if (n == 0) {
  50096         val = date_now();
  50097     } else if (n == 1) {
  50098         JSValue v, dv;
  50099         if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) {
  50100             JSObject *p = JS_VALUE_GET_OBJ(argv[0]);
  50101             if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) {
  50102                 if (JS_ToFloat64(ctx, &val, p->u.object_data))
  50103                     return JS_EXCEPTION;
  50104                 val = time_clip(val);
  50105                 goto has_val;
  50106             }
  50107         }
  50108         v = JS_ToPrimitive(ctx, argv[0], HINT_NONE);
  50109         if (JS_IsString(v)) {
  50110             dv = js_Date_parse(ctx, JS_UNDEFINED, 1, (JSValueConst *)&v);
  50111             JS_FreeValue(ctx, v);
  50112             if (JS_IsException(dv))
  50113                 return JS_EXCEPTION;
  50114             if (JS_ToFloat64Free(ctx, &val, dv))
  50115                 return JS_EXCEPTION;
  50116         } else {
  50117             if (JS_ToFloat64Free(ctx, &val, v))
  50118                 return JS_EXCEPTION;
  50119         }
  50120         val = time_clip(val);
  50121     } else {
  50122         double fields[] = { 0, 0, 1, 0, 0, 0, 0 };
  50123         if (n > 7)
  50124             n = 7;
  50125         for(i = 0; i < n; i++) {
  50126             if (JS_ToFloat64(ctx, &a, argv[i]))
  50127                 return JS_EXCEPTION;
  50128             if (!isfinite(a))
  50129                 break;
  50130             fields[i] = trunc(a);
  50131             if (i == 0 && fields[0] >= 0 && fields[0] < 100)
  50132                 fields[0] += 1900;
  50133         }
  50134         val = (i == n) ? set_date_fields(fields, 1) : NAN;
  50135     }
  50136 has_val:
  50137 #if 0
  50138     JSValueConst args[3];
  50139     args[0] = new_target;
  50140     args[1] = ctx->class_proto[JS_CLASS_DATE];
  50141     args[2] = JS_NewFloat64(ctx, val);
  50142     rv = js___date_create(ctx, JS_UNDEFINED, 3, args);
  50143 #else
  50144     rv = js_create_from_ctor(ctx, new_target, JS_CLASS_DATE);
  50145     if (!JS_IsException(rv))
  50146         JS_SetObjectData(ctx, rv, JS_NewFloat64(ctx, val));
  50147 #endif
  50148     if (!JS_IsException(rv) && JS_IsUndefined(new_target)) {
  50149         /* invoked as a function, return (new Date()).toString(); */
  50150         JSValue s;
  50151         s = get_date_string(ctx, rv, 0, NULL, 0x13);
  50152         JS_FreeValue(ctx, rv);
  50153         rv = s;
  50154     }
  50155     return rv;
  50156 }
  50157 
  50158 static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val,
  50159                            int argc, JSValueConst *argv)
  50160 {
  50161     // UTC(y, mon, d, h, m, s, ms)
  50162     double fields[] = { 0, 0, 1, 0, 0, 0, 0 };
  50163     int i, n;
  50164     double a;
  50165 
  50166     n = argc;
  50167     if (n == 0)
  50168         return JS_NAN;
  50169     if (n > 7)
  50170         n = 7;
  50171     for(i = 0; i < n; i++) {
  50172         if (JS_ToFloat64(ctx, &a, argv[i]))
  50173             return JS_EXCEPTION;
  50174         if (!isfinite(a))
  50175             return JS_NAN;
  50176         fields[i] = trunc(a);
  50177         if (i == 0 && fields[0] >= 0 && fields[0] < 100)
  50178             fields[0] += 1900;
  50179     }
  50180     return JS_NewFloat64(ctx, set_date_fields(fields, 0));
  50181 }
  50182 
  50183 /* Date string parsing */
  50184 
  50185 static BOOL string_skip_char(const uint8_t *sp, int *pp, int c) {
  50186     if (sp[*pp] == c) {
  50187         *pp += 1;
  50188         return TRUE;
  50189     } else {
  50190         return FALSE;
  50191     }
  50192 }
  50193 
  50194 /* skip spaces, update offset, return next char */
  50195 static int string_skip_spaces(const uint8_t *sp, int *pp) {
  50196     int c;
  50197     while ((c = sp[*pp]) == ' ')
  50198         *pp += 1;
  50199     return c;
  50200 }
  50201 
  50202 /* skip dashes dots and commas */
  50203 static int string_skip_separators(const uint8_t *sp, int *pp) {
  50204     int c;
  50205     while ((c = sp[*pp]) == '-' || c == '/' || c == '.' || c == ',')
  50206         *pp += 1;
  50207     return c;
  50208 }
  50209 
  50210 /* skip a word, stop on spaces, digits and separators, update offset */
  50211 static int string_skip_until(const uint8_t *sp, int *pp, const char *stoplist) {
  50212     int c;
  50213     while (!strchr(stoplist, c = sp[*pp]))
  50214         *pp += 1;
  50215     return c;
  50216 }
  50217 
  50218 /* parse a numeric field (max_digits = 0 -> no maximum) */
  50219 static BOOL string_get_digits(const uint8_t *sp, int *pp, int *pval,
  50220                               int min_digits, int max_digits)
  50221 {
  50222     int v = 0;
  50223     int c, p = *pp, p_start;
  50224 
  50225     p_start = p;
  50226     while ((c = sp[p]) >= '0' && c <= '9') {
  50227         v = v * 10 + c - '0';
  50228         p++;
  50229         if (p - p_start == max_digits)
  50230             break;
  50231     }
  50232     if (p - p_start < min_digits)
  50233         return FALSE;
  50234     *pval = v;
  50235     *pp = p;
  50236     return TRUE;
  50237 }
  50238 
  50239 static BOOL string_get_milliseconds(const uint8_t *sp, int *pp, int *pval) {
  50240     /* parse optional fractional part as milliseconds and truncate. */
  50241     /* spec does not indicate which rounding should be used */
  50242     int mul = 100, ms = 0, c, p_start, p = *pp;
  50243 
  50244     c = sp[p];
  50245     if (c == '.' || c == ',') {
  50246         p++;
  50247         p_start = p;
  50248         while ((c = sp[p]) >= '0' && c <= '9') {
  50249             ms += (c - '0') * mul;
  50250             mul /= 10;
  50251             p++;
  50252             if (p - p_start == 9)
  50253                 break;
  50254         }
  50255         if (p > p_start) {
  50256             /* only consume the separator if digits are present */
  50257             *pval = ms;
  50258             *pp = p;
  50259         }
  50260     }
  50261     return TRUE;
  50262 }
  50263 
  50264 static uint8_t upper_ascii(uint8_t c) {
  50265     return c >= 'a' && c <= 'z' ? c - 'a' + 'A' : c;
  50266 }
  50267 
  50268 static BOOL string_get_tzoffset(const uint8_t *sp, int *pp, int *tzp, BOOL strict) {
  50269     int tz = 0, sgn, hh, mm, p = *pp;
  50270 
  50271     sgn = sp[p++];
  50272     if (sgn == '+' || sgn == '-') {
  50273         int n = p;
  50274         if (!string_get_digits(sp, &p, &hh, 1, 9))
  50275             return FALSE;
  50276         n = p - n;
  50277         if (strict && n != 2 && n != 4)
  50278             return FALSE;
  50279         while (n > 4) {
  50280             n -= 2;
  50281             hh /= 100;
  50282         }
  50283         if (n > 2) {
  50284             mm = hh % 100;
  50285             hh = hh / 100;
  50286         } else {
  50287             mm = 0;
  50288             if (string_skip_char(sp, &p, ':')  /* optional separator */
  50289             &&  !string_get_digits(sp, &p, &mm, 2, 2))
  50290                 return FALSE;
  50291         }
  50292         if (hh > 23 || mm > 59)
  50293             return FALSE;
  50294         tz = hh * 60 + mm;
  50295         if (sgn != '+')
  50296             tz = -tz;
  50297     } else
  50298     if (sgn != 'Z') {
  50299         return FALSE;
  50300     }
  50301     *pp = p;
  50302     *tzp = tz;
  50303     return TRUE;
  50304 }
  50305 
  50306 static BOOL string_match(const uint8_t *sp, int *pp, const char *s) {
  50307     int p = *pp;
  50308     while (*s != '\0') {
  50309         if (upper_ascii(sp[p]) != upper_ascii(*s++))
  50310             return FALSE;
  50311         p++;
  50312     }
  50313     *pp = p;
  50314     return TRUE;
  50315 }
  50316 
  50317 static int find_abbrev(const uint8_t *sp, int p, const char *list, int count) {
  50318     int n, i;
  50319 
  50320     for (n = 0; n < count; n++) {
  50321         for (i = 0;; i++) {
  50322             if (upper_ascii(sp[p + i]) != upper_ascii(list[n * 3 + i]))
  50323                 break;
  50324             if (i == 2)
  50325                 return n;
  50326         }
  50327     }
  50328     return -1;
  50329 }
  50330 
  50331 static BOOL string_get_month(const uint8_t *sp, int *pp, int *pval) {
  50332     int n;
  50333 
  50334     n = find_abbrev(sp, *pp, month_names, 12);
  50335     if (n < 0)
  50336         return FALSE;
  50337 
  50338     *pval = n + 1;
  50339     *pp += 3;
  50340     return TRUE;
  50341 }
  50342 
  50343 /* parse toISOString format */
  50344 static BOOL js_date_parse_isostring(const uint8_t *sp, int fields[9], BOOL *is_local) {
  50345     int sgn, i, p = 0;
  50346 
  50347     /* initialize fields to the beginning of the Epoch */
  50348     for (i = 0; i < 9; i++) {
  50349         fields[i] = (i == 2);
  50350     }
  50351     *is_local = FALSE;
  50352 
  50353     /* year is either yyyy digits or [+-]yyyyyy */
  50354     sgn = sp[p];
  50355     if (sgn == '-' || sgn == '+') {
  50356         p++;
  50357         if (!string_get_digits(sp, &p, &fields[0], 6, 6))
  50358             return FALSE;
  50359         if (sgn == '-') {
  50360             if (fields[0] == 0)
  50361                 return FALSE; // reject -000000
  50362             fields[0] = -fields[0];
  50363         }
  50364     } else {
  50365         if (!string_get_digits(sp, &p, &fields[0], 4, 4))
  50366             return FALSE;
  50367     }
  50368     if (string_skip_char(sp, &p, '-')) {
  50369         if (!string_get_digits(sp, &p, &fields[1], 2, 2))  /* month */
  50370             return FALSE;
  50371         if (fields[1] < 1)
  50372             return FALSE;
  50373         fields[1] -= 1;
  50374         if (string_skip_char(sp, &p, '-')) {
  50375             if (!string_get_digits(sp, &p, &fields[2], 2, 2))  /* day */
  50376                 return FALSE;
  50377             if (fields[2] < 1)
  50378                 return FALSE;
  50379         }
  50380     }
  50381     if (string_skip_char(sp, &p, 'T')) {
  50382         *is_local = TRUE;
  50383         if (!string_get_digits(sp, &p, &fields[3], 2, 2)  /* hour */
  50384         ||  !string_skip_char(sp, &p, ':')
  50385         ||  !string_get_digits(sp, &p, &fields[4], 2, 2)) {  /* minute */
  50386             fields[3] = 100;  // reject unconditionally
  50387             return TRUE;
  50388         }
  50389         if (string_skip_char(sp, &p, ':')) {
  50390             if (!string_get_digits(sp, &p, &fields[5], 2, 2))  /* second */
  50391                 return FALSE;
  50392             string_get_milliseconds(sp, &p, &fields[6]);
  50393         }
  50394     }
  50395     /* parse the time zone offset if present: [+-]HH:mm or [+-]HHmm */
  50396     if (sp[p]) {
  50397         *is_local = FALSE;
  50398         if (!string_get_tzoffset(sp, &p, &fields[8], TRUE))
  50399             return FALSE;
  50400     }
  50401     /* error if extraneous characters */
  50402     return sp[p] == '\0';
  50403 }
  50404 
  50405 static struct {
  50406     char name[6];
  50407     int16_t offset;
  50408 } const js_tzabbr[] = {
  50409     { "GMT",   0 },         // Greenwich Mean Time
  50410     { "UTC",   0 },         // Coordinated Universal Time
  50411     { "UT",    0 },         // Universal Time
  50412     { "Z",     0 },         // Zulu Time
  50413     { "EDT",  -4 * 60 },    // Eastern Daylight Time
  50414     { "EST",  -5 * 60 },    // Eastern Standard Time
  50415     { "CDT",  -5 * 60 },    // Central Daylight Time
  50416     { "CST",  -6 * 60 },    // Central Standard Time
  50417     { "MDT",  -6 * 60 },    // Mountain Daylight Time
  50418     { "MST",  -7 * 60 },    // Mountain Standard Time
  50419     { "PDT",  -7 * 60 },    // Pacific Daylight Time
  50420     { "PST",  -8 * 60 },    // Pacific Standard Time
  50421     { "WET",  +0 * 60 },    // Western European Time
  50422     { "WEST", +1 * 60 },    // Western European Summer Time
  50423     { "CET",  +1 * 60 },    // Central European Time
  50424     { "CEST", +2 * 60 },    // Central European Summer Time
  50425     { "EET",  +2 * 60 },    // Eastern European Time
  50426     { "EEST", +3 * 60 },    // Eastern European Summer Time
  50427 };
  50428 
  50429 static BOOL string_get_tzabbr(const uint8_t *sp, int *pp, int *offset) {
  50430     for (size_t i = 0; i < countof(js_tzabbr); i++) {
  50431         if (string_match(sp, pp, js_tzabbr[i].name)) {
  50432             *offset = js_tzabbr[i].offset;
  50433             return TRUE;
  50434         }
  50435     }
  50436     return FALSE;
  50437 }
  50438 
  50439 /* parse toString, toUTCString and other formats */
  50440 static BOOL js_date_parse_otherstring(const uint8_t *sp,
  50441                                       int fields[minimum_length(9)],
  50442                                       BOOL *is_local) {
  50443     int c, i, val, p = 0, p_start;
  50444     int num[3];
  50445     BOOL has_year = FALSE;
  50446     BOOL has_mon = FALSE;
  50447     BOOL has_time = FALSE;
  50448     int num_index = 0;
  50449 
  50450     /* initialize fields to the beginning of 2001-01-01 */
  50451     fields[0] = 2001;
  50452     fields[1] = 1;
  50453     fields[2] = 1;
  50454     for (i = 3; i < 9; i++) {
  50455         fields[i] = 0;
  50456     }
  50457     *is_local = TRUE;
  50458 
  50459     while (string_skip_spaces(sp, &p)) {
  50460         p_start = p;
  50461         if ((c = sp[p]) == '+' || c == '-') {
  50462             if (has_time && string_get_tzoffset(sp, &p, &fields[8], FALSE)) {
  50463                 *is_local = FALSE;
  50464             } else {
  50465                 p++;
  50466                 if (string_get_digits(sp, &p, &val, 1, 9)) {
  50467                     if (c == '-') {
  50468                         if (val == 0)
  50469                             return FALSE;
  50470                         val = -val;
  50471                     }
  50472                     fields[0] = val;
  50473                     has_year = TRUE;
  50474                 }
  50475             }
  50476         } else
  50477         if (string_get_digits(sp, &p, &val, 1, 9)) {
  50478             if (string_skip_char(sp, &p, ':')) {
  50479                 /* time part */
  50480                 fields[3] = val;
  50481                 if (!string_get_digits(sp, &p, &fields[4], 1, 2))
  50482                     return FALSE;
  50483                 if (string_skip_char(sp, &p, ':')) {
  50484                     if (!string_get_digits(sp, &p, &fields[5], 1, 2))
  50485                         return FALSE;
  50486                     string_get_milliseconds(sp, &p, &fields[6]);
  50487                 }
  50488                 has_time = TRUE;
  50489             } else {
  50490                 if (p - p_start > 2) {
  50491                     fields[0] = val;
  50492                     has_year = TRUE;
  50493                 } else
  50494                 if (val < 1 || val > 31) {
  50495                     fields[0] = val + (val < 100) * 1900 + (val < 50) * 100;
  50496                     has_year = TRUE;
  50497                 } else {
  50498                     if (num_index == 3)
  50499                         return FALSE;
  50500                     num[num_index++] = val;
  50501                 }
  50502             }
  50503         } else
  50504         if (string_get_month(sp, &p, &fields[1])) {
  50505             has_mon = TRUE;
  50506             string_skip_until(sp, &p, "0123456789 -/(");
  50507         } else
  50508         if (has_time && string_match(sp, &p, "PM")) {
  50509             if (fields[3] < 12)
  50510                 fields[3] += 12;
  50511             continue;
  50512         } else
  50513         if (has_time && string_match(sp, &p, "AM")) {
  50514             if (fields[3] == 12)
  50515                 fields[3] -= 12;
  50516             continue;
  50517         } else
  50518         if (string_get_tzabbr(sp, &p, &fields[8])) {
  50519             *is_local = FALSE;
  50520             continue;
  50521         } else
  50522         if (c == '(') {  /* skip parenthesized phrase */
  50523             int level = 0;
  50524             while ((c = sp[p]) != '\0') {
  50525                 p++;
  50526                 level += (c == '(');
  50527                 level -= (c == ')');
  50528                 if (!level)
  50529                     break;
  50530             }
  50531             if (level > 0)
  50532                 return FALSE;
  50533         } else
  50534         if (c == ')') {
  50535             return FALSE;
  50536         } else {
  50537             if (has_year + has_mon + has_time + num_index)
  50538                 return FALSE;
  50539             /* skip a word */
  50540             string_skip_until(sp, &p, " -/(");
  50541         }
  50542         string_skip_separators(sp, &p);
  50543     }
  50544     if (num_index + has_year + has_mon > 3)
  50545         return FALSE;
  50546 
  50547     switch (num_index) {
  50548     case 0:
  50549         if (!has_year)
  50550             return FALSE;
  50551         break;
  50552     case 1:
  50553         if (has_mon)
  50554             fields[2] = num[0];
  50555         else
  50556             fields[1] = num[0];
  50557         break;
  50558     case 2:
  50559         if (has_year) {
  50560             fields[1] = num[0];
  50561             fields[2] = num[1];
  50562         } else
  50563         if (has_mon) {
  50564             fields[0] = num[1] + (num[1] < 100) * 1900 + (num[1] < 50) * 100;
  50565             fields[2] = num[0];
  50566         } else {
  50567             fields[1] = num[0];
  50568             fields[2] = num[1];
  50569         }
  50570         break;
  50571     case 3:
  50572         fields[0] = num[2] + (num[2] < 100) * 1900 + (num[2] < 50) * 100;
  50573         fields[1] = num[0];
  50574         fields[2] = num[1];
  50575         break;
  50576     default:
  50577         return FALSE;
  50578     }
  50579     if (fields[1] < 1 || fields[2] < 1)
  50580         return FALSE;
  50581     fields[1] -= 1;
  50582     return TRUE;
  50583 }
  50584 
  50585 static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
  50586                              int argc, JSValueConst *argv)
  50587 {
  50588     JSValue s, rv;
  50589     int fields[9];
  50590     double fields1[9];
  50591     double d;
  50592     int i, c;
  50593     JSString *sp;
  50594     uint8_t buf[128];
  50595     BOOL is_local;
  50596 
  50597     rv = JS_NAN;
  50598 
  50599     s = JS_ToString(ctx, argv[0]);
  50600     if (JS_IsException(s))
  50601         return JS_EXCEPTION;
  50602 
  50603     sp = JS_VALUE_GET_STRING(s);
  50604     /* convert the string as a byte array */
  50605     for (i = 0; i < sp->len && i < (int)countof(buf) - 1; i++) {
  50606         c = string_get(sp, i);
  50607         if (c > 255)
  50608             c = (c == 0x2212) ? '-' : 'x';
  50609         buf[i] = c;
  50610     }
  50611     buf[i] = '\0';
  50612     if (js_date_parse_isostring(buf, fields, &is_local)
  50613     ||  js_date_parse_otherstring(buf, fields, &is_local)) {
  50614         static int const field_max[6] = { 0, 11, 31, 24, 59, 59 };
  50615         BOOL valid = TRUE;
  50616         /* check field maximum values */
  50617         for (i = 1; i < 6; i++) {
  50618             if (fields[i] > field_max[i])
  50619                 valid = FALSE;
  50620         }
  50621         /* special case 24:00:00.000 */
  50622         if (fields[3] == 24 && (fields[4] | fields[5] | fields[6]))
  50623             valid = FALSE;
  50624         if (valid) {
  50625             for(i = 0; i < 7; i++)
  50626                 fields1[i] = fields[i];
  50627             d = set_date_fields(fields1, is_local) - fields[8] * 60000;
  50628             rv = JS_NewFloat64(ctx, d);
  50629         }
  50630     }
  50631     JS_FreeValue(ctx, s);
  50632     return rv;
  50633 }
  50634 
  50635 static JSValue js_Date_now(JSContext *ctx, JSValueConst this_val,
  50636                            int argc, JSValueConst *argv)
  50637 {
  50638     // now()
  50639     return JS_NewInt64(ctx, date_now());
  50640 }
  50641 
  50642 static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValueConst this_val,
  50643                                           int argc, JSValueConst *argv)
  50644 {
  50645     // Symbol_toPrimitive(hint)
  50646     JSValueConst obj = this_val;
  50647     JSAtom hint = JS_ATOM_NULL;
  50648     int hint_num;
  50649 
  50650     if (!JS_IsObject(obj))
  50651         return JS_ThrowTypeErrorNotAnObject(ctx);
  50652 
  50653     if (JS_IsString(argv[0])) {
  50654         hint = JS_ValueToAtom(ctx, argv[0]);
  50655         if (hint == JS_ATOM_NULL)
  50656             return JS_EXCEPTION;
  50657         JS_FreeAtom(ctx, hint);
  50658     }
  50659     switch (hint) {
  50660     case JS_ATOM_number:
  50661     case JS_ATOM_integer:
  50662         hint_num = HINT_NUMBER;
  50663         break;
  50664     case JS_ATOM_string:
  50665     case JS_ATOM_default:
  50666         hint_num = HINT_STRING;
  50667         break;
  50668     default:
  50669         return JS_ThrowTypeError(ctx, "invalid hint");
  50670     }
  50671     return JS_ToPrimitive(ctx, obj, hint_num | HINT_FORCE_ORDINARY);
  50672 }
  50673 
  50674 static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val,
  50675                                          int argc, JSValueConst *argv)
  50676 {
  50677     // getTimezoneOffset()
  50678     double v;
  50679 
  50680     if (JS_ThisTimeValue(ctx, &v, this_val))
  50681         return JS_EXCEPTION;
  50682     if (isnan(v))
  50683         return JS_NAN;
  50684     else
  50685         /* assuming -8.64e15 <= v <= -8.64e15 */
  50686         return JS_NewInt64(ctx, getTimezoneOffset((int64_t)trunc(v)));
  50687 }
  50688 
  50689 static JSValue js_date_getTime(JSContext *ctx, JSValueConst this_val,
  50690                                int argc, JSValueConst *argv)
  50691 {
  50692     // getTime()
  50693     double v;
  50694 
  50695     if (JS_ThisTimeValue(ctx, &v, this_val))
  50696         return JS_EXCEPTION;
  50697     return JS_NewFloat64(ctx, v);
  50698 }
  50699 
  50700 static JSValue js_date_setTime(JSContext *ctx, JSValueConst this_val,
  50701                                int argc, JSValueConst *argv)
  50702 {
  50703     // setTime(v)
  50704     double v;
  50705 
  50706     if (JS_ThisTimeValue(ctx, &v, this_val) || JS_ToFloat64(ctx, &v, argv[0]))
  50707         return JS_EXCEPTION;
  50708     return JS_SetThisTimeValue(ctx, this_val, time_clip(v));
  50709 }
  50710 
  50711 static JSValue js_date_setYear(JSContext *ctx, JSValueConst this_val,
  50712                                int argc, JSValueConst *argv)
  50713 {
  50714     // setYear(y)
  50715     double y;
  50716     JSValueConst args[1];
  50717 
  50718     if (JS_ThisTimeValue(ctx, &y, this_val) || JS_ToFloat64(ctx, &y, argv[0]))
  50719         return JS_EXCEPTION;
  50720     y = +y;
  50721     if (isfinite(y)) {
  50722         y = trunc(y);
  50723         if (y >= 0 && y < 100)
  50724             y += 1900;
  50725     }
  50726     args[0] = JS_NewFloat64(ctx, y);
  50727     return set_date_field(ctx, this_val, 1, args, 0x011);
  50728 }
  50729 
  50730 static JSValue js_date_toJSON(JSContext *ctx, JSValueConst this_val,
  50731                               int argc, JSValueConst *argv)
  50732 {
  50733     // toJSON(key)
  50734     JSValue obj, tv, method, rv;
  50735     double d;
  50736 
  50737     rv = JS_EXCEPTION;
  50738     tv = JS_UNDEFINED;
  50739 
  50740     obj = JS_ToObject(ctx, this_val);
  50741     tv = JS_ToPrimitive(ctx, obj, HINT_NUMBER);
  50742     if (JS_IsException(tv))
  50743         goto exception;
  50744     if (JS_IsNumber(tv)) {
  50745         if (JS_ToFloat64(ctx, &d, tv) < 0)
  50746             goto exception;
  50747         if (!isfinite(d)) {
  50748             rv = JS_NULL;
  50749             goto done;
  50750         }
  50751     }
  50752     method = JS_GetPropertyStr(ctx, obj, "toISOString");
  50753     if (JS_IsException(method))
  50754         goto exception;
  50755     if (!JS_IsFunction(ctx, method)) {
  50756         JS_ThrowTypeError(ctx, "object needs toISOString method");
  50757         JS_FreeValue(ctx, method);
  50758         goto exception;
  50759     }
  50760     rv = JS_CallFree(ctx, method, obj, 0, NULL);
  50761 exception:
  50762 done:
  50763     JS_FreeValue(ctx, obj);
  50764     JS_FreeValue(ctx, tv);
  50765     return rv;
  50766 }
  50767 
  50768 static const JSCFunctionListEntry js_date_funcs[] = {
  50769     JS_CFUNC_DEF("now", 0, js_Date_now ),
  50770     JS_CFUNC_DEF("parse", 1, js_Date_parse ),
  50771     JS_CFUNC_DEF("UTC", 7, js_Date_UTC ),
  50772 };
  50773 
  50774 static const JSCFunctionListEntry js_date_proto_funcs[] = {
  50775     JS_CFUNC_DEF("valueOf", 0, js_date_getTime ),
  50776     JS_CFUNC_MAGIC_DEF("toString", 0, get_date_string, 0x13 ),
  50777     JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_date_Symbol_toPrimitive ),
  50778     JS_CFUNC_MAGIC_DEF("toUTCString", 0, get_date_string, 0x03 ),
  50779     JS_ALIAS_DEF("toGMTString", "toUTCString" ),
  50780     JS_CFUNC_MAGIC_DEF("toISOString", 0, get_date_string, 0x23 ),
  50781     JS_CFUNC_MAGIC_DEF("toDateString", 0, get_date_string, 0x11 ),
  50782     JS_CFUNC_MAGIC_DEF("toTimeString", 0, get_date_string, 0x12 ),
  50783     JS_CFUNC_MAGIC_DEF("toLocaleString", 0, get_date_string, 0x33 ),
  50784     JS_CFUNC_MAGIC_DEF("toLocaleDateString", 0, get_date_string, 0x31 ),
  50785     JS_CFUNC_MAGIC_DEF("toLocaleTimeString", 0, get_date_string, 0x32 ),
  50786     JS_CFUNC_DEF("getTimezoneOffset", 0, js_date_getTimezoneOffset ),
  50787     JS_CFUNC_DEF("getTime", 0, js_date_getTime ),
  50788     JS_CFUNC_MAGIC_DEF("getYear", 0, get_date_field, 0x101 ),
  50789     JS_CFUNC_MAGIC_DEF("getFullYear", 0, get_date_field, 0x01 ),
  50790     JS_CFUNC_MAGIC_DEF("getUTCFullYear", 0, get_date_field, 0x00 ),
  50791     JS_CFUNC_MAGIC_DEF("getMonth", 0, get_date_field, 0x11 ),
  50792     JS_CFUNC_MAGIC_DEF("getUTCMonth", 0, get_date_field, 0x10 ),
  50793     JS_CFUNC_MAGIC_DEF("getDate", 0, get_date_field, 0x21 ),
  50794     JS_CFUNC_MAGIC_DEF("getUTCDate", 0, get_date_field, 0x20 ),
  50795     JS_CFUNC_MAGIC_DEF("getHours", 0, get_date_field, 0x31 ),
  50796     JS_CFUNC_MAGIC_DEF("getUTCHours", 0, get_date_field, 0x30 ),
  50797     JS_CFUNC_MAGIC_DEF("getMinutes", 0, get_date_field, 0x41 ),
  50798     JS_CFUNC_MAGIC_DEF("getUTCMinutes", 0, get_date_field, 0x40 ),
  50799     JS_CFUNC_MAGIC_DEF("getSeconds", 0, get_date_field, 0x51 ),
  50800     JS_CFUNC_MAGIC_DEF("getUTCSeconds", 0, get_date_field, 0x50 ),
  50801     JS_CFUNC_MAGIC_DEF("getMilliseconds", 0, get_date_field, 0x61 ),
  50802     JS_CFUNC_MAGIC_DEF("getUTCMilliseconds", 0, get_date_field, 0x60 ),
  50803     JS_CFUNC_MAGIC_DEF("getDay", 0, get_date_field, 0x71 ),
  50804     JS_CFUNC_MAGIC_DEF("getUTCDay", 0, get_date_field, 0x70 ),
  50805     JS_CFUNC_DEF("setTime", 1, js_date_setTime ),
  50806     JS_CFUNC_MAGIC_DEF("setMilliseconds", 1, set_date_field, 0x671 ),
  50807     JS_CFUNC_MAGIC_DEF("setUTCMilliseconds", 1, set_date_field, 0x670 ),
  50808     JS_CFUNC_MAGIC_DEF("setSeconds", 2, set_date_field, 0x571 ),
  50809     JS_CFUNC_MAGIC_DEF("setUTCSeconds", 2, set_date_field, 0x570 ),
  50810     JS_CFUNC_MAGIC_DEF("setMinutes", 3, set_date_field, 0x471 ),
  50811     JS_CFUNC_MAGIC_DEF("setUTCMinutes", 3, set_date_field, 0x470 ),
  50812     JS_CFUNC_MAGIC_DEF("setHours", 4, set_date_field, 0x371 ),
  50813     JS_CFUNC_MAGIC_DEF("setUTCHours", 4, set_date_field, 0x370 ),
  50814     JS_CFUNC_MAGIC_DEF("setDate", 1, set_date_field, 0x231 ),
  50815     JS_CFUNC_MAGIC_DEF("setUTCDate", 1, set_date_field, 0x230 ),
  50816     JS_CFUNC_MAGIC_DEF("setMonth", 2, set_date_field, 0x131 ),
  50817     JS_CFUNC_MAGIC_DEF("setUTCMonth", 2, set_date_field, 0x130 ),
  50818     JS_CFUNC_DEF("setYear", 1, js_date_setYear ),
  50819     JS_CFUNC_MAGIC_DEF("setFullYear", 3, set_date_field, 0x031 ),
  50820     JS_CFUNC_MAGIC_DEF("setUTCFullYear", 3, set_date_field, 0x030 ),
  50821     JS_CFUNC_DEF("toJSON", 1, js_date_toJSON ),
  50822 };
  50823 
  50824 JSValue JS_NewDate(JSContext *ctx, double epoch_ms)
  50825 {
  50826     JSValue obj = js_create_from_ctor(ctx, JS_UNDEFINED, JS_CLASS_DATE);
  50827     if (JS_IsException(obj))
  50828         return JS_EXCEPTION;
  50829     JS_SetObjectData(ctx, obj, __JS_NewFloat64(ctx, time_clip(epoch_ms)));
  50830     return obj;
  50831 }
  50832 
  50833 void JS_AddIntrinsicDate(JSContext *ctx)
  50834 {
  50835     JSValueConst obj;
  50836 
  50837     /* Date */
  50838     ctx->class_proto[JS_CLASS_DATE] = JS_NewObject(ctx);
  50839     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATE], js_date_proto_funcs,
  50840                                countof(js_date_proto_funcs));
  50841     obj = JS_NewGlobalCConstructor(ctx, "Date", js_date_constructor, 7,
  50842                                    ctx->class_proto[JS_CLASS_DATE]);
  50843     JS_SetPropertyFunctionList(ctx, obj, js_date_funcs, countof(js_date_funcs));
  50844 }
  50845 
  50846 /* eval */
  50847 
  50848 void JS_AddIntrinsicEval(JSContext *ctx)
  50849 {
  50850     ctx->eval_internal = __JS_EvalInternal;
  50851 }
  50852 
  50853 #ifdef CONFIG_BIGNUM
  50854 
  50855 /* Operators */
  50856 
  50857 static void js_operator_set_finalizer(JSRuntime *rt, JSValue val)
  50858 {
  50859     JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET);
  50860     int i, j;
  50861     JSBinaryOperatorDefEntry *ent;
  50862 
  50863     if (opset) {
  50864         for(i = 0; i < JS_OVOP_COUNT; i++) {
  50865             if (opset->self_ops[i])
  50866                 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i]));
  50867         }
  50868         for(j = 0; j < opset->left.count; j++) {
  50869             ent = &opset->left.tab[j];
  50870             for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
  50871                 if (ent->ops[i])
  50872                     JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]));
  50873             }
  50874         }
  50875         js_free_rt(rt, opset->left.tab);
  50876         for(j = 0; j < opset->right.count; j++) {
  50877             ent = &opset->right.tab[j];
  50878             for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
  50879                 if (ent->ops[i])
  50880                     JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]));
  50881             }
  50882         }
  50883         js_free_rt(rt, opset->right.tab);
  50884         js_free_rt(rt, opset);
  50885     }
  50886 }
  50887 
  50888 static void js_operator_set_mark(JSRuntime *rt, JSValueConst val,
  50889                                  JS_MarkFunc *mark_func)
  50890 {
  50891     JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET);
  50892     int i, j;
  50893     JSBinaryOperatorDefEntry *ent;
  50894 
  50895     if (opset) {
  50896         for(i = 0; i < JS_OVOP_COUNT; i++) {
  50897             if (opset->self_ops[i])
  50898                 JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i]),
  50899                              mark_func);
  50900         }
  50901         for(j = 0; j < opset->left.count; j++) {
  50902             ent = &opset->left.tab[j];
  50903             for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
  50904                 if (ent->ops[i])
  50905                     JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]),
  50906                                  mark_func);
  50907             }
  50908         }
  50909         for(j = 0; j < opset->right.count; j++) {
  50910             ent = &opset->right.tab[j];
  50911             for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
  50912                 if (ent->ops[i])
  50913                     JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]),
  50914                                  mark_func);
  50915             }
  50916         }
  50917     }
  50918 }
  50919 
  50920 
  50921 /* create an OperatorSet object */
  50922 static JSValue js_operators_create_internal(JSContext *ctx,
  50923                                             int argc, JSValueConst *argv,
  50924                                             BOOL is_primitive)
  50925 {
  50926     JSValue opset_obj, prop, obj;
  50927     JSOperatorSetData *opset, *opset1;
  50928     JSBinaryOperatorDef *def;
  50929     JSValueConst arg;
  50930     int i, j;
  50931     JSBinaryOperatorDefEntry *new_tab;
  50932     JSBinaryOperatorDefEntry *ent;
  50933     uint32_t op_count;
  50934 
  50935     if (ctx->rt->operator_count == UINT32_MAX) {
  50936         return JS_ThrowTypeError(ctx, "too many operators");
  50937     }
  50938     opset_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_OPERATOR_SET);
  50939     if (JS_IsException(opset_obj))
  50940         goto fail;
  50941     opset = js_mallocz(ctx, sizeof(*opset));
  50942     if (!opset)
  50943         goto fail;
  50944     JS_SetOpaque(opset_obj, opset);
  50945     if (argc >= 1) {
  50946         arg = argv[0];
  50947         /* self operators */
  50948         for(i = 0; i < JS_OVOP_COUNT; i++) {
  50949             prop = JS_GetPropertyStr(ctx, arg, js_overloadable_operator_names[i]);
  50950             if (JS_IsException(prop))
  50951                 goto fail;
  50952             if (!JS_IsUndefined(prop)) {
  50953                 if (check_function(ctx, prop)) {
  50954                     JS_FreeValue(ctx, prop);
  50955                     goto fail;
  50956                 }
  50957                 opset->self_ops[i] = JS_VALUE_GET_OBJ(prop);
  50958             }
  50959         }
  50960     }
  50961     /* left & right operators */
  50962     for(j = 1; j < argc; j++) {
  50963         arg = argv[j];
  50964         prop = JS_GetPropertyStr(ctx, arg, "left");
  50965         if (JS_IsException(prop))
  50966             goto fail;
  50967         def = &opset->right;
  50968         if (JS_IsUndefined(prop)) {
  50969             prop = JS_GetPropertyStr(ctx, arg, "right");
  50970             if (JS_IsException(prop))
  50971                 goto fail;
  50972             if (JS_IsUndefined(prop)) {
  50973                 JS_ThrowTypeError(ctx, "left or right property must be present");
  50974                 goto fail;
  50975             }
  50976             def = &opset->left;
  50977         }
  50978         /* get the operator set */
  50979         obj = JS_GetProperty(ctx, prop, JS_ATOM_prototype);
  50980         JS_FreeValue(ctx, prop);
  50981         if (JS_IsException(obj))
  50982             goto fail;
  50983         prop = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet);
  50984         JS_FreeValue(ctx, obj);
  50985         if (JS_IsException(prop))
  50986             goto fail;
  50987         opset1 = JS_GetOpaque2(ctx, prop, JS_CLASS_OPERATOR_SET);
  50988         if (!opset1) {
  50989             JS_FreeValue(ctx, prop);
  50990             goto fail;
  50991         }
  50992         op_count = opset1->operator_counter;
  50993         JS_FreeValue(ctx, prop);
  50994 
  50995         /* we assume there are few entries */
  50996         new_tab = js_realloc(ctx, def->tab,
  50997                              (def->count + 1) * sizeof(def->tab[0]));
  50998         if (!new_tab)
  50999             goto fail;
  51000         def->tab = new_tab;
  51001         def->count++;
  51002         ent = def->tab + def->count - 1;
  51003         memset(ent, 0, sizeof(def->tab[0]));
  51004         ent->operator_index = op_count;
  51005 
  51006         for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
  51007             prop = JS_GetPropertyStr(ctx, arg,
  51008                                      js_overloadable_operator_names[i]);
  51009             if (JS_IsException(prop))
  51010                 goto fail;
  51011             if (!JS_IsUndefined(prop)) {
  51012                 if (check_function(ctx, prop)) {
  51013                     JS_FreeValue(ctx, prop);
  51014                     goto fail;
  51015                 }
  51016                 ent->ops[i] = JS_VALUE_GET_OBJ(prop);
  51017             }
  51018         }
  51019     }
  51020     opset->is_primitive = is_primitive;
  51021     opset->operator_counter = ctx->rt->operator_count++;
  51022     return opset_obj;
  51023  fail:
  51024     JS_FreeValue(ctx, opset_obj);
  51025     return JS_EXCEPTION;
  51026 }
  51027 
  51028 static JSValue js_operators_create(JSContext *ctx, JSValueConst this_val,
  51029                                 int argc, JSValueConst *argv)
  51030 {
  51031     return js_operators_create_internal(ctx, argc, argv, FALSE);
  51032 }
  51033 
  51034 static JSValue js_operators_updateBigIntOperators(JSContext *ctx, JSValueConst this_val,
  51035                                                   int argc, JSValueConst *argv)
  51036 {
  51037     JSValue opset_obj, prop;
  51038     JSOperatorSetData *opset;
  51039     const JSOverloadableOperatorEnum ops[2] = { JS_OVOP_DIV, JS_OVOP_POW };
  51040     JSOverloadableOperatorEnum op;
  51041     int i;
  51042 
  51043     opset_obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_BIG_INT],
  51044                                JS_ATOM_Symbol_operatorSet);
  51045     if (JS_IsException(opset_obj))
  51046         goto fail;
  51047     opset = JS_GetOpaque2(ctx, opset_obj, JS_CLASS_OPERATOR_SET);
  51048     if (!opset)
  51049         goto fail;
  51050     for(i = 0; i < countof(ops); i++) {
  51051         op = ops[i];
  51052         prop = JS_GetPropertyStr(ctx, argv[0],
  51053                                  js_overloadable_operator_names[op]);
  51054         if (JS_IsException(prop))
  51055             goto fail;
  51056         if (!JS_IsUndefined(prop)) {
  51057             if (!JS_IsNull(prop) && check_function(ctx, prop)) {
  51058                 JS_FreeValue(ctx, prop);
  51059                 goto fail;
  51060             }
  51061             if (opset->self_ops[op])
  51062                 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[op]));
  51063             if (JS_IsNull(prop)) {
  51064                 opset->self_ops[op] = NULL;
  51065             } else {
  51066                 opset->self_ops[op] = JS_VALUE_GET_PTR(prop);
  51067             }
  51068         }
  51069     }
  51070     JS_FreeValue(ctx, opset_obj);
  51071     return JS_UNDEFINED;
  51072  fail:
  51073     JS_FreeValue(ctx, opset_obj);
  51074     return JS_EXCEPTION;
  51075 }
  51076 
  51077 static int js_operators_set_default(JSContext *ctx, JSValueConst obj)
  51078 {
  51079     JSValue opset_obj;
  51080 
  51081     if (!JS_IsObject(obj)) /* in case the prototype is not defined */
  51082         return 0;
  51083     opset_obj = js_operators_create_internal(ctx, 0, NULL, TRUE);
  51084     if (JS_IsException(opset_obj))
  51085         return -1;
  51086     /* cannot be modified by the user */
  51087     JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_operatorSet,
  51088                            opset_obj, 0);
  51089     return 0;
  51090 }
  51091 
  51092 static JSValue js_dummy_operators_ctor(JSContext *ctx, JSValueConst new_target,
  51093                                        int argc, JSValueConst *argv)
  51094 {
  51095     return js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);
  51096 }
  51097 
  51098 static JSValue js_global_operators(JSContext *ctx, JSValueConst this_val,
  51099                                    int argc, JSValueConst *argv)
  51100 {
  51101     JSValue func_obj, proto, opset_obj;
  51102 
  51103     func_obj = JS_UNDEFINED;
  51104     proto = JS_NewObject(ctx);
  51105     if (JS_IsException(proto))
  51106         return JS_EXCEPTION;
  51107     opset_obj = js_operators_create_internal(ctx, argc, argv, FALSE);
  51108     if (JS_IsException(opset_obj))
  51109         goto fail;
  51110     JS_DefinePropertyValue(ctx, proto, JS_ATOM_Symbol_operatorSet,
  51111                            opset_obj, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
  51112     func_obj = JS_NewCFunction2(ctx, js_dummy_operators_ctor, "Operators",
  51113                                 0, JS_CFUNC_constructor, 0);
  51114     if (JS_IsException(func_obj))
  51115         goto fail;
  51116     JS_SetConstructor2(ctx, func_obj, proto,
  51117                        0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
  51118     JS_FreeValue(ctx, proto);
  51119     return func_obj;
  51120  fail:
  51121     JS_FreeValue(ctx, proto);
  51122     JS_FreeValue(ctx, func_obj);
  51123     return JS_EXCEPTION;
  51124 }
  51125 
  51126 static const JSCFunctionListEntry js_operators_funcs[] = {
  51127     JS_CFUNC_DEF("create", 1, js_operators_create ),
  51128     JS_CFUNC_DEF("updateBigIntOperators", 2, js_operators_updateBigIntOperators ),
  51129 };
  51130 
  51131 /* must be called after all overloadable base types are initialized */
  51132 void JS_AddIntrinsicOperators(JSContext *ctx)
  51133 {
  51134     JSValue obj;
  51135 
  51136     ctx->allow_operator_overloading = TRUE;
  51137     obj = JS_NewCFunction(ctx, js_global_operators, "Operators", 1);
  51138     JS_SetPropertyFunctionList(ctx, obj,
  51139                                js_operators_funcs,
  51140                                countof(js_operators_funcs));
  51141     JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_Operators,
  51142                            obj,
  51143                            JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
  51144     /* add default operatorSets */
  51145     js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BOOLEAN]);
  51146     js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_NUMBER]);
  51147     js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_STRING]);
  51148     js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_INT]);
  51149     js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT]);
  51150     js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL]);
  51151 }
  51152 #endif /* CONFIG_BIGNUM */
  51153 
  51154 /* BigInt */
  51155 
  51156 static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val)
  51157 {
  51158     uint32_t tag;
  51159 
  51160  redo:
  51161     tag = JS_VALUE_GET_NORM_TAG(val);
  51162     switch(tag) {
  51163     case JS_TAG_INT:
  51164     case JS_TAG_BOOL:
  51165         val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val));
  51166         break;
  51167     case JS_TAG_BIG_INT:
  51168         break;
  51169     case JS_TAG_FLOAT64:
  51170 #ifdef CONFIG_BIGNUM
  51171     case JS_TAG_BIG_FLOAT:
  51172 #endif
  51173         {
  51174             bf_t *a, a_s;
  51175 
  51176             a = JS_ToBigFloat(ctx, &a_s, val);
  51177             if (!a) {
  51178                 JS_FreeValue(ctx, val);
  51179                 return JS_EXCEPTION;
  51180             }
  51181             if (!bf_is_finite(a)) {
  51182                 JS_FreeValue(ctx, val);
  51183                 val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to BigInt");
  51184             } else {
  51185                 JSValue val1 = JS_NewBigInt(ctx);
  51186                 bf_t *r;
  51187                 int ret;
  51188                 if (JS_IsException(val1)) {
  51189                     JS_FreeValue(ctx, val);
  51190                     return JS_EXCEPTION;
  51191                 }
  51192                 r = JS_GetBigInt(val1);
  51193                 ret = bf_set(r, a);
  51194                 ret |= bf_rint(r, BF_RNDZ);
  51195                 JS_FreeValue(ctx, val);
  51196                 if (ret & BF_ST_MEM_ERROR) {
  51197                     JS_FreeValue(ctx, val1);
  51198                     val = JS_ThrowOutOfMemory(ctx);
  51199                 } else if (ret & BF_ST_INEXACT) {
  51200                     JS_FreeValue(ctx, val1);
  51201                     val = JS_ThrowRangeError(ctx, "cannot convert to BigInt: not an integer");
  51202                 } else {
  51203                     val = JS_CompactBigInt(ctx, val1);
  51204                 }
  51205             }
  51206             if (a == &a_s)
  51207                 bf_delete(a);
  51208         }
  51209         break;
  51210 #ifdef CONFIG_BIGNUM
  51211     case JS_TAG_BIG_DECIMAL:
  51212         val = JS_ToStringFree(ctx, val);
  51213         if (JS_IsException(val))
  51214             break;
  51215         goto redo;
  51216 #endif
  51217     case JS_TAG_STRING:
  51218         val = JS_StringToBigIntErr(ctx, val);
  51219         break;
  51220     case JS_TAG_OBJECT:
  51221         val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
  51222         if (JS_IsException(val))
  51223             break;
  51224         goto redo;
  51225     case JS_TAG_NULL:
  51226     case JS_TAG_UNDEFINED:
  51227     default:
  51228         JS_FreeValue(ctx, val);
  51229         return JS_ThrowTypeError(ctx, "cannot convert to BigInt");
  51230     }
  51231     return val;
  51232 }
  51233 
  51234 static JSValue js_bigint_constructor(JSContext *ctx,
  51235                                      JSValueConst new_target,
  51236                                      int argc, JSValueConst *argv)
  51237 {
  51238     if (!JS_IsUndefined(new_target))
  51239         return JS_ThrowTypeError(ctx, "not a constructor");
  51240     return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0]));
  51241 }
  51242 
  51243 static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val)
  51244 {
  51245     if (JS_IsBigInt(ctx, this_val))
  51246         return JS_DupValue(ctx, this_val);
  51247 
  51248     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
  51249         JSObject *p = JS_VALUE_GET_OBJ(this_val);
  51250         if (p->class_id == JS_CLASS_BIG_INT) {
  51251             if (JS_IsBigInt(ctx, p->u.object_data))
  51252                 return JS_DupValue(ctx, p->u.object_data);
  51253         }
  51254     }
  51255     return JS_ThrowTypeError(ctx, "not a BigInt");
  51256 }
  51257 
  51258 static JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val,
  51259                                   int argc, JSValueConst *argv)
  51260 {
  51261     JSValue val;
  51262     int base;
  51263     JSValue ret;
  51264 
  51265     val = js_thisBigIntValue(ctx, this_val);
  51266     if (JS_IsException(val))
  51267         return val;
  51268     if (argc == 0 || JS_IsUndefined(argv[0])) {
  51269         base = 10;
  51270     } else {
  51271         base = js_get_radix(ctx, argv[0]);
  51272         if (base < 0)
  51273             goto fail;
  51274     }
  51275     ret = js_bigint_to_string1(ctx, val, base);
  51276     JS_FreeValue(ctx, val);
  51277     return ret;
  51278  fail:
  51279     JS_FreeValue(ctx, val);
  51280     return JS_EXCEPTION;
  51281 }
  51282 
  51283 static JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val,
  51284                                  int argc, JSValueConst *argv)
  51285 {
  51286     return js_thisBigIntValue(ctx, this_val);
  51287 }
  51288 
  51289 #ifdef CONFIG_BIGNUM
  51290 static JSValue js_bigint_div(JSContext *ctx,
  51291                               JSValueConst this_val,
  51292                               int argc, JSValueConst *argv, int magic)
  51293 {
  51294     bf_t a_s, b_s, *a, *b, *r, *q;
  51295     int status;
  51296     JSValue q_val, r_val;
  51297 
  51298     q_val = JS_NewBigInt(ctx);
  51299     if (JS_IsException(q_val))
  51300         return JS_EXCEPTION;
  51301     r_val = JS_NewBigInt(ctx);
  51302     if (JS_IsException(r_val))
  51303         goto fail;
  51304     b = NULL;
  51305     a = JS_ToBigInt(ctx, &a_s, argv[0]);
  51306     if (!a)
  51307         goto fail;
  51308     b = JS_ToBigInt(ctx, &b_s, argv[1]);
  51309     if (!b) {
  51310         JS_FreeBigInt(ctx, a, &a_s);
  51311         goto fail;
  51312     }
  51313     q = JS_GetBigInt(q_val);
  51314     r = JS_GetBigInt(r_val);
  51315     status = bf_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ, magic & 0xf);
  51316     JS_FreeBigInt(ctx, a, &a_s);
  51317     JS_FreeBigInt(ctx, b, &b_s);
  51318     if (unlikely(status)) {
  51319         throw_bf_exception(ctx, status);
  51320         goto fail;
  51321     }
  51322     q_val = JS_CompactBigInt(ctx, q_val);
  51323     if (magic & 0x10) {
  51324         JSValue ret;
  51325         ret = JS_NewArray(ctx);
  51326         if (JS_IsException(ret))
  51327             goto fail;
  51328         JS_SetPropertyUint32(ctx, ret, 0, q_val);
  51329         JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, r_val));
  51330         return ret;
  51331     } else {
  51332         JS_FreeValue(ctx, r_val);
  51333         return q_val;
  51334     }
  51335  fail:
  51336     JS_FreeValue(ctx, q_val);
  51337     JS_FreeValue(ctx, r_val);
  51338     return JS_EXCEPTION;
  51339 }
  51340 
  51341 static JSValue js_bigint_sqrt(JSContext *ctx,
  51342                                JSValueConst this_val,
  51343                                int argc, JSValueConst *argv, int magic)
  51344 {
  51345     bf_t a_s, *a, *r, *rem;
  51346     int status;
  51347     JSValue r_val, rem_val;
  51348 
  51349     r_val = JS_NewBigInt(ctx);
  51350     if (JS_IsException(r_val))
  51351         return JS_EXCEPTION;
  51352     rem_val = JS_NewBigInt(ctx);
  51353     if (JS_IsException(rem_val))
  51354         return JS_EXCEPTION;
  51355     r = JS_GetBigInt(r_val);
  51356     rem = JS_GetBigInt(rem_val);
  51357 
  51358     a = JS_ToBigInt(ctx, &a_s, argv[0]);
  51359     if (!a)
  51360         goto fail;
  51361     status = bf_sqrtrem(r, rem, a);
  51362     JS_FreeBigInt(ctx, a, &a_s);
  51363     if (unlikely(status & ~BF_ST_INEXACT)) {
  51364         throw_bf_exception(ctx, status);
  51365         goto fail;
  51366     }
  51367     r_val = JS_CompactBigInt(ctx, r_val);
  51368     if (magic) {
  51369         JSValue ret;
  51370         ret = JS_NewArray(ctx);
  51371         if (JS_IsException(ret))
  51372             goto fail;
  51373         JS_SetPropertyUint32(ctx, ret, 0, r_val);
  51374         JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, rem_val));
  51375         return ret;
  51376     } else {
  51377         JS_FreeValue(ctx, rem_val);
  51378         return r_val;
  51379     }
  51380  fail:
  51381     JS_FreeValue(ctx, r_val);
  51382     JS_FreeValue(ctx, rem_val);
  51383     return JS_EXCEPTION;
  51384 }
  51385 
  51386 static JSValue js_bigint_op1(JSContext *ctx,
  51387                               JSValueConst this_val,
  51388                               int argc, JSValueConst *argv,
  51389                               int magic)
  51390 {
  51391     bf_t a_s, *a;
  51392     int64_t res;
  51393 
  51394     a = JS_ToBigInt(ctx, &a_s, argv[0]);
  51395     if (!a)
  51396         return JS_EXCEPTION;
  51397     switch(magic) {
  51398     case 0: /* floorLog2 */
  51399         if (a->sign || a->expn <= 0) {
  51400             res = -1;
  51401         } else {
  51402             res = a->expn - 1;
  51403         }
  51404         break;
  51405     case 1: /* ctz */
  51406         if (bf_is_zero(a)) {
  51407             res = -1;
  51408         } else {
  51409             res = bf_get_exp_min(a);
  51410         }
  51411         break;
  51412     default:
  51413         abort();
  51414     }
  51415     JS_FreeBigInt(ctx, a, &a_s);
  51416     return JS_NewBigInt64(ctx, res);
  51417 }
  51418 #endif
  51419 
  51420 static JSValue js_bigint_asUintN(JSContext *ctx,
  51421                                   JSValueConst this_val,
  51422                                   int argc, JSValueConst *argv, int asIntN)
  51423 {
  51424     uint64_t bits;
  51425     bf_t a_s, *a = &a_s, *r, mask_s, *mask = &mask_s;
  51426     JSValue res;
  51427 
  51428     if (JS_ToIndex(ctx, &bits, argv[0]))
  51429         return JS_EXCEPTION;
  51430     res = JS_NewBigInt(ctx);
  51431     if (JS_IsException(res))
  51432         return JS_EXCEPTION;
  51433     r = JS_GetBigInt(res);
  51434     a = JS_ToBigInt(ctx, &a_s, argv[1]);
  51435     if (!a) {
  51436         JS_FreeValue(ctx, res);
  51437         return JS_EXCEPTION;
  51438     }
  51439     /* XXX: optimize */
  51440     r = JS_GetBigInt(res);
  51441     bf_init(ctx->bf_ctx, mask);
  51442     bf_set_ui(mask, 1);
  51443     bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ);
  51444     bf_add_si(mask, mask, -1, BF_PREC_INF, BF_RNDZ);
  51445     bf_logic_and(r, a, mask);
  51446     if (asIntN && bits != 0) {
  51447         bf_set_ui(mask, 1);
  51448         bf_mul_2exp(mask, bits - 1, BF_PREC_INF, BF_RNDZ);
  51449         if (bf_cmpu(r, mask) >= 0) {
  51450             bf_set_ui(mask, 1);
  51451             bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ);
  51452             bf_sub(r, r, mask, BF_PREC_INF, BF_RNDZ);
  51453         }
  51454     }
  51455     bf_delete(mask);
  51456     JS_FreeBigInt(ctx, a, &a_s);
  51457     return JS_CompactBigInt(ctx, res);
  51458 }
  51459 
  51460 static const JSCFunctionListEntry js_bigint_funcs[] = {
  51461     JS_CFUNC_MAGIC_DEF("asUintN", 2, js_bigint_asUintN, 0 ),
  51462     JS_CFUNC_MAGIC_DEF("asIntN", 2, js_bigint_asUintN, 1 ),
  51463 #ifdef CONFIG_BIGNUM
  51464     /* QuickJS extensions */
  51465     JS_CFUNC_MAGIC_DEF("tdiv", 2, js_bigint_div, BF_RNDZ ),
  51466     JS_CFUNC_MAGIC_DEF("fdiv", 2, js_bigint_div, BF_RNDD ),
  51467     JS_CFUNC_MAGIC_DEF("cdiv", 2, js_bigint_div, BF_RNDU ),
  51468     JS_CFUNC_MAGIC_DEF("ediv", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN ),
  51469     JS_CFUNC_MAGIC_DEF("tdivrem", 2, js_bigint_div, BF_RNDZ | 0x10 ),
  51470     JS_CFUNC_MAGIC_DEF("fdivrem", 2, js_bigint_div, BF_RNDD | 0x10 ),
  51471     JS_CFUNC_MAGIC_DEF("cdivrem", 2, js_bigint_div, BF_RNDU | 0x10 ),
  51472     JS_CFUNC_MAGIC_DEF("edivrem", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN | 0x10 ),
  51473     JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigint_sqrt, 0 ),
  51474     JS_CFUNC_MAGIC_DEF("sqrtrem", 1, js_bigint_sqrt, 1 ),
  51475     JS_CFUNC_MAGIC_DEF("floorLog2", 1, js_bigint_op1, 0 ),
  51476     JS_CFUNC_MAGIC_DEF("ctz", 1, js_bigint_op1, 1 ),
  51477 #endif
  51478 };
  51479 
  51480 static const JSCFunctionListEntry js_bigint_proto_funcs[] = {
  51481     JS_CFUNC_DEF("toString", 0, js_bigint_toString ),
  51482     JS_CFUNC_DEF("valueOf", 0, js_bigint_valueOf ),
  51483     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "BigInt", JS_PROP_CONFIGURABLE ),
  51484 };
  51485 
  51486 void JS_AddIntrinsicBigInt(JSContext *ctx)
  51487 {
  51488     JSRuntime *rt = ctx->rt;
  51489     JSValueConst obj1;
  51490 
  51491     rt->bigint_ops.to_string = js_bigint_to_string;
  51492     rt->bigint_ops.from_string = js_string_to_bigint;
  51493     rt->bigint_ops.unary_arith = js_unary_arith_bigint;
  51494     rt->bigint_ops.binary_arith = js_binary_arith_bigint;
  51495     rt->bigint_ops.compare = js_compare_bigfloat;
  51496 
  51497     ctx->class_proto[JS_CLASS_BIG_INT] = JS_NewObject(ctx);
  51498     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_INT],
  51499                                js_bigint_proto_funcs,
  51500                                countof(js_bigint_proto_funcs));
  51501     obj1 = JS_NewGlobalCConstructor(ctx, "BigInt", js_bigint_constructor, 1,
  51502                                     ctx->class_proto[JS_CLASS_BIG_INT]);
  51503     JS_SetPropertyFunctionList(ctx, obj1, js_bigint_funcs,
  51504                                countof(js_bigint_funcs));
  51505 }
  51506 
  51507 #ifdef CONFIG_BIGNUM
  51508 
  51509 /* BigFloat */
  51510 
  51511 static JSValue js_thisBigFloatValue(JSContext *ctx, JSValueConst this_val)
  51512 {
  51513     if (JS_IsBigFloat(this_val))
  51514         return JS_DupValue(ctx, this_val);
  51515 
  51516     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
  51517         JSObject *p = JS_VALUE_GET_OBJ(this_val);
  51518         if (p->class_id == JS_CLASS_BIG_FLOAT) {
  51519             if (JS_IsBigFloat(p->u.object_data))
  51520                 return JS_DupValue(ctx, p->u.object_data);
  51521         }
  51522     }
  51523     return JS_ThrowTypeError(ctx, "not a bigfloat");
  51524 }
  51525 
  51526 static JSValue js_bigfloat_toString(JSContext *ctx, JSValueConst this_val,
  51527                                     int argc, JSValueConst *argv)
  51528 {
  51529     JSValue val;
  51530     int base;
  51531     JSValue ret;
  51532 
  51533     val = js_thisBigFloatValue(ctx, this_val);
  51534     if (JS_IsException(val))
  51535         return val;
  51536     if (argc == 0 || JS_IsUndefined(argv[0])) {
  51537         base = 10;
  51538     } else {
  51539         base = js_get_radix(ctx, argv[0]);
  51540         if (base < 0)
  51541             goto fail;
  51542     }
  51543     ret = js_ftoa(ctx, val, base, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN);
  51544     JS_FreeValue(ctx, val);
  51545     return ret;
  51546  fail:
  51547     JS_FreeValue(ctx, val);
  51548     return JS_EXCEPTION;
  51549 }
  51550 
  51551 static JSValue js_bigfloat_valueOf(JSContext *ctx, JSValueConst this_val,
  51552                                    int argc, JSValueConst *argv)
  51553 {
  51554     return js_thisBigFloatValue(ctx, this_val);
  51555 }
  51556 
  51557 static int bigfloat_get_rnd_mode(JSContext *ctx, JSValueConst val)
  51558 {
  51559     int rnd_mode;
  51560     if (JS_ToInt32Sat(ctx, &rnd_mode, val))
  51561         return -1;
  51562     if (rnd_mode < BF_RNDN || rnd_mode > BF_RNDF) {
  51563         JS_ThrowRangeError(ctx, "invalid rounding mode");
  51564         return -1;
  51565     }
  51566     return rnd_mode;
  51567 }
  51568 
  51569 static JSValue js_bigfloat_toFixed(JSContext *ctx, JSValueConst this_val,
  51570                                  int argc, JSValueConst *argv)
  51571 {
  51572     JSValue val, ret;
  51573     int64_t f;
  51574     int rnd_mode, radix;
  51575 
  51576     val = js_thisBigFloatValue(ctx, this_val);
  51577     if (JS_IsException(val))
  51578         return val;
  51579     if (JS_ToInt64Sat(ctx, &f, argv[0]))
  51580         goto fail;
  51581     if (f < 0 || f > BF_PREC_MAX) {
  51582         JS_ThrowRangeError(ctx, "invalid number of digits");
  51583         goto fail;
  51584     }
  51585     rnd_mode = BF_RNDNA;
  51586     radix = 10;
  51587     /* XXX: swap parameter order for rounding mode and radix */
  51588     if (argc > 1) {
  51589         rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
  51590         if (rnd_mode < 0)
  51591             goto fail;
  51592     }
  51593     if (argc > 2) {
  51594         radix = js_get_radix(ctx, argv[2]);
  51595         if (radix < 0)
  51596             goto fail;
  51597     }
  51598     ret = js_ftoa(ctx, val, radix, f, rnd_mode | BF_FTOA_FORMAT_FRAC);
  51599     JS_FreeValue(ctx, val);
  51600     return ret;
  51601  fail:
  51602     JS_FreeValue(ctx, val);
  51603     return JS_EXCEPTION;
  51604 }
  51605 
  51606 static BOOL js_bigfloat_is_finite(JSContext *ctx, JSValueConst val)
  51607 {
  51608     BOOL res;
  51609     uint32_t tag;
  51610 
  51611     tag = JS_VALUE_GET_NORM_TAG(val);
  51612     switch(tag) {
  51613     case JS_TAG_BIG_FLOAT:
  51614         {
  51615             JSBigFloat *p = JS_VALUE_GET_PTR(val);
  51616             res = bf_is_finite(&p->num);
  51617         }
  51618         break;
  51619     default:
  51620         res = FALSE;
  51621         break;
  51622     }
  51623     return res;
  51624 }
  51625 
  51626 static JSValue js_bigfloat_toExponential(JSContext *ctx, JSValueConst this_val,
  51627                                        int argc, JSValueConst *argv)
  51628 {
  51629     JSValue val, ret;
  51630     int64_t f;
  51631     int rnd_mode, radix;
  51632 
  51633     val = js_thisBigFloatValue(ctx, this_val);
  51634     if (JS_IsException(val))
  51635         return val;
  51636     if (JS_ToInt64Sat(ctx, &f, argv[0]))
  51637         goto fail;
  51638     if (!js_bigfloat_is_finite(ctx, val)) {
  51639         ret = JS_ToString(ctx, val);
  51640     } else if (JS_IsUndefined(argv[0])) {
  51641         ret = js_ftoa(ctx, val, 10, 0,
  51642                       BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP);
  51643     } else {
  51644         if (f < 0 || f > BF_PREC_MAX) {
  51645             JS_ThrowRangeError(ctx, "invalid number of digits");
  51646             goto fail;
  51647         }
  51648         rnd_mode = BF_RNDNA;
  51649         radix = 10;
  51650         if (argc > 1) {
  51651             rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
  51652             if (rnd_mode < 0)
  51653                 goto fail;
  51654         }
  51655         if (argc > 2) {
  51656             radix = js_get_radix(ctx, argv[2]);
  51657             if (radix < 0)
  51658                 goto fail;
  51659         }
  51660         ret = js_ftoa(ctx, val, radix, f + 1,
  51661                       rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP);
  51662     }
  51663     JS_FreeValue(ctx, val);
  51664     return ret;
  51665  fail:
  51666     JS_FreeValue(ctx, val);
  51667     return JS_EXCEPTION;
  51668 }
  51669 
  51670 static JSValue js_bigfloat_toPrecision(JSContext *ctx, JSValueConst this_val,
  51671                                      int argc, JSValueConst *argv)
  51672 {
  51673     JSValue val, ret;
  51674     int64_t p;
  51675     int rnd_mode, radix;
  51676 
  51677     val = js_thisBigFloatValue(ctx, this_val);
  51678     if (JS_IsException(val))
  51679         return val;
  51680     if (JS_IsUndefined(argv[0]))
  51681         goto to_string;
  51682     if (JS_ToInt64Sat(ctx, &p, argv[0]))
  51683         goto fail;
  51684     if (!js_bigfloat_is_finite(ctx, val)) {
  51685     to_string:
  51686         ret = JS_ToString(ctx, this_val);
  51687     } else {
  51688         if (p < 1 || p > BF_PREC_MAX) {
  51689             JS_ThrowRangeError(ctx, "invalid number of digits");
  51690             goto fail;
  51691         }
  51692         rnd_mode = BF_RNDNA;
  51693         radix = 10;
  51694         if (argc > 1) {
  51695             rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
  51696             if (rnd_mode < 0)
  51697                 goto fail;
  51698         }
  51699         if (argc > 2) {
  51700             radix = js_get_radix(ctx, argv[2]);
  51701             if (radix < 0)
  51702                 goto fail;
  51703         }
  51704         ret = js_ftoa(ctx, val, radix, p, rnd_mode | BF_FTOA_FORMAT_FIXED);
  51705     }
  51706     JS_FreeValue(ctx, val);
  51707     return ret;
  51708  fail:
  51709     JS_FreeValue(ctx, val);
  51710     return JS_EXCEPTION;
  51711 }
  51712 
  51713 static const JSCFunctionListEntry js_bigfloat_proto_funcs[] = {
  51714     JS_CFUNC_DEF("toString", 0, js_bigfloat_toString ),
  51715     JS_CFUNC_DEF("valueOf", 0, js_bigfloat_valueOf ),
  51716     JS_CFUNC_DEF("toPrecision", 1, js_bigfloat_toPrecision ),
  51717     JS_CFUNC_DEF("toFixed", 1, js_bigfloat_toFixed ),
  51718     JS_CFUNC_DEF("toExponential", 1, js_bigfloat_toExponential ),
  51719 };
  51720 
  51721 static JSValue js_bigfloat_constructor(JSContext *ctx,
  51722                                        JSValueConst new_target,
  51723                                        int argc, JSValueConst *argv)
  51724 {
  51725     JSValue val;
  51726     if (!JS_IsUndefined(new_target))
  51727         return JS_ThrowTypeError(ctx, "not a constructor");
  51728     if (argc == 0) {
  51729         bf_t *r;
  51730         val = JS_NewBigFloat(ctx);
  51731         if (JS_IsException(val))
  51732             return val;
  51733         r = JS_GetBigFloat(val);
  51734         bf_set_zero(r, 0);
  51735     } else {
  51736         val = JS_DupValue(ctx, argv[0]);
  51737     redo:
  51738         switch(JS_VALUE_GET_NORM_TAG(val)) {
  51739         case JS_TAG_BIG_FLOAT:
  51740             break;
  51741         case JS_TAG_FLOAT64:
  51742             {
  51743                 bf_t *r;
  51744                 double d = JS_VALUE_GET_FLOAT64(val);
  51745                 val = JS_NewBigFloat(ctx);
  51746                 if (JS_IsException(val))
  51747                     break;
  51748                 r = JS_GetBigFloat(val);
  51749                 if (bf_set_float64(r, d))
  51750                     goto fail;
  51751             }
  51752             break;
  51753         case JS_TAG_INT:
  51754             {
  51755                 bf_t *r;
  51756                 int32_t v = JS_VALUE_GET_INT(val);
  51757                 val = JS_NewBigFloat(ctx);
  51758                 if (JS_IsException(val))
  51759                     break;
  51760                 r = JS_GetBigFloat(val);
  51761                 if (bf_set_si(r, v))
  51762                     goto fail;
  51763             }
  51764             break;
  51765         case JS_TAG_BIG_INT:
  51766             /* We keep the full precision of the integer */
  51767             {
  51768                 JSBigFloat *p = JS_VALUE_GET_PTR(val);
  51769                 val = JS_MKPTR(JS_TAG_BIG_FLOAT, p);
  51770             }
  51771             break;
  51772         case JS_TAG_BIG_DECIMAL:
  51773             val = JS_ToStringFree(ctx, val);
  51774             if (JS_IsException(val))
  51775                 break;
  51776             goto redo;
  51777         case JS_TAG_STRING:
  51778             {
  51779                 const char *str, *p;
  51780                 size_t len;
  51781                 int err;
  51782 
  51783                 str = JS_ToCStringLen(ctx, &len, val);
  51784                 JS_FreeValue(ctx, val);
  51785                 if (!str)
  51786                     return JS_EXCEPTION;
  51787                 p = str;
  51788                 p += skip_spaces(p);
  51789                 if ((p - str) == len) {
  51790                     bf_t *r;
  51791                     val = JS_NewBigFloat(ctx);
  51792                     if (JS_IsException(val))
  51793                         break;
  51794                     r = JS_GetBigFloat(val);
  51795                     bf_set_zero(r, 0);
  51796                     err = 0;
  51797                 } else {
  51798                     val = js_atof(ctx, p, &p, 0, ATOD_ACCEPT_BIN_OCT |
  51799                                   ATOD_TYPE_BIG_FLOAT |
  51800                                   ATOD_ACCEPT_PREFIX_AFTER_SIGN);
  51801                     if (JS_IsException(val)) {
  51802                         JS_FreeCString(ctx, str);
  51803                         return JS_EXCEPTION;
  51804                     }
  51805                     p += skip_spaces(p);
  51806                     err = ((p - str) != len);
  51807                 }
  51808                 JS_FreeCString(ctx, str);
  51809                 if (err) {
  51810                     JS_FreeValue(ctx, val);
  51811                     return JS_ThrowSyntaxError(ctx, "invalid bigfloat literal");
  51812                 }
  51813             }
  51814             break;
  51815         case JS_TAG_OBJECT:
  51816             val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
  51817             if (JS_IsException(val))
  51818                 break;
  51819             goto redo;
  51820         case JS_TAG_NULL:
  51821         case JS_TAG_UNDEFINED:
  51822         default:
  51823             JS_FreeValue(ctx, val);
  51824             return JS_ThrowTypeError(ctx, "cannot convert to bigfloat");
  51825         }
  51826     }
  51827     return val;
  51828  fail:
  51829     JS_FreeValue(ctx, val);
  51830     return JS_EXCEPTION;
  51831 }
  51832 
  51833 static JSValue js_bigfloat_get_const(JSContext *ctx,
  51834                                      JSValueConst this_val, int magic)
  51835 {
  51836     bf_t *r;
  51837     JSValue val;
  51838     val = JS_NewBigFloat(ctx);
  51839     if (JS_IsException(val))
  51840         return val;
  51841     r = JS_GetBigFloat(val);
  51842     switch(magic) {
  51843     case 0: /* PI */
  51844         bf_const_pi(r, ctx->fp_env.prec, ctx->fp_env.flags);
  51845         break;
  51846     case 1: /* LN2 */
  51847         bf_const_log2(r, ctx->fp_env.prec, ctx->fp_env.flags);
  51848         break;
  51849     case 2: /* MIN_VALUE */
  51850     case 3: /* MAX_VALUE */
  51851         {
  51852             slimb_t e_range, e;
  51853             e_range = (limb_t)1 << (bf_get_exp_bits(ctx->fp_env.flags) - 1);
  51854             bf_set_ui(r, 1);
  51855             if (magic == 2) {
  51856                 e = -e_range + 2;
  51857                 if (ctx->fp_env.flags & BF_FLAG_SUBNORMAL)
  51858                     e -= ctx->fp_env.prec - 1;
  51859                 bf_mul_2exp(r, e, ctx->fp_env.prec, ctx->fp_env.flags);
  51860             } else {
  51861                 bf_mul_2exp(r, ctx->fp_env.prec, ctx->fp_env.prec,
  51862                             ctx->fp_env.flags);
  51863                 bf_add_si(r, r, -1, ctx->fp_env.prec, ctx->fp_env.flags);
  51864                 bf_mul_2exp(r, e_range - ctx->fp_env.prec, ctx->fp_env.prec,
  51865                             ctx->fp_env.flags);
  51866             }
  51867         }
  51868         break;
  51869     case 4: /* EPSILON */
  51870         bf_set_ui(r, 1);
  51871         bf_mul_2exp(r, 1 - ctx->fp_env.prec,
  51872                     ctx->fp_env.prec, ctx->fp_env.flags);
  51873         break;
  51874     default:
  51875         abort();
  51876     }
  51877     return val;
  51878 }
  51879 
  51880 static JSValue js_bigfloat_parseFloat(JSContext *ctx, JSValueConst this_val,
  51881                                       int argc, JSValueConst *argv)
  51882 {
  51883     bf_t *a;
  51884     const char *str;
  51885     JSValue ret;
  51886     int radix;
  51887     JSFloatEnv *fe;
  51888 
  51889     str = JS_ToCString(ctx, argv[0]);
  51890     if (!str)
  51891         return JS_EXCEPTION;
  51892     if (JS_ToInt32(ctx, &radix, argv[1])) {
  51893     fail:
  51894         JS_FreeCString(ctx, str);
  51895         return JS_EXCEPTION;
  51896     }
  51897     if (radix != 0 && (radix < 2 || radix > 36)) {
  51898         JS_ThrowRangeError(ctx, "radix must be between 2 and 36");
  51899         goto fail;
  51900     }
  51901     fe = &ctx->fp_env;
  51902     if (argc > 2) {
  51903         fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV);
  51904         if (!fe)
  51905             goto fail;
  51906     }
  51907     ret = JS_NewBigFloat(ctx);
  51908     if (JS_IsException(ret))
  51909         goto done;
  51910     a = JS_GetBigFloat(ret);
  51911     /* XXX: use js_atof() */
  51912     bf_atof(a, str, NULL, radix, fe->prec, fe->flags);
  51913  done:
  51914     JS_FreeCString(ctx, str);
  51915     return ret;
  51916 }
  51917 
  51918 static JSValue js_bigfloat_isFinite(JSContext *ctx, JSValueConst this_val,
  51919                                     int argc, JSValueConst *argv)
  51920 {
  51921     JSValueConst val = argv[0];
  51922     JSBigFloat *p;
  51923 
  51924     if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT)
  51925         return JS_FALSE;
  51926     p = JS_VALUE_GET_PTR(val);
  51927     return JS_NewBool(ctx, bf_is_finite(&p->num));
  51928 }
  51929 
  51930 static JSValue js_bigfloat_isNaN(JSContext *ctx, JSValueConst this_val,
  51931                                  int argc, JSValueConst *argv)
  51932 {
  51933     JSValueConst val = argv[0];
  51934     JSBigFloat *p;
  51935 
  51936     if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT)
  51937         return JS_FALSE;
  51938     p = JS_VALUE_GET_PTR(val);
  51939     return JS_NewBool(ctx, bf_is_nan(&p->num));
  51940 }
  51941 
  51942 enum {
  51943     MATH_OP_ABS,
  51944     MATH_OP_FLOOR,
  51945     MATH_OP_CEIL,
  51946     MATH_OP_ROUND,
  51947     MATH_OP_TRUNC,
  51948     MATH_OP_SQRT,
  51949     MATH_OP_FPROUND,
  51950     MATH_OP_ACOS,
  51951     MATH_OP_ASIN,
  51952     MATH_OP_ATAN,
  51953     MATH_OP_ATAN2,
  51954     MATH_OP_COS,
  51955     MATH_OP_EXP,
  51956     MATH_OP_LOG,
  51957     MATH_OP_POW,
  51958     MATH_OP_SIN,
  51959     MATH_OP_TAN,
  51960     MATH_OP_FMOD,
  51961     MATH_OP_REM,
  51962     MATH_OP_SIGN,
  51963 
  51964     MATH_OP_ADD,
  51965     MATH_OP_SUB,
  51966     MATH_OP_MUL,
  51967     MATH_OP_DIV,
  51968 };
  51969 
  51970 static JSValue js_bigfloat_fop(JSContext *ctx, JSValueConst this_val,
  51971                            int argc, JSValueConst *argv, int magic)
  51972 {
  51973     bf_t a_s, *a, *r;
  51974     JSFloatEnv *fe;
  51975     int rnd_mode;
  51976     JSValue op1, res;
  51977 
  51978     op1 = JS_ToNumeric(ctx, argv[0]);
  51979     if (JS_IsException(op1))
  51980         return op1;
  51981     a = JS_ToBigFloat(ctx, &a_s, op1);
  51982     if (!a) {
  51983         JS_FreeValue(ctx, op1);
  51984         return JS_EXCEPTION;
  51985     }
  51986     fe = &ctx->fp_env;
  51987     if (argc > 1) {
  51988         fe = JS_GetOpaque2(ctx, argv[1], JS_CLASS_FLOAT_ENV);
  51989         if (!fe)
  51990             goto fail;
  51991     }
  51992     res = JS_NewBigFloat(ctx);
  51993     if (JS_IsException(res)) {
  51994     fail:
  51995         if (a == &a_s)
  51996             bf_delete(a);
  51997         JS_FreeValue(ctx, op1);
  51998         return JS_EXCEPTION;
  51999     }
  52000     r = JS_GetBigFloat(res);
  52001     switch (magic) {
  52002     case MATH_OP_ABS:
  52003         bf_set(r, a);
  52004         r->sign = 0;
  52005         break;
  52006     case MATH_OP_FLOOR:
  52007         rnd_mode = BF_RNDD;
  52008         goto rint;
  52009     case MATH_OP_CEIL:
  52010         rnd_mode = BF_RNDU;
  52011         goto rint;
  52012     case MATH_OP_ROUND:
  52013         rnd_mode = BF_RNDNA;
  52014         goto rint;
  52015     case MATH_OP_TRUNC:
  52016         rnd_mode = BF_RNDZ;
  52017     rint:
  52018         bf_set(r, a);
  52019         fe->status |= bf_rint(r, rnd_mode);
  52020         break;
  52021     case MATH_OP_SQRT:
  52022         fe->status |= bf_sqrt(r, a, fe->prec, fe->flags);
  52023         break;
  52024     case MATH_OP_FPROUND:
  52025         bf_set(r, a);
  52026         fe->status |= bf_round(r, fe->prec, fe->flags);
  52027         break;
  52028     case MATH_OP_ACOS:
  52029         fe->status |= bf_acos(r, a, fe->prec, fe->flags);
  52030         break;
  52031     case MATH_OP_ASIN:
  52032         fe->status |= bf_asin(r, a, fe->prec, fe->flags);
  52033         break;
  52034     case MATH_OP_ATAN:
  52035         fe->status |= bf_atan(r, a, fe->prec, fe->flags);
  52036         break;
  52037     case MATH_OP_COS:
  52038         fe->status |= bf_cos(r, a, fe->prec, fe->flags);
  52039         break;
  52040     case MATH_OP_EXP:
  52041         fe->status |= bf_exp(r, a, fe->prec, fe->flags);
  52042         break;
  52043     case MATH_OP_LOG:
  52044         fe->status |= bf_log(r, a, fe->prec, fe->flags);
  52045         break;
  52046     case MATH_OP_SIN:
  52047         fe->status |= bf_sin(r, a, fe->prec, fe->flags);
  52048         break;
  52049     case MATH_OP_TAN:
  52050         fe->status |= bf_tan(r, a, fe->prec, fe->flags);
  52051         break;
  52052     case MATH_OP_SIGN:
  52053         if (bf_is_nan(a) || bf_is_zero(a)) {
  52054             bf_set(r, a);
  52055         } else {
  52056             bf_set_si(r, 1 - 2 * a->sign);
  52057         }
  52058         break;
  52059     default:
  52060         abort();
  52061     }
  52062     if (a == &a_s)
  52063         bf_delete(a);
  52064     JS_FreeValue(ctx, op1);
  52065     return res;
  52066 }
  52067 
  52068 static JSValue js_bigfloat_fop2(JSContext *ctx, JSValueConst this_val,
  52069                             int argc, JSValueConst *argv, int magic)
  52070 {
  52071     bf_t a_s, *a, b_s, *b, r_s, *r = &r_s;
  52072     JSFloatEnv *fe;
  52073     JSValue op1, op2, res;
  52074 
  52075     op1 = JS_ToNumeric(ctx, argv[0]);
  52076     if (JS_IsException(op1))
  52077         return op1;
  52078     op2 = JS_ToNumeric(ctx, argv[1]);
  52079     if (JS_IsException(op2)) {
  52080         JS_FreeValue(ctx, op1);
  52081         return op2;
  52082     }
  52083     a = JS_ToBigFloat(ctx, &a_s, op1);
  52084     if (!a)
  52085         goto fail1;
  52086     b = JS_ToBigFloat(ctx, &b_s, op2);
  52087     if (!b)
  52088         goto fail2;
  52089     fe = &ctx->fp_env;
  52090     if (argc > 2) {
  52091         fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV);
  52092         if (!fe)
  52093             goto fail;
  52094     }
  52095     res = JS_NewBigFloat(ctx);
  52096     if (JS_IsException(res)) {
  52097     fail:
  52098         if (b == &b_s)
  52099             bf_delete(b);
  52100     fail2:
  52101         if (a == &a_s)
  52102             bf_delete(a);
  52103     fail1:
  52104         JS_FreeValue(ctx, op1);
  52105         JS_FreeValue(ctx, op2);
  52106         return JS_EXCEPTION;
  52107     }
  52108     r = JS_GetBigFloat(res);
  52109     switch (magic) {
  52110     case MATH_OP_ATAN2:
  52111         fe->status |= bf_atan2(r, a, b, fe->prec, fe->flags);
  52112         break;
  52113     case MATH_OP_POW:
  52114         fe->status |= bf_pow(r, a, b, fe->prec, fe->flags | BF_POW_JS_QUIRKS);
  52115         break;
  52116     case MATH_OP_FMOD:
  52117         fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ);
  52118         break;
  52119     case MATH_OP_REM:
  52120         fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDN);
  52121         break;
  52122     case MATH_OP_ADD:
  52123         fe->status |= bf_add(r, a, b, fe->prec, fe->flags);
  52124         break;
  52125     case MATH_OP_SUB:
  52126         fe->status |= bf_sub(r, a, b, fe->prec, fe->flags);
  52127         break;
  52128     case MATH_OP_MUL:
  52129         fe->status |= bf_mul(r, a, b, fe->prec, fe->flags);
  52130         break;
  52131     case MATH_OP_DIV:
  52132         fe->status |= bf_div(r, a, b, fe->prec, fe->flags);
  52133         break;
  52134     default:
  52135         abort();
  52136     }
  52137     if (a == &a_s)
  52138         bf_delete(a);
  52139     if (b == &b_s)
  52140         bf_delete(b);
  52141     JS_FreeValue(ctx, op1);
  52142     JS_FreeValue(ctx, op2);
  52143     return res;
  52144 }
  52145 
  52146 static const JSCFunctionListEntry js_bigfloat_funcs[] = {
  52147     JS_CGETSET_MAGIC_DEF("PI", js_bigfloat_get_const, NULL, 0 ),
  52148     JS_CGETSET_MAGIC_DEF("LN2", js_bigfloat_get_const, NULL, 1 ),
  52149     JS_CGETSET_MAGIC_DEF("MIN_VALUE", js_bigfloat_get_const, NULL, 2 ),
  52150     JS_CGETSET_MAGIC_DEF("MAX_VALUE", js_bigfloat_get_const, NULL, 3 ),
  52151     JS_CGETSET_MAGIC_DEF("EPSILON", js_bigfloat_get_const, NULL, 4 ),
  52152     JS_CFUNC_DEF("parseFloat", 1, js_bigfloat_parseFloat ),
  52153     JS_CFUNC_DEF("isFinite", 1, js_bigfloat_isFinite ),
  52154     JS_CFUNC_DEF("isNaN", 1, js_bigfloat_isNaN ),
  52155     JS_CFUNC_MAGIC_DEF("abs", 1, js_bigfloat_fop, MATH_OP_ABS ),
  52156     JS_CFUNC_MAGIC_DEF("fpRound", 1, js_bigfloat_fop, MATH_OP_FPROUND ),
  52157     JS_CFUNC_MAGIC_DEF("floor", 1, js_bigfloat_fop, MATH_OP_FLOOR ),
  52158     JS_CFUNC_MAGIC_DEF("ceil", 1, js_bigfloat_fop, MATH_OP_CEIL ),
  52159     JS_CFUNC_MAGIC_DEF("round", 1, js_bigfloat_fop, MATH_OP_ROUND ),
  52160     JS_CFUNC_MAGIC_DEF("trunc", 1, js_bigfloat_fop, MATH_OP_TRUNC ),
  52161     JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigfloat_fop, MATH_OP_SQRT ),
  52162     JS_CFUNC_MAGIC_DEF("acos", 1, js_bigfloat_fop, MATH_OP_ACOS ),
  52163     JS_CFUNC_MAGIC_DEF("asin", 1, js_bigfloat_fop, MATH_OP_ASIN ),
  52164     JS_CFUNC_MAGIC_DEF("atan", 1, js_bigfloat_fop, MATH_OP_ATAN ),
  52165     JS_CFUNC_MAGIC_DEF("atan2", 2, js_bigfloat_fop2, MATH_OP_ATAN2 ),
  52166     JS_CFUNC_MAGIC_DEF("cos", 1, js_bigfloat_fop, MATH_OP_COS ),
  52167     JS_CFUNC_MAGIC_DEF("exp", 1, js_bigfloat_fop, MATH_OP_EXP ),
  52168     JS_CFUNC_MAGIC_DEF("log", 1, js_bigfloat_fop, MATH_OP_LOG ),
  52169     JS_CFUNC_MAGIC_DEF("pow", 2, js_bigfloat_fop2, MATH_OP_POW ),
  52170     JS_CFUNC_MAGIC_DEF("sin", 1, js_bigfloat_fop, MATH_OP_SIN ),
  52171     JS_CFUNC_MAGIC_DEF("tan", 1, js_bigfloat_fop, MATH_OP_TAN ),
  52172     JS_CFUNC_MAGIC_DEF("sign", 1, js_bigfloat_fop, MATH_OP_SIGN ),
  52173     JS_CFUNC_MAGIC_DEF("add", 2, js_bigfloat_fop2, MATH_OP_ADD ),
  52174     JS_CFUNC_MAGIC_DEF("sub", 2, js_bigfloat_fop2, MATH_OP_SUB ),
  52175     JS_CFUNC_MAGIC_DEF("mul", 2, js_bigfloat_fop2, MATH_OP_MUL ),
  52176     JS_CFUNC_MAGIC_DEF("div", 2, js_bigfloat_fop2, MATH_OP_DIV ),
  52177     JS_CFUNC_MAGIC_DEF("fmod", 2, js_bigfloat_fop2, MATH_OP_FMOD ),
  52178     JS_CFUNC_MAGIC_DEF("remainder", 2, js_bigfloat_fop2, MATH_OP_REM ),
  52179 };
  52180 
  52181 /* FloatEnv */
  52182 
  52183 static JSValue js_float_env_constructor(JSContext *ctx,
  52184                                         JSValueConst new_target,
  52185                                         int argc, JSValueConst *argv)
  52186 {
  52187     JSValue obj;
  52188     JSFloatEnv *fe;
  52189     int64_t prec;
  52190     int flags, rndmode;
  52191 
  52192     prec = ctx->fp_env.prec;
  52193     flags = ctx->fp_env.flags;
  52194     if (!JS_IsUndefined(argv[0])) {
  52195         if (JS_ToInt64Sat(ctx, &prec, argv[0]))
  52196             return JS_EXCEPTION;
  52197         if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)
  52198             return JS_ThrowRangeError(ctx, "invalid precision");
  52199         flags = BF_RNDN; /* RNDN, max exponent size, no subnormal */
  52200         if (argc > 1 && !JS_IsUndefined(argv[1])) {
  52201             if (JS_ToInt32Sat(ctx, &rndmode, argv[1]))
  52202                 return JS_EXCEPTION;
  52203             if (rndmode < BF_RNDN || rndmode > BF_RNDF)
  52204                 return JS_ThrowRangeError(ctx, "invalid rounding mode");
  52205             flags = rndmode;
  52206         }
  52207     }
  52208 
  52209     obj = JS_NewObjectClass(ctx, JS_CLASS_FLOAT_ENV);
  52210     if (JS_IsException(obj))
  52211         return JS_EXCEPTION;
  52212     fe = js_malloc(ctx, sizeof(*fe));
  52213     if (!fe)
  52214         return JS_EXCEPTION;
  52215     fe->prec = prec;
  52216     fe->flags = flags;
  52217     fe->status = 0;
  52218     JS_SetOpaque(obj, fe);
  52219     return obj;
  52220 }
  52221 
  52222 static void js_float_env_finalizer(JSRuntime *rt, JSValue val)
  52223 {
  52224     JSFloatEnv *fe = JS_GetOpaque(val, JS_CLASS_FLOAT_ENV);
  52225     js_free_rt(rt, fe);
  52226 }
  52227 
  52228 static JSValue js_float_env_get_prec(JSContext *ctx, JSValueConst this_val)
  52229 {
  52230     return JS_NewInt64(ctx, ctx->fp_env.prec);
  52231 }
  52232 
  52233 static JSValue js_float_env_get_expBits(JSContext *ctx, JSValueConst this_val)
  52234 {
  52235     return JS_NewInt32(ctx, bf_get_exp_bits(ctx->fp_env.flags));
  52236 }
  52237 
  52238 static JSValue js_float_env_setPrec(JSContext *ctx,
  52239                                     JSValueConst this_val,
  52240                                     int argc, JSValueConst *argv)
  52241 {
  52242     JSValueConst func;
  52243     int exp_bits, flags, saved_flags;
  52244     JSValue ret;
  52245     limb_t saved_prec;
  52246     int64_t prec;
  52247 
  52248     func = argv[0];
  52249     if (JS_ToInt64Sat(ctx, &prec, argv[1]))
  52250         return JS_EXCEPTION;
  52251     if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)
  52252         return JS_ThrowRangeError(ctx, "invalid precision");
  52253     exp_bits = BF_EXP_BITS_MAX;
  52254 
  52255     if (argc > 2 && !JS_IsUndefined(argv[2])) {
  52256         if (JS_ToInt32Sat(ctx, &exp_bits, argv[2]))
  52257             return JS_EXCEPTION;
  52258         if (exp_bits < BF_EXP_BITS_MIN || exp_bits > BF_EXP_BITS_MAX)
  52259             return JS_ThrowRangeError(ctx, "invalid number of exponent bits");
  52260     }
  52261 
  52262     flags = BF_RNDN | BF_FLAG_SUBNORMAL | bf_set_exp_bits(exp_bits);
  52263 
  52264     saved_prec = ctx->fp_env.prec;
  52265     saved_flags = ctx->fp_env.flags;
  52266 
  52267     ctx->fp_env.prec = prec;
  52268     ctx->fp_env.flags = flags;
  52269 
  52270     ret = JS_Call(ctx, func, JS_UNDEFINED, 0, NULL);
  52271     /* always restore the floating point precision */
  52272     ctx->fp_env.prec = saved_prec;
  52273     ctx->fp_env.flags = saved_flags;
  52274     return ret;
  52275 }
  52276 
  52277 #define FE_PREC      (-1)
  52278 #define FE_EXP       (-2)
  52279 #define FE_RNDMODE   (-3)
  52280 #define FE_SUBNORMAL (-4)
  52281 
  52282 static JSValue js_float_env_proto_get_status(JSContext *ctx, JSValueConst this_val, int magic)
  52283 {
  52284     JSFloatEnv *fe;
  52285     fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);
  52286     if (!fe)
  52287         return JS_EXCEPTION;
  52288     switch(magic) {
  52289     case FE_PREC:
  52290         return JS_NewInt64(ctx, fe->prec);
  52291     case FE_EXP:
  52292         return JS_NewInt32(ctx, bf_get_exp_bits(fe->flags));
  52293     case FE_RNDMODE:
  52294         return JS_NewInt32(ctx, fe->flags & BF_RND_MASK);
  52295     case FE_SUBNORMAL:
  52296         return JS_NewBool(ctx, fe->flags & BF_FLAG_SUBNORMAL);
  52297     default:
  52298         return JS_NewBool(ctx, fe->status & magic);
  52299     }
  52300 }
  52301 
  52302 static JSValue js_float_env_proto_set_status(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic)
  52303 {
  52304     JSFloatEnv *fe;
  52305     int b;
  52306     int64_t prec;
  52307 
  52308     fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);
  52309     if (!fe)
  52310         return JS_EXCEPTION;
  52311     switch(magic) {
  52312     case FE_PREC:
  52313         if (JS_ToInt64Sat(ctx, &prec, val))
  52314             return JS_EXCEPTION;
  52315         if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)
  52316             return JS_ThrowRangeError(ctx, "invalid precision");
  52317         fe->prec = prec;
  52318         break;
  52319     case FE_EXP:
  52320         if (JS_ToInt32Sat(ctx, &b, val))
  52321             return JS_EXCEPTION;
  52322         if (b < BF_EXP_BITS_MIN || b > BF_EXP_BITS_MAX)
  52323             return JS_ThrowRangeError(ctx, "invalid number of exponent bits");
  52324         fe->flags = (fe->flags & ~(BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)) |
  52325             bf_set_exp_bits(b);
  52326         break;
  52327     case FE_RNDMODE:
  52328         b = bigfloat_get_rnd_mode(ctx, val);
  52329         if (b < 0)
  52330             return JS_EXCEPTION;
  52331         fe->flags = (fe->flags & ~BF_RND_MASK) | b;
  52332         break;
  52333     case FE_SUBNORMAL:
  52334         b = JS_ToBool(ctx, val);
  52335         fe->flags = (fe->flags & ~BF_FLAG_SUBNORMAL) | (b ? BF_FLAG_SUBNORMAL: 0);
  52336         break;
  52337     default:
  52338         b = JS_ToBool(ctx, val);
  52339         fe->status = (fe->status & ~magic) & ((-b) & magic);
  52340         break;
  52341     }
  52342     return JS_UNDEFINED;
  52343 }
  52344 
  52345 static JSValue js_float_env_clearStatus(JSContext *ctx,
  52346                                         JSValueConst this_val,
  52347                                         int argc, JSValueConst *argv)
  52348 {
  52349     JSFloatEnv *fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);
  52350     if (!fe)
  52351         return JS_EXCEPTION;
  52352     fe->status = 0;
  52353     return JS_UNDEFINED;
  52354 }
  52355 
  52356 static const JSCFunctionListEntry js_float_env_funcs[] = {
  52357     JS_CGETSET_DEF("prec", js_float_env_get_prec, NULL ),
  52358     JS_CGETSET_DEF("expBits", js_float_env_get_expBits, NULL ),
  52359     JS_CFUNC_DEF("setPrec", 2, js_float_env_setPrec ),
  52360     JS_PROP_INT32_DEF("RNDN", BF_RNDN, 0 ),
  52361     JS_PROP_INT32_DEF("RNDZ", BF_RNDZ, 0 ),
  52362     JS_PROP_INT32_DEF("RNDU", BF_RNDU, 0 ),
  52363     JS_PROP_INT32_DEF("RNDD", BF_RNDD, 0 ),
  52364     JS_PROP_INT32_DEF("RNDNA", BF_RNDNA, 0 ),
  52365     JS_PROP_INT32_DEF("RNDA", BF_RNDA, 0 ),
  52366     JS_PROP_INT32_DEF("RNDF", BF_RNDF, 0 ),
  52367     JS_PROP_INT32_DEF("precMin", BF_PREC_MIN, 0 ),
  52368     JS_PROP_INT64_DEF("precMax", BF_PREC_MAX, 0 ),
  52369     JS_PROP_INT32_DEF("expBitsMin", BF_EXP_BITS_MIN, 0 ),
  52370     JS_PROP_INT32_DEF("expBitsMax", BF_EXP_BITS_MAX, 0 ),
  52371 };
  52372 
  52373 static const JSCFunctionListEntry js_float_env_proto_funcs[] = {
  52374     JS_CGETSET_MAGIC_DEF("prec", js_float_env_proto_get_status,
  52375                          js_float_env_proto_set_status, FE_PREC ),
  52376     JS_CGETSET_MAGIC_DEF("expBits", js_float_env_proto_get_status,
  52377                          js_float_env_proto_set_status, FE_EXP ),
  52378     JS_CGETSET_MAGIC_DEF("rndMode", js_float_env_proto_get_status,
  52379                          js_float_env_proto_set_status, FE_RNDMODE ),
  52380     JS_CGETSET_MAGIC_DEF("subnormal", js_float_env_proto_get_status,
  52381                          js_float_env_proto_set_status, FE_SUBNORMAL ),
  52382     JS_CGETSET_MAGIC_DEF("invalidOperation", js_float_env_proto_get_status,
  52383                          js_float_env_proto_set_status, BF_ST_INVALID_OP ),
  52384     JS_CGETSET_MAGIC_DEF("divideByZero", js_float_env_proto_get_status,
  52385                          js_float_env_proto_set_status, BF_ST_DIVIDE_ZERO ),
  52386     JS_CGETSET_MAGIC_DEF("overflow", js_float_env_proto_get_status,
  52387                          js_float_env_proto_set_status, BF_ST_OVERFLOW ),
  52388     JS_CGETSET_MAGIC_DEF("underflow", js_float_env_proto_get_status,
  52389                          js_float_env_proto_set_status, BF_ST_UNDERFLOW ),
  52390     JS_CGETSET_MAGIC_DEF("inexact", js_float_env_proto_get_status,
  52391                          js_float_env_proto_set_status, BF_ST_INEXACT ),
  52392     JS_CFUNC_DEF("clearStatus", 0, js_float_env_clearStatus ),
  52393 };
  52394 
  52395 void JS_AddIntrinsicBigFloat(JSContext *ctx)
  52396 {
  52397     JSRuntime *rt = ctx->rt;
  52398     JSValueConst obj1;
  52399 
  52400     rt->bigfloat_ops.to_string = js_bigfloat_to_string;
  52401     rt->bigfloat_ops.from_string = js_string_to_bigfloat;
  52402     rt->bigfloat_ops.unary_arith = js_unary_arith_bigfloat;
  52403     rt->bigfloat_ops.binary_arith = js_binary_arith_bigfloat;
  52404     rt->bigfloat_ops.compare = js_compare_bigfloat;
  52405     rt->bigfloat_ops.mul_pow10_to_float64 = js_mul_pow10_to_float64;
  52406     rt->bigfloat_ops.mul_pow10 = js_mul_pow10;
  52407 
  52408     ctx->class_proto[JS_CLASS_BIG_FLOAT] = JS_NewObject(ctx);
  52409     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT],
  52410                                js_bigfloat_proto_funcs,
  52411                                countof(js_bigfloat_proto_funcs));
  52412     obj1 = JS_NewGlobalCConstructor(ctx, "BigFloat", js_bigfloat_constructor, 1,
  52413                                     ctx->class_proto[JS_CLASS_BIG_FLOAT]);
  52414     JS_SetPropertyFunctionList(ctx, obj1, js_bigfloat_funcs,
  52415                                countof(js_bigfloat_funcs));
  52416 
  52417     ctx->class_proto[JS_CLASS_FLOAT_ENV] = JS_NewObject(ctx);
  52418     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_FLOAT_ENV],
  52419                                js_float_env_proto_funcs,
  52420                                countof(js_float_env_proto_funcs));
  52421     obj1 = JS_NewGlobalCConstructorOnly(ctx, "BigFloatEnv",
  52422                                         js_float_env_constructor, 1,
  52423                                         ctx->class_proto[JS_CLASS_FLOAT_ENV]);
  52424     JS_SetPropertyFunctionList(ctx, obj1, js_float_env_funcs,
  52425                                countof(js_float_env_funcs));
  52426 }
  52427 
  52428 /* BigDecimal */
  52429 
  52430 static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,
  52431                                    BOOL allow_null_or_undefined)
  52432 {
  52433  redo:
  52434     switch(JS_VALUE_GET_NORM_TAG(val)) {
  52435     case JS_TAG_BIG_DECIMAL:
  52436         break;
  52437     case JS_TAG_NULL:
  52438         if (!allow_null_or_undefined)
  52439             goto fail;
  52440         /* fall thru */
  52441     case JS_TAG_BOOL:
  52442     case JS_TAG_INT:
  52443         {
  52444             bfdec_t *r;
  52445             int32_t v = JS_VALUE_GET_INT(val);
  52446 
  52447             val = JS_NewBigDecimal(ctx);
  52448             if (JS_IsException(val))
  52449                 break;
  52450             r = JS_GetBigDecimal(val);
  52451             if (bfdec_set_si(r, v)) {
  52452                 JS_FreeValue(ctx, val);
  52453                 val = JS_EXCEPTION;
  52454                 break;
  52455             }
  52456         }
  52457         break;
  52458     case JS_TAG_FLOAT64:
  52459     case JS_TAG_BIG_INT:
  52460     case JS_TAG_BIG_FLOAT:
  52461         val = JS_ToStringFree(ctx, val);
  52462         if (JS_IsException(val))
  52463             break;
  52464         goto redo;
  52465     case JS_TAG_STRING:
  52466         {
  52467             const char *str, *p;
  52468             size_t len;
  52469             int err;
  52470 
  52471             str = JS_ToCStringLen(ctx, &len, val);
  52472             JS_FreeValue(ctx, val);
  52473             if (!str)
  52474                 return JS_EXCEPTION;
  52475             p = str;
  52476             p += skip_spaces(p);
  52477             if ((p - str) == len) {
  52478                 bfdec_t *r;
  52479                 val = JS_NewBigDecimal(ctx);
  52480                 if (JS_IsException(val))
  52481                     break;
  52482                 r = JS_GetBigDecimal(val);
  52483                 bfdec_set_zero(r, 0);
  52484                 err = 0;
  52485             } else {
  52486                 val = js_atof(ctx, p, &p, 0, ATOD_TYPE_BIG_DECIMAL);
  52487                 if (JS_IsException(val)) {
  52488                     JS_FreeCString(ctx, str);
  52489                     return JS_EXCEPTION;
  52490                 }
  52491                 p += skip_spaces(p);
  52492                 err = ((p - str) != len);
  52493             }
  52494             JS_FreeCString(ctx, str);
  52495             if (err) {
  52496                 JS_FreeValue(ctx, val);
  52497                 return JS_ThrowSyntaxError(ctx, "invalid bigdecimal literal");
  52498             }
  52499         }
  52500         break;
  52501     case JS_TAG_OBJECT:
  52502         val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
  52503         if (JS_IsException(val))
  52504             break;
  52505         goto redo;
  52506     case JS_TAG_UNDEFINED:
  52507         {
  52508             bfdec_t *r;
  52509             if (!allow_null_or_undefined)
  52510                 goto fail;
  52511             val = JS_NewBigDecimal(ctx);
  52512             if (JS_IsException(val))
  52513                 break;
  52514             r = JS_GetBigDecimal(val);
  52515             bfdec_set_nan(r);
  52516         }
  52517         break;
  52518     default:
  52519     fail:
  52520         JS_FreeValue(ctx, val);
  52521         return JS_ThrowTypeError(ctx, "cannot convert to bigdecimal");
  52522     }
  52523     return val;
  52524 }
  52525 
  52526 static JSValue js_bigdecimal_constructor(JSContext *ctx,
  52527                                          JSValueConst new_target,
  52528                                          int argc, JSValueConst *argv)
  52529 {
  52530     JSValue val;
  52531     if (!JS_IsUndefined(new_target))
  52532         return JS_ThrowTypeError(ctx, "not a constructor");
  52533     if (argc == 0) {
  52534         bfdec_t *r;
  52535         val = JS_NewBigDecimal(ctx);
  52536         if (JS_IsException(val))
  52537             return val;
  52538         r = JS_GetBigDecimal(val);
  52539         bfdec_set_zero(r, 0);
  52540     } else {
  52541         val = JS_ToBigDecimalFree(ctx, JS_DupValue(ctx, argv[0]), FALSE);
  52542     }
  52543     return val;
  52544 }
  52545 
  52546 static JSValue js_thisBigDecimalValue(JSContext *ctx, JSValueConst this_val)
  52547 {
  52548     if (JS_IsBigDecimal(this_val))
  52549         return JS_DupValue(ctx, this_val);
  52550 
  52551     if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
  52552         JSObject *p = JS_VALUE_GET_OBJ(this_val);
  52553         if (p->class_id == JS_CLASS_BIG_DECIMAL) {
  52554             if (JS_IsBigDecimal(p->u.object_data))
  52555                 return JS_DupValue(ctx, p->u.object_data);
  52556         }
  52557     }
  52558     return JS_ThrowTypeError(ctx, "not a bigdecimal");
  52559 }
  52560 
  52561 static JSValue js_bigdecimal_toString(JSContext *ctx, JSValueConst this_val,
  52562                                       int argc, JSValueConst *argv)
  52563 {
  52564     JSValue val;
  52565 
  52566     val = js_thisBigDecimalValue(ctx, this_val);
  52567     if (JS_IsException(val))
  52568         return val;
  52569     return JS_ToStringFree(ctx, val);
  52570 }
  52571 
  52572 static JSValue js_bigdecimal_valueOf(JSContext *ctx, JSValueConst this_val,
  52573                                    int argc, JSValueConst *argv)
  52574 {
  52575     return js_thisBigDecimalValue(ctx, this_val);
  52576 }
  52577 
  52578 static int js_bigdecimal_get_rnd_mode(JSContext *ctx, JSValueConst obj)
  52579 {
  52580     const char *str;
  52581     size_t size;
  52582     int rnd_mode;
  52583 
  52584     str = JS_ToCStringLen(ctx, &size, obj);
  52585     if (!str)
  52586         return -1;
  52587     if (strlen(str) != size)
  52588         goto invalid_rounding_mode;
  52589     if (!strcmp(str, "floor")) {
  52590         rnd_mode = BF_RNDD;
  52591     } else if (!strcmp(str, "ceiling")) {
  52592         rnd_mode = BF_RNDU;
  52593     } else if (!strcmp(str, "down")) {
  52594         rnd_mode = BF_RNDZ;
  52595     } else if (!strcmp(str, "up")) {
  52596         rnd_mode = BF_RNDA;
  52597     } else if (!strcmp(str, "half-even")) {
  52598         rnd_mode = BF_RNDN;
  52599     } else if (!strcmp(str, "half-up")) {
  52600         rnd_mode = BF_RNDNA;
  52601     } else {
  52602     invalid_rounding_mode:
  52603         JS_FreeCString(ctx, str);
  52604         JS_ThrowTypeError(ctx, "invalid rounding mode");
  52605         return -1;
  52606     }
  52607     JS_FreeCString(ctx, str);
  52608     return rnd_mode;
  52609 }
  52610 
  52611 typedef struct {
  52612     int64_t prec;
  52613     bf_flags_t flags;
  52614 } BigDecimalEnv;
  52615 
  52616 static int js_bigdecimal_get_env(JSContext *ctx, BigDecimalEnv *fe,
  52617                                  JSValueConst obj)
  52618 {
  52619     JSValue prop;
  52620     int64_t val;
  52621     BOOL has_prec;
  52622     int rnd_mode;
  52623 
  52624     if (!JS_IsObject(obj)) {
  52625         JS_ThrowTypeErrorNotAnObject(ctx);
  52626         return -1;
  52627     }
  52628     prop = JS_GetProperty(ctx, obj, JS_ATOM_roundingMode);
  52629     if (JS_IsException(prop))
  52630         return -1;
  52631     rnd_mode = js_bigdecimal_get_rnd_mode(ctx, prop);
  52632     JS_FreeValue(ctx, prop);
  52633     if (rnd_mode < 0)
  52634         return -1;
  52635     fe->flags = rnd_mode;
  52636 
  52637     prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumSignificantDigits);
  52638     if (JS_IsException(prop))
  52639         return -1;
  52640     has_prec = FALSE;
  52641     if (!JS_IsUndefined(prop)) {
  52642         if (JS_ToInt64SatFree(ctx, &val, prop))
  52643             return -1;
  52644         if (val < 1 || val > BF_PREC_MAX)
  52645             goto invalid_precision;
  52646         fe->prec = val;
  52647         has_prec = TRUE;
  52648     }
  52649 
  52650     prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumFractionDigits);
  52651     if (JS_IsException(prop))
  52652         return -1;
  52653     if (!JS_IsUndefined(prop)) {
  52654         if (has_prec) {
  52655             JS_FreeValue(ctx, prop);
  52656             JS_ThrowTypeError(ctx, "cannot provide both maximumSignificantDigits and maximumFractionDigits");
  52657             return -1;
  52658         }
  52659         if (JS_ToInt64SatFree(ctx, &val, prop))
  52660             return -1;
  52661         if (val < 0 || val > BF_PREC_MAX) {
  52662         invalid_precision:
  52663             JS_ThrowTypeError(ctx, "invalid precision");
  52664             return -1;
  52665         }
  52666         fe->prec = val;
  52667         fe->flags |= BF_FLAG_RADPNT_PREC;
  52668         has_prec = TRUE;
  52669     }
  52670     if (!has_prec) {
  52671         JS_ThrowTypeError(ctx, "precision must be present");
  52672         return -1;
  52673     }
  52674     return 0;
  52675 }
  52676 
  52677 
  52678 static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val,
  52679                                  int argc, JSValueConst *argv, int magic)
  52680 {
  52681     bfdec_t *a, *b, r_s, *r = &r_s;
  52682     JSValue op1, op2, res;
  52683     BigDecimalEnv fe_s, *fe = &fe_s;
  52684     int op_count, ret;
  52685 
  52686     if (magic == MATH_OP_SQRT ||
  52687         magic == MATH_OP_ROUND)
  52688         op_count = 1;
  52689     else
  52690         op_count = 2;
  52691 
  52692     op1 = JS_ToNumeric(ctx, argv[0]);
  52693     if (JS_IsException(op1))
  52694         return op1;
  52695     a = JS_ToBigDecimal(ctx, op1);
  52696     if (!a) {
  52697         JS_FreeValue(ctx, op1);
  52698         return JS_EXCEPTION;
  52699     }
  52700     if (op_count >= 2) {
  52701         op2 = JS_ToNumeric(ctx, argv[1]);
  52702         if (JS_IsException(op2)) {
  52703             JS_FreeValue(ctx, op1);
  52704             return op2;
  52705         }
  52706         b = JS_ToBigDecimal(ctx, op2);
  52707         if (!b)
  52708             goto fail;
  52709     } else {
  52710         op2 = JS_UNDEFINED;
  52711         b = NULL;
  52712     }
  52713     fe->flags = BF_RNDZ;
  52714     fe->prec = BF_PREC_INF;
  52715     if (op_count < argc) {
  52716         if (js_bigdecimal_get_env(ctx, fe, argv[op_count]))
  52717             goto fail;
  52718     }
  52719 
  52720     res = JS_NewBigDecimal(ctx);
  52721     if (JS_IsException(res)) {
  52722     fail:
  52723         JS_FreeValue(ctx, op1);
  52724         JS_FreeValue(ctx, op2);
  52725         return JS_EXCEPTION;
  52726     }
  52727     r = JS_GetBigDecimal(res);
  52728     switch (magic) {
  52729     case MATH_OP_ADD:
  52730         ret = bfdec_add(r, a, b, fe->prec, fe->flags);
  52731         break;
  52732     case MATH_OP_SUB:
  52733         ret = bfdec_sub(r, a, b, fe->prec, fe->flags);
  52734         break;
  52735     case MATH_OP_MUL:
  52736         ret = bfdec_mul(r, a, b, fe->prec, fe->flags);
  52737         break;
  52738     case MATH_OP_DIV:
  52739         ret = bfdec_div(r, a, b, fe->prec, fe->flags);
  52740         break;
  52741     case MATH_OP_FMOD:
  52742         ret = bfdec_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ);
  52743         break;
  52744     case MATH_OP_SQRT:
  52745         ret = bfdec_sqrt(r, a, fe->prec, fe->flags);
  52746         break;
  52747     case MATH_OP_ROUND:
  52748         ret = bfdec_set(r, a);
  52749         if (!(ret & BF_ST_MEM_ERROR))
  52750             ret = bfdec_round(r, fe->prec, fe->flags);
  52751         break;
  52752     default:
  52753         abort();
  52754     }
  52755     JS_FreeValue(ctx, op1);
  52756     JS_FreeValue(ctx, op2);
  52757     ret &= BF_ST_MEM_ERROR | BF_ST_DIVIDE_ZERO | BF_ST_INVALID_OP |
  52758         BF_ST_OVERFLOW;
  52759     if (ret != 0) {
  52760         JS_FreeValue(ctx, res);
  52761         return throw_bf_exception(ctx, ret);
  52762     } else {
  52763         return res;
  52764     }
  52765 }
  52766 
  52767 static JSValue js_bigdecimal_toFixed(JSContext *ctx, JSValueConst this_val,
  52768                                  int argc, JSValueConst *argv)
  52769 {
  52770     JSValue val, ret;
  52771     int64_t f;
  52772     int rnd_mode;
  52773 
  52774     val = js_thisBigDecimalValue(ctx, this_val);
  52775     if (JS_IsException(val))
  52776         return val;
  52777     if (JS_ToInt64Sat(ctx, &f, argv[0]))
  52778         goto fail;
  52779     if (f < 0 || f > BF_PREC_MAX) {
  52780         JS_ThrowRangeError(ctx, "invalid number of digits");
  52781         goto fail;
  52782     }
  52783     rnd_mode = BF_RNDNA;
  52784     if (argc > 1) {
  52785         rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
  52786         if (rnd_mode < 0)
  52787             goto fail;
  52788     }
  52789     ret = js_bigdecimal_to_string1(ctx, val, f, rnd_mode | BF_FTOA_FORMAT_FRAC);
  52790     JS_FreeValue(ctx, val);
  52791     return ret;
  52792  fail:
  52793     JS_FreeValue(ctx, val);
  52794     return JS_EXCEPTION;
  52795 }
  52796 
  52797 static JSValue js_bigdecimal_toExponential(JSContext *ctx, JSValueConst this_val,
  52798                                        int argc, JSValueConst *argv)
  52799 {
  52800     JSValue val, ret;
  52801     int64_t f;
  52802     int rnd_mode;
  52803 
  52804     val = js_thisBigDecimalValue(ctx, this_val);
  52805     if (JS_IsException(val))
  52806         return val;
  52807     if (JS_ToInt64Sat(ctx, &f, argv[0]))
  52808         goto fail;
  52809     if (JS_IsUndefined(argv[0])) {
  52810         ret = js_bigdecimal_to_string1(ctx, val, 0,
  52811                   BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP);
  52812     } else {
  52813         if (f < 0 || f > BF_PREC_MAX) {
  52814             JS_ThrowRangeError(ctx, "invalid number of digits");
  52815             goto fail;
  52816         }
  52817         rnd_mode = BF_RNDNA;
  52818         if (argc > 1) {
  52819             rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
  52820             if (rnd_mode < 0)
  52821                 goto fail;
  52822         }
  52823         ret = js_bigdecimal_to_string1(ctx, val, f + 1,
  52824                       rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP);
  52825     }
  52826     JS_FreeValue(ctx, val);
  52827     return ret;
  52828  fail:
  52829     JS_FreeValue(ctx, val);
  52830     return JS_EXCEPTION;
  52831 }
  52832 
  52833 static JSValue js_bigdecimal_toPrecision(JSContext *ctx, JSValueConst this_val,
  52834                                      int argc, JSValueConst *argv)
  52835 {
  52836     JSValue val, ret;
  52837     int64_t p;
  52838     int rnd_mode;
  52839 
  52840     val = js_thisBigDecimalValue(ctx, this_val);
  52841     if (JS_IsException(val))
  52842         return val;
  52843     if (JS_IsUndefined(argv[0])) {
  52844         return JS_ToStringFree(ctx, val);
  52845     }
  52846     if (JS_ToInt64Sat(ctx, &p, argv[0]))
  52847         goto fail;
  52848     if (p < 1 || p > BF_PREC_MAX) {
  52849         JS_ThrowRangeError(ctx, "invalid number of digits");
  52850         goto fail;
  52851     }
  52852     rnd_mode = BF_RNDNA;
  52853     if (argc > 1) {
  52854         rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
  52855         if (rnd_mode < 0)
  52856             goto fail;
  52857     }
  52858     ret = js_bigdecimal_to_string1(ctx, val, p,
  52859                                    rnd_mode | BF_FTOA_FORMAT_FIXED);
  52860     JS_FreeValue(ctx, val);
  52861     return ret;
  52862  fail:
  52863     JS_FreeValue(ctx, val);
  52864     return JS_EXCEPTION;
  52865 }
  52866 
  52867 static const JSCFunctionListEntry js_bigdecimal_proto_funcs[] = {
  52868     JS_CFUNC_DEF("toString", 0, js_bigdecimal_toString ),
  52869     JS_CFUNC_DEF("valueOf", 0, js_bigdecimal_valueOf ),
  52870     JS_CFUNC_DEF("toPrecision", 1, js_bigdecimal_toPrecision ),
  52871     JS_CFUNC_DEF("toFixed", 1, js_bigdecimal_toFixed ),
  52872     JS_CFUNC_DEF("toExponential", 1, js_bigdecimal_toExponential ),
  52873 };
  52874 
  52875 static const JSCFunctionListEntry js_bigdecimal_funcs[] = {
  52876     JS_CFUNC_MAGIC_DEF("add", 2, js_bigdecimal_fop, MATH_OP_ADD ),
  52877     JS_CFUNC_MAGIC_DEF("sub", 2, js_bigdecimal_fop, MATH_OP_SUB ),
  52878     JS_CFUNC_MAGIC_DEF("mul", 2, js_bigdecimal_fop, MATH_OP_MUL ),
  52879     JS_CFUNC_MAGIC_DEF("div", 2, js_bigdecimal_fop, MATH_OP_DIV ),
  52880     JS_CFUNC_MAGIC_DEF("mod", 2, js_bigdecimal_fop, MATH_OP_FMOD ),
  52881     JS_CFUNC_MAGIC_DEF("round", 1, js_bigdecimal_fop, MATH_OP_ROUND ),
  52882     JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigdecimal_fop, MATH_OP_SQRT ),
  52883 };
  52884 
  52885 void JS_AddIntrinsicBigDecimal(JSContext *ctx)
  52886 {
  52887     JSRuntime *rt = ctx->rt;
  52888     JSValueConst obj1;
  52889 
  52890     rt->bigdecimal_ops.to_string = js_bigdecimal_to_string;
  52891     rt->bigdecimal_ops.from_string = js_string_to_bigdecimal;
  52892     rt->bigdecimal_ops.unary_arith = js_unary_arith_bigdecimal;
  52893     rt->bigdecimal_ops.binary_arith = js_binary_arith_bigdecimal;
  52894     rt->bigdecimal_ops.compare = js_compare_bigdecimal;
  52895 
  52896     ctx->class_proto[JS_CLASS_BIG_DECIMAL] = JS_NewObject(ctx);
  52897     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL],
  52898                                js_bigdecimal_proto_funcs,
  52899                                countof(js_bigdecimal_proto_funcs));
  52900     obj1 = JS_NewGlobalCConstructor(ctx, "BigDecimal",
  52901                                     js_bigdecimal_constructor, 1,
  52902                                     ctx->class_proto[JS_CLASS_BIG_DECIMAL]);
  52903     JS_SetPropertyFunctionList(ctx, obj1, js_bigdecimal_funcs,
  52904                                countof(js_bigdecimal_funcs));
  52905 }
  52906 
  52907 void JS_EnableBignumExt(JSContext *ctx, BOOL enable)
  52908 {
  52909     ctx->bignum_ext = enable;
  52910 }
  52911 
  52912 #endif /* CONFIG_BIGNUM */
  52913 
  52914 static const char * const native_error_name[JS_NATIVE_ERROR_COUNT] = {
  52915     "EvalError", "RangeError", "ReferenceError",
  52916     "SyntaxError", "TypeError", "URIError",
  52917     "InternalError", "AggregateError",
  52918 };
  52919 
  52920 /* Minimum amount of objects to be able to compile code and display
  52921    error messages. No JSAtom should be allocated by this function. */
  52922 static void JS_AddIntrinsicBasicObjects(JSContext *ctx)
  52923 {
  52924     JSValue proto;
  52925     int i;
  52926 
  52927     ctx->class_proto[JS_CLASS_OBJECT] = JS_NewObjectProto(ctx, JS_NULL);
  52928     ctx->function_proto = JS_NewCFunction3(ctx, js_function_proto, "", 0,
  52929                                            JS_CFUNC_generic, 0,
  52930                                            ctx->class_proto[JS_CLASS_OBJECT]);
  52931     ctx->class_proto[JS_CLASS_BYTECODE_FUNCTION] = JS_DupValue(ctx, ctx->function_proto);
  52932     ctx->class_proto[JS_CLASS_ERROR] = JS_NewObject(ctx);
  52933 #if 0
  52934     /* these are auto-initialized from js_error_proto_funcs,
  52935        but delaying might be a problem */
  52936     JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ERROR], JS_ATOM_name,
  52937                            JS_AtomToString(ctx, JS_ATOM_Error),
  52938                            JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
  52939     JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ERROR], JS_ATOM_message,
  52940                            JS_AtomToString(ctx, JS_ATOM_empty_string),
  52941                            JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
  52942 #endif
  52943     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ERROR],
  52944                                js_error_proto_funcs,
  52945                                countof(js_error_proto_funcs));
  52946 
  52947     for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
  52948         proto = JS_NewObjectProto(ctx, ctx->class_proto[JS_CLASS_ERROR]);
  52949         JS_DefinePropertyValue(ctx, proto, JS_ATOM_name,
  52950                                JS_NewAtomString(ctx, native_error_name[i]),
  52951                                JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
  52952         JS_DefinePropertyValue(ctx, proto, JS_ATOM_message,
  52953                                JS_AtomToString(ctx, JS_ATOM_empty_string),
  52954                                JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
  52955         ctx->native_error_proto[i] = proto;
  52956     }
  52957 
  52958     /* the array prototype is an array */
  52959     ctx->class_proto[JS_CLASS_ARRAY] =
  52960         JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
  52961                                JS_CLASS_ARRAY);
  52962 
  52963     ctx->array_shape = js_new_shape2(ctx, get_proto_obj(ctx->class_proto[JS_CLASS_ARRAY]),
  52964                                      JS_PROP_INITIAL_HASH_SIZE, 1);
  52965     add_shape_property(ctx, &ctx->array_shape, NULL,
  52966                        JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_LENGTH);
  52967 
  52968     /* XXX: could test it on first context creation to ensure that no
  52969        new atoms are created in JS_AddIntrinsicBasicObjects(). It is
  52970        necessary to avoid useless renumbering of atoms after
  52971        JS_EvalBinary() if it is done just after
  52972        JS_AddIntrinsicBasicObjects(). */
  52973     //    assert(ctx->rt->atom_count == JS_ATOM_END);
  52974 }
  52975 
  52976 void JS_AddIntrinsicBaseObjects(JSContext *ctx)
  52977 {
  52978     int i;
  52979     JSValueConst obj, number_obj;
  52980     JSValue obj1;
  52981 
  52982     ctx->throw_type_error = JS_NewCFunction(ctx, js_throw_type_error, NULL, 0);
  52983 
  52984     /* add caller and arguments properties to throw a TypeError */
  52985     obj1 = JS_NewCFunction(ctx, js_function_proto_caller, NULL, 0);
  52986     JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_caller, JS_UNDEFINED,
  52987                       obj1, ctx->throw_type_error,
  52988                       JS_PROP_HAS_GET | JS_PROP_HAS_SET |
  52989                       JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE);
  52990     JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_arguments, JS_UNDEFINED,
  52991                       obj1, ctx->throw_type_error,
  52992                       JS_PROP_HAS_GET | JS_PROP_HAS_SET |
  52993                       JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE);
  52994     JS_FreeValue(ctx, obj1);
  52995     JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, (JSValueConst *)&ctx->throw_type_error, 1));
  52996 
  52997     ctx->global_obj = JS_NewObject(ctx);
  52998     ctx->global_var_obj = JS_NewObjectProto(ctx, JS_NULL);
  52999 
  53000     /* Object */
  53001     obj = JS_NewGlobalCConstructor(ctx, "Object", js_object_constructor, 1,
  53002                                    ctx->class_proto[JS_CLASS_OBJECT]);
  53003     JS_SetPropertyFunctionList(ctx, obj, js_object_funcs, countof(js_object_funcs));
  53004     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_OBJECT],
  53005                                js_object_proto_funcs, countof(js_object_proto_funcs));
  53006 
  53007     /* Function */
  53008     JS_SetPropertyFunctionList(ctx, ctx->function_proto, js_function_proto_funcs, countof(js_function_proto_funcs));
  53009     ctx->function_ctor = JS_NewCFunctionMagic(ctx, js_function_constructor,
  53010                                               "Function", 1, JS_CFUNC_constructor_or_func_magic,
  53011                                               JS_FUNC_NORMAL);
  53012     JS_NewGlobalCConstructor2(ctx, JS_DupValue(ctx, ctx->function_ctor), "Function",
  53013                               ctx->function_proto);
  53014 
  53015     /* Error */
  53016     obj1 = JS_NewCFunctionMagic(ctx, js_error_constructor,
  53017                                 "Error", 1, JS_CFUNC_constructor_or_func_magic, -1);
  53018     JS_NewGlobalCConstructor2(ctx, obj1,
  53019                               "Error", ctx->class_proto[JS_CLASS_ERROR]);
  53020 
  53021     /* Used to squelch a -Wcast-function-type warning. */
  53022     JSCFunctionType ft = { .generic_magic = js_error_constructor };
  53023     for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
  53024         JSValue func_obj;
  53025         int n_args;
  53026         n_args = 1 + (i == JS_AGGREGATE_ERROR);
  53027         func_obj = JS_NewCFunction3(ctx, ft.generic,
  53028                                     native_error_name[i], n_args,
  53029                                     JS_CFUNC_constructor_or_func_magic, i, obj1);
  53030         JS_NewGlobalCConstructor2(ctx, func_obj, native_error_name[i],
  53031                                   ctx->native_error_proto[i]);
  53032     }
  53033 
  53034     /* Iterator prototype */
  53035     ctx->iterator_proto = JS_NewObject(ctx);
  53036     JS_SetPropertyFunctionList(ctx, ctx->iterator_proto,
  53037                                js_iterator_proto_funcs,
  53038                                countof(js_iterator_proto_funcs));
  53039 
  53040     /* Array */
  53041     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY],
  53042                                js_array_proto_funcs,
  53043                                countof(js_array_proto_funcs));
  53044 
  53045     obj = JS_NewGlobalCConstructor(ctx, "Array", js_array_constructor, 1,
  53046                                    ctx->class_proto[JS_CLASS_ARRAY]);
  53047     ctx->array_ctor = JS_DupValue(ctx, obj);
  53048     JS_SetPropertyFunctionList(ctx, obj, js_array_funcs,
  53049                                countof(js_array_funcs));
  53050 
  53051     /* XXX: create auto_initializer */
  53052     {
  53053         /* initialize Array.prototype[Symbol.unscopables] */
  53054         static const char unscopables[] =
  53055             "copyWithin" "\0"
  53056             "entries" "\0"
  53057             "fill" "\0"
  53058             "find" "\0"
  53059             "findIndex" "\0"
  53060             "findLast" "\0"
  53061             "findLastIndex" "\0"
  53062             "flat" "\0"
  53063             "flatMap" "\0"
  53064             "includes" "\0"
  53065             "keys" "\0"
  53066             "toReversed" "\0"
  53067             "toSorted" "\0"
  53068             "toSpliced" "\0"
  53069             "values" "\0";
  53070         const char *p = unscopables;
  53071         obj1 = JS_NewObjectProto(ctx, JS_NULL);
  53072         for(p = unscopables; *p; p += strlen(p) + 1) {
  53073             JS_DefinePropertyValueStr(ctx, obj1, p, JS_TRUE, JS_PROP_C_W_E);
  53074         }
  53075         JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ARRAY],
  53076                                JS_ATOM_Symbol_unscopables, obj1,
  53077                                JS_PROP_CONFIGURABLE);
  53078     }
  53079 
  53080     /* needed to initialize arguments[Symbol.iterator] */
  53081     ctx->array_proto_values =
  53082         JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_values);
  53083 
  53084     ctx->class_proto[JS_CLASS_ARRAY_ITERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);
  53085     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_ITERATOR],
  53086                                js_array_iterator_proto_funcs,
  53087                                countof(js_array_iterator_proto_funcs));
  53088 
  53089     /* parseFloat and parseInteger must be defined before Number
  53090        because of the Number.parseFloat and Number.parseInteger
  53091        aliases */
  53092     JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_global_funcs,
  53093                                countof(js_global_funcs));
  53094 
  53095     /* Number */
  53096     ctx->class_proto[JS_CLASS_NUMBER] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
  53097                                                                JS_CLASS_NUMBER);
  53098     JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_NUMBER], JS_NewInt32(ctx, 0));
  53099     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_NUMBER],
  53100                                js_number_proto_funcs,
  53101                                countof(js_number_proto_funcs));
  53102     number_obj = JS_NewGlobalCConstructor(ctx, "Number", js_number_constructor, 1,
  53103                                           ctx->class_proto[JS_CLASS_NUMBER]);
  53104     JS_SetPropertyFunctionList(ctx, number_obj, js_number_funcs, countof(js_number_funcs));
  53105 
  53106     /* Boolean */
  53107     ctx->class_proto[JS_CLASS_BOOLEAN] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
  53108                                                                 JS_CLASS_BOOLEAN);
  53109     JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_BOOLEAN], JS_NewBool(ctx, FALSE));
  53110     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BOOLEAN], js_boolean_proto_funcs,
  53111                                countof(js_boolean_proto_funcs));
  53112     JS_NewGlobalCConstructor(ctx, "Boolean", js_boolean_constructor, 1,
  53113                              ctx->class_proto[JS_CLASS_BOOLEAN]);
  53114 
  53115     /* String */
  53116     ctx->class_proto[JS_CLASS_STRING] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
  53117                                                                JS_CLASS_STRING);
  53118     JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_STRING], JS_AtomToString(ctx, JS_ATOM_empty_string));
  53119     obj = JS_NewGlobalCConstructor(ctx, "String", js_string_constructor, 1,
  53120                                    ctx->class_proto[JS_CLASS_STRING]);
  53121     JS_SetPropertyFunctionList(ctx, obj, js_string_funcs,
  53122                                countof(js_string_funcs));
  53123     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_funcs,
  53124                                countof(js_string_proto_funcs));
  53125 
  53126     ctx->class_proto[JS_CLASS_STRING_ITERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);
  53127     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING_ITERATOR],
  53128                                js_string_iterator_proto_funcs,
  53129                                countof(js_string_iterator_proto_funcs));
  53130 
  53131     /* Math: create as autoinit object */
  53132     js_random_init(ctx);
  53133     JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_math_obj, countof(js_math_obj));
  53134 
  53135     /* ES6 Reflect: create as autoinit object */
  53136     JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_reflect_obj, countof(js_reflect_obj));
  53137 
  53138     /* ES6 Symbol */
  53139     ctx->class_proto[JS_CLASS_SYMBOL] = JS_NewObject(ctx);
  53140     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SYMBOL], js_symbol_proto_funcs,
  53141                                countof(js_symbol_proto_funcs));
  53142     obj = JS_NewGlobalCConstructor(ctx, "Symbol", js_symbol_constructor, 0,
  53143                                    ctx->class_proto[JS_CLASS_SYMBOL]);
  53144     JS_SetPropertyFunctionList(ctx, obj, js_symbol_funcs,
  53145                                countof(js_symbol_funcs));
  53146     for(i = JS_ATOM_Symbol_toPrimitive; i < JS_ATOM_END; i++) {
  53147         char buf[ATOM_GET_STR_BUF_SIZE];
  53148         const char *str, *p;
  53149         str = JS_AtomGetStr(ctx, buf, sizeof(buf), i);
  53150         /* skip "Symbol." */
  53151         p = strchr(str, '.');
  53152         if (p)
  53153             str = p + 1;
  53154         JS_DefinePropertyValueStr(ctx, obj, str, JS_AtomToValue(ctx, i), 0);
  53155     }
  53156 
  53157     /* ES6 Generator */
  53158     ctx->class_proto[JS_CLASS_GENERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);
  53159     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_GENERATOR],
  53160                                js_generator_proto_funcs,
  53161                                countof(js_generator_proto_funcs));
  53162 
  53163     ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto);
  53164     obj1 = JS_NewCFunctionMagic(ctx, js_function_constructor,
  53165                                 "GeneratorFunction", 1,
  53166                                 JS_CFUNC_constructor_or_func_magic, JS_FUNC_GENERATOR);
  53167     JS_SetPropertyFunctionList(ctx,
  53168                                ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],
  53169                                js_generator_function_proto_funcs,
  53170                                countof(js_generator_function_proto_funcs));
  53171     JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],
  53172                        ctx->class_proto[JS_CLASS_GENERATOR],
  53173                        JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE);
  53174     JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],
  53175                        0, JS_PROP_CONFIGURABLE);
  53176     JS_FreeValue(ctx, obj1);
  53177 
  53178     /* global properties */
  53179     ctx->eval_obj = JS_NewCFunction(ctx, js_global_eval, "eval", 1);
  53180     JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_eval,
  53181                            JS_DupValue(ctx, ctx->eval_obj),
  53182                            JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
  53183 
  53184     JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_globalThis,
  53185                            JS_DupValue(ctx, ctx->global_obj),
  53186                            JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
  53187 }
  53188 
  53189 /* Typed Arrays */
  53190 
  53191 static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT] = {
  53192     0, 0, 0, 1, 1, 2, 2,
  53193     3, 3, /* BigInt64Array, BigUint64Array */
  53194     2, 3
  53195 };
  53196 
  53197 static JSValue js_array_buffer_constructor3(JSContext *ctx,
  53198                                             JSValueConst new_target,
  53199                                             uint64_t len, JSClassID class_id,
  53200                                             uint8_t *buf,
  53201                                             JSFreeArrayBufferDataFunc *free_func,
  53202                                             void *opaque, BOOL alloc_flag)
  53203 {
  53204     JSRuntime *rt = ctx->rt;
  53205     JSValue obj;
  53206     JSArrayBuffer *abuf = NULL;
  53207 
  53208     obj = js_create_from_ctor(ctx, new_target, class_id);
  53209     if (JS_IsException(obj))
  53210         return obj;
  53211     /* XXX: we are currently limited to 2 GB */
  53212     if (len > INT32_MAX) {
  53213         JS_ThrowRangeError(ctx, "invalid array buffer length");
  53214         goto fail;
  53215     }
  53216     abuf = js_malloc(ctx, sizeof(*abuf));
  53217     if (!abuf)
  53218         goto fail;
  53219     abuf->byte_length = len;
  53220     if (alloc_flag) {
  53221         if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER &&
  53222             rt->sab_funcs.sab_alloc) {
  53223             abuf->data = rt->sab_funcs.sab_alloc(rt->sab_funcs.sab_opaque,
  53224                                                  max_int(len, 1));
  53225             if (!abuf->data)
  53226                 goto fail;
  53227             memset(abuf->data, 0, len);
  53228         } else {
  53229             /* the allocation must be done after the object creation */
  53230             abuf->data = js_mallocz(ctx, max_int(len, 1));
  53231             if (!abuf->data)
  53232                 goto fail;
  53233         }
  53234     } else {
  53235         if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER &&
  53236             rt->sab_funcs.sab_dup) {
  53237             rt->sab_funcs.sab_dup(rt->sab_funcs.sab_opaque, buf);
  53238         }
  53239         abuf->data = buf;
  53240     }
  53241     init_list_head(&abuf->array_list);
  53242     abuf->detached = FALSE;
  53243     abuf->shared = (class_id == JS_CLASS_SHARED_ARRAY_BUFFER);
  53244     abuf->opaque = opaque;
  53245     abuf->free_func = free_func;
  53246     if (alloc_flag && buf)
  53247         memcpy(abuf->data, buf, len);
  53248     JS_SetOpaque(obj, abuf);
  53249     return obj;
  53250  fail:
  53251     JS_FreeValue(ctx, obj);
  53252     js_free(ctx, abuf);
  53253     return JS_EXCEPTION;
  53254 }
  53255 
  53256 static void js_array_buffer_free(JSRuntime *rt, void *opaque, void *ptr)
  53257 {
  53258     js_free_rt(rt, ptr);
  53259 }
  53260 
  53261 static JSValue js_array_buffer_constructor2(JSContext *ctx,
  53262                                             JSValueConst new_target,
  53263                                             uint64_t len, JSClassID class_id)
  53264 {
  53265     return js_array_buffer_constructor3(ctx, new_target, len, class_id,
  53266                                         NULL, js_array_buffer_free, NULL,
  53267                                         TRUE);
  53268 }
  53269 
  53270 static JSValue js_array_buffer_constructor1(JSContext *ctx,
  53271                                             JSValueConst new_target,
  53272                                             uint64_t len)
  53273 {
  53274     return js_array_buffer_constructor2(ctx, new_target, len,
  53275                                         JS_CLASS_ARRAY_BUFFER);
  53276 }
  53277 
  53278 JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len,
  53279                           JSFreeArrayBufferDataFunc *free_func, void *opaque,
  53280                           BOOL is_shared)
  53281 {
  53282     return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len,
  53283                                         is_shared ? JS_CLASS_SHARED_ARRAY_BUFFER : JS_CLASS_ARRAY_BUFFER,
  53284                                         buf, free_func, opaque, FALSE);
  53285 }
  53286 
  53287 /* create a new ArrayBuffer of length 'len' and copy 'buf' to it */
  53288 JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len)
  53289 {
  53290     return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len,
  53291                                         JS_CLASS_ARRAY_BUFFER,
  53292                                         (uint8_t *)buf,
  53293                                         js_array_buffer_free, NULL,
  53294                                         TRUE);
  53295 }
  53296 
  53297 static JSValue js_array_buffer_constructor(JSContext *ctx,
  53298                                            JSValueConst new_target,
  53299                                            int argc, JSValueConst *argv)
  53300 {
  53301     uint64_t len;
  53302     if (JS_ToIndex(ctx, &len, argv[0]))
  53303         return JS_EXCEPTION;
  53304     return js_array_buffer_constructor1(ctx, new_target, len);
  53305 }
  53306 
  53307 static JSValue js_shared_array_buffer_constructor(JSContext *ctx,
  53308                                                   JSValueConst new_target,
  53309                                                   int argc, JSValueConst *argv)
  53310 {
  53311     uint64_t len;
  53312     if (JS_ToIndex(ctx, &len, argv[0]))
  53313         return JS_EXCEPTION;
  53314     return js_array_buffer_constructor2(ctx, new_target, len,
  53315                                         JS_CLASS_SHARED_ARRAY_BUFFER);
  53316 }
  53317 
  53318 /* also used for SharedArrayBuffer */
  53319 static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val)
  53320 {
  53321     JSObject *p = JS_VALUE_GET_OBJ(val);
  53322     JSArrayBuffer *abuf = p->u.array_buffer;
  53323     struct list_head *el, *el1;
  53324 
  53325     if (abuf) {
  53326         /* The ArrayBuffer finalizer may be called before the typed
  53327            array finalizers using it, so abuf->array_list is not
  53328            necessarily empty. */
  53329         list_for_each_safe(el, el1, &abuf->array_list) {
  53330             JSTypedArray *ta;
  53331             JSObject *p1;
  53332 
  53333             ta = list_entry(el, JSTypedArray, link);
  53334             ta->link.prev = NULL;
  53335             ta->link.next = NULL;
  53336             p1 = ta->obj;
  53337             /* Note: the typed array length and offset fields are not modified */
  53338             if (p1->class_id != JS_CLASS_DATAVIEW) {
  53339                 p1->u.array.count = 0;
  53340                 p1->u.array.u.ptr = NULL;
  53341             }
  53342         }
  53343         if (abuf->shared && rt->sab_funcs.sab_free) {
  53344             rt->sab_funcs.sab_free(rt->sab_funcs.sab_opaque, abuf->data);
  53345         } else {
  53346             if (abuf->free_func)
  53347                 abuf->free_func(rt, abuf->opaque, abuf->data);
  53348         }
  53349         js_free_rt(rt, abuf);
  53350     }
  53351 }
  53352 
  53353 static JSValue js_array_buffer_isView(JSContext *ctx,
  53354                                       JSValueConst this_val,
  53355                                       int argc, JSValueConst *argv)
  53356 {
  53357     JSObject *p;
  53358     BOOL res;
  53359     res = FALSE;
  53360     if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) {
  53361         p = JS_VALUE_GET_OBJ(argv[0]);
  53362         if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
  53363             p->class_id <= JS_CLASS_DATAVIEW) {
  53364             res = TRUE;
  53365         }
  53366     }
  53367     return JS_NewBool(ctx, res);
  53368 }
  53369 
  53370 static const JSCFunctionListEntry js_array_buffer_funcs[] = {
  53371     JS_CFUNC_DEF("isView", 1, js_array_buffer_isView ),
  53372     JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
  53373 };
  53374 
  53375 static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx)
  53376 {
  53377     return JS_ThrowTypeError(ctx, "ArrayBuffer is detached");
  53378 }
  53379 
  53380 static JSValue js_array_buffer_get_byteLength(JSContext *ctx,
  53381                                               JSValueConst this_val,
  53382                                               int class_id)
  53383 {
  53384     JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id);
  53385     if (!abuf)
  53386         return JS_EXCEPTION;
  53387     /* return 0 if detached */
  53388     return JS_NewUint32(ctx, abuf->byte_length);
  53389 }
  53390 
  53391 void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj)
  53392 {
  53393     JSArrayBuffer *abuf = JS_GetOpaque(obj, JS_CLASS_ARRAY_BUFFER);
  53394     struct list_head *el;
  53395 
  53396     if (!abuf || abuf->detached)
  53397         return;
  53398     if (abuf->free_func)
  53399         abuf->free_func(ctx->rt, abuf->opaque, abuf->data);
  53400     abuf->data = NULL;
  53401     abuf->byte_length = 0;
  53402     abuf->detached = TRUE;
  53403 
  53404     list_for_each(el, &abuf->array_list) {
  53405         JSTypedArray *ta;
  53406         JSObject *p;
  53407 
  53408         ta = list_entry(el, JSTypedArray, link);
  53409         p = ta->obj;
  53410         /* Note: the typed array length and offset fields are not modified */
  53411         if (p->class_id != JS_CLASS_DATAVIEW) {
  53412             p->u.array.count = 0;
  53413             p->u.array.u.ptr = NULL;
  53414         }
  53415     }
  53416 }
  53417 
  53418 /* check if obj is ArrayBuffer or SharedArrayBuffer */
  53419 static BOOL js_is_array_buffer(JSContext *ctx, JSValueConst obj)
  53420 {
  53421   JSObject *p;
  53422   if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
  53423       return FALSE;
  53424   p = JS_VALUE_GET_OBJ(obj);
  53425   if (p->class_id != JS_CLASS_ARRAY_BUFFER &&
  53426       p->class_id != JS_CLASS_SHARED_ARRAY_BUFFER) {
  53427     return FALSE;
  53428   }
  53429   return TRUE;
  53430 }
  53431 
  53432 /* get an ArrayBuffer or SharedArrayBuffer */
  53433 static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj)
  53434 {
  53435     JSObject *p;
  53436     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
  53437         goto fail;
  53438     p = JS_VALUE_GET_OBJ(obj);
  53439     if (p->class_id != JS_CLASS_ARRAY_BUFFER &&
  53440         p->class_id != JS_CLASS_SHARED_ARRAY_BUFFER) {
  53441     fail:
  53442         JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_ARRAY_BUFFER);
  53443         return NULL;
  53444     }
  53445     return p->u.array_buffer;
  53446 }
  53447 
  53448 static JSValue js_array_buffer_slice(JSContext *ctx,
  53449                                      JSValueConst this_val,
  53450                                      int argc, JSValueConst *argv, int class_id)
  53451 {
  53452     JSArrayBuffer *abuf, *new_abuf;
  53453     int64_t len, start, end, new_len;
  53454     JSValue ctor, new_obj;
  53455 
  53456     abuf = JS_GetOpaque2(ctx, this_val, class_id);
  53457     if (!abuf)
  53458         return JS_EXCEPTION;
  53459     if (abuf->detached)
  53460         return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  53461     len = abuf->byte_length;
  53462 
  53463     if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len))
  53464         return JS_EXCEPTION;
  53465 
  53466     end = len;
  53467     if (!JS_IsUndefined(argv[1])) {
  53468         if (JS_ToInt64Clamp(ctx, &end, argv[1], 0, len, len))
  53469             return JS_EXCEPTION;
  53470     }
  53471     new_len = max_int64(end - start, 0);
  53472     ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);
  53473     if (JS_IsException(ctor))
  53474         return ctor;
  53475     if (JS_IsUndefined(ctor)) {
  53476         new_obj = js_array_buffer_constructor2(ctx, JS_UNDEFINED, new_len,
  53477                                                class_id);
  53478     } else {
  53479         JSValue args[1];
  53480         args[0] = JS_NewInt64(ctx, new_len);
  53481         new_obj = JS_CallConstructor(ctx, ctor, 1, (JSValueConst *)args);
  53482         JS_FreeValue(ctx, ctor);
  53483         JS_FreeValue(ctx, args[0]);
  53484     }
  53485     if (JS_IsException(new_obj))
  53486         return new_obj;
  53487     new_abuf = JS_GetOpaque2(ctx, new_obj, class_id);
  53488     if (!new_abuf)
  53489         goto fail;
  53490     if (js_same_value(ctx, new_obj, this_val)) {
  53491         JS_ThrowTypeError(ctx, "cannot use identical ArrayBuffer");
  53492         goto fail;
  53493     }
  53494     if (new_abuf->detached) {
  53495         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  53496         goto fail;
  53497     }
  53498     if (new_abuf->byte_length < new_len) {
  53499         JS_ThrowTypeError(ctx, "new ArrayBuffer is too small");
  53500         goto fail;
  53501     }
  53502     /* must test again because of side effects */
  53503     if (abuf->detached) {
  53504         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  53505         goto fail;
  53506     }
  53507     memcpy(new_abuf->data, abuf->data + start, new_len);
  53508     return new_obj;
  53509  fail:
  53510     JS_FreeValue(ctx, new_obj);
  53511     return JS_EXCEPTION;
  53512 }
  53513 
  53514 static const JSCFunctionListEntry js_array_buffer_proto_funcs[] = {
  53515     JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_ARRAY_BUFFER ),
  53516     JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_ARRAY_BUFFER ),
  53517     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "ArrayBuffer", JS_PROP_CONFIGURABLE ),
  53518 };
  53519 
  53520 /* SharedArrayBuffer */
  53521 
  53522 static const JSCFunctionListEntry js_shared_array_buffer_funcs[] = {
  53523     JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
  53524 };
  53525 
  53526 static const JSCFunctionListEntry js_shared_array_buffer_proto_funcs[] = {
  53527     JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_SHARED_ARRAY_BUFFER ),
  53528     JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_SHARED_ARRAY_BUFFER ),
  53529     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "SharedArrayBuffer", JS_PROP_CONFIGURABLE ),
  53530 };
  53531 
  53532 static JSObject *get_typed_array(JSContext *ctx,
  53533                                  JSValueConst this_val,
  53534                                  int is_dataview)
  53535 {
  53536     JSObject *p;
  53537     if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
  53538         goto fail;
  53539     p = JS_VALUE_GET_OBJ(this_val);
  53540     if (is_dataview) {
  53541         if (p->class_id != JS_CLASS_DATAVIEW)
  53542             goto fail;
  53543     } else {
  53544         if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY &&
  53545               p->class_id <= JS_CLASS_FLOAT64_ARRAY)) {
  53546         fail:
  53547             JS_ThrowTypeError(ctx, "not a %s", is_dataview ? "DataView" : "TypedArray");
  53548             return NULL;
  53549         }
  53550     }
  53551     return p;
  53552 }
  53553 
  53554 /* WARNING: 'p' must be a typed array */
  53555 static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p)
  53556 {
  53557     JSTypedArray *ta = p->u.typed_array;
  53558     JSArrayBuffer *abuf = ta->buffer->u.array_buffer;
  53559     /* XXX: could simplify test by ensuring that
  53560        p->u.array.u.ptr is NULL iff it is detached */
  53561     return abuf->detached;
  53562 }
  53563 
  53564 /* WARNING: 'p' must be a typed array. Works even if the array buffer
  53565    is detached */
  53566 static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p)
  53567 {
  53568     JSTypedArray *ta = p->u.typed_array;
  53569     int size_log2 = typed_array_size_log2(p->class_id);
  53570     return ta->length >> size_log2;
  53571 }
  53572 
  53573 static int validate_typed_array(JSContext *ctx, JSValueConst this_val)
  53574 {
  53575     JSObject *p;
  53576     p = get_typed_array(ctx, this_val, 0);
  53577     if (!p)
  53578         return -1;
  53579     if (typed_array_is_detached(ctx, p)) {
  53580         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  53581         return -1;
  53582     }
  53583     return 0;
  53584 }
  53585 
  53586 static JSValue js_typed_array_get_length(JSContext *ctx,
  53587                                          JSValueConst this_val)
  53588 {
  53589     JSObject *p;
  53590     p = get_typed_array(ctx, this_val, 0);
  53591     if (!p)
  53592         return JS_EXCEPTION;
  53593     return JS_NewInt32(ctx, p->u.array.count);
  53594 }
  53595 
  53596 static JSValue js_typed_array_get_buffer(JSContext *ctx,
  53597                                          JSValueConst this_val, int is_dataview)
  53598 {
  53599     JSObject *p;
  53600     JSTypedArray *ta;
  53601     p = get_typed_array(ctx, this_val, is_dataview);
  53602     if (!p)
  53603         return JS_EXCEPTION;
  53604     ta = p->u.typed_array;
  53605     return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer));
  53606 }
  53607 
  53608 static JSValue js_typed_array_get_byteLength(JSContext *ctx,
  53609                                              JSValueConst this_val,
  53610                                              int is_dataview)
  53611 {
  53612     JSObject *p;
  53613     JSTypedArray *ta;
  53614     p = get_typed_array(ctx, this_val, is_dataview);
  53615     if (!p)
  53616         return JS_EXCEPTION;
  53617     if (typed_array_is_detached(ctx, p)) {
  53618         if (is_dataview) {
  53619             return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  53620         } else {
  53621             return JS_NewInt32(ctx, 0);
  53622         }
  53623     }
  53624     ta = p->u.typed_array;
  53625     return JS_NewInt32(ctx, ta->length);
  53626 }
  53627 
  53628 static JSValue js_typed_array_get_byteOffset(JSContext *ctx,
  53629                                              JSValueConst this_val,
  53630                                              int is_dataview)
  53631 {
  53632     JSObject *p;
  53633     JSTypedArray *ta;
  53634     p = get_typed_array(ctx, this_val, is_dataview);
  53635     if (!p)
  53636         return JS_EXCEPTION;
  53637     if (typed_array_is_detached(ctx, p)) {
  53638         if (is_dataview) {
  53639             return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  53640         } else {
  53641             return JS_NewInt32(ctx, 0);
  53642         }
  53643     }
  53644     ta = p->u.typed_array;
  53645     return JS_NewInt32(ctx, ta->offset);
  53646 }
  53647 
  53648 JSValue JS_NewTypedArray(JSContext *ctx, int argc, JSValueConst *argv,
  53649                          JSTypedArrayEnum type)
  53650 {
  53651     if (type < JS_TYPED_ARRAY_UINT8C || type > JS_TYPED_ARRAY_FLOAT64)
  53652         return JS_ThrowRangeError(ctx, "invalid typed array type");
  53653 
  53654     return js_typed_array_constructor(ctx, JS_UNDEFINED, argc, argv,
  53655                                       JS_CLASS_UINT8C_ARRAY + type);
  53656 }
  53657 
  53658 /* Return the buffer associated to the typed array or an exception if
  53659    it is not a typed array or if the buffer is detached. pbyte_offset,
  53660    pbyte_length or pbytes_per_element can be NULL. */
  53661 JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj,
  53662                                size_t *pbyte_offset,
  53663                                size_t *pbyte_length,
  53664                                size_t *pbytes_per_element)
  53665 {
  53666     JSObject *p;
  53667     JSTypedArray *ta;
  53668     p = get_typed_array(ctx, obj, FALSE);
  53669     if (!p)
  53670         return JS_EXCEPTION;
  53671     if (typed_array_is_detached(ctx, p))
  53672         return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  53673     ta = p->u.typed_array;
  53674     if (pbyte_offset)
  53675         *pbyte_offset = ta->offset;
  53676     if (pbyte_length)
  53677         *pbyte_length = ta->length;
  53678     if (pbytes_per_element) {
  53679         *pbytes_per_element = 1 << typed_array_size_log2(p->class_id);
  53680     }
  53681     return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer));
  53682 }
  53683 
  53684 BOOL JS_IsArrayBuffer(JSValueConst obj)
  53685 {
  53686     JSObject *p;
  53687 
  53688     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
  53689         return FALSE;
  53690     p = JS_VALUE_GET_OBJ(obj);
  53691     if (p->class_id == JS_CLASS_ARRAY_BUFFER ||
  53692         p->class_id == JS_CLASS_SHARED_ARRAY_BUFFER) {
  53693         return TRUE;
  53694     }
  53695     if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
  53696         p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
  53697         return TRUE;
  53698     }
  53699     return FALSE;
  53700 }
  53701 
  53702 /* return NULL if exception. WARNING: any JS call can detach the
  53703    buffer and render the returned pointer invalid */
  53704 uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj)
  53705 {
  53706     if (js_is_array_buffer(ctx, obj)) {
  53707         JSArrayBuffer* abuf = js_get_array_buffer(ctx, obj);
  53708         if (!abuf)
  53709             goto fail;
  53710         if (abuf->detached) {
  53711             JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  53712             goto fail;
  53713         }
  53714         *psize = abuf->byte_length;
  53715         return abuf->data;
  53716     } else {
  53717         JSObject* p;
  53718         JSTypedArray* ta;
  53719         JSArrayBuffer* abuf;
  53720         p = get_typed_array(ctx, obj, FALSE);
  53721         if (!p) {
  53722             goto fail;
  53723         }
  53724         if (typed_array_is_detached(ctx, p)) {
  53725             JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  53726             goto fail;
  53727         }
  53728         ta = p->u.typed_array;
  53729         abuf = ta->buffer->u.array_buffer;
  53730         if (!abuf)
  53731             goto fail;
  53732         if (abuf->detached) {
  53733             JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  53734             goto fail;
  53735         }
  53736         *psize = ta->length;
  53737         return abuf->data + ta->offset;
  53738     }
  53739     JS_ThrowTypeError(ctx, "expected ArrayBuffer or ArrayBufferView");
  53740 fail:
  53741     *psize = 0;
  53742     return NULL;
  53743 }
  53744 
  53745                                
  53746 static JSValue js_typed_array_get_toStringTag(JSContext *ctx,
  53747                                               JSValueConst this_val)
  53748 {
  53749     JSObject *p;
  53750     if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
  53751         return JS_UNDEFINED;
  53752     p = JS_VALUE_GET_OBJ(this_val);
  53753     if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY &&
  53754           p->class_id <= JS_CLASS_FLOAT64_ARRAY))
  53755         return JS_UNDEFINED;
  53756     return JS_AtomToString(ctx, ctx->rt->class_array[p->class_id].class_name);
  53757 }
  53758 
  53759 static JSValue js_typed_array_set_internal(JSContext *ctx,
  53760                                            JSValueConst dst,
  53761                                            JSValueConst src,
  53762                                            JSValueConst off)
  53763 {
  53764     JSObject *p;
  53765     JSObject *src_p;
  53766     uint32_t i;
  53767     int64_t src_len, offset;
  53768     JSValue val, src_obj = JS_UNDEFINED;
  53769 
  53770     p = get_typed_array(ctx, dst, 0);
  53771     if (!p)
  53772         goto fail;
  53773     if (JS_ToInt64Sat(ctx, &offset, off))
  53774         goto fail;
  53775     if (offset < 0)
  53776         goto range_error;
  53777     if (typed_array_is_detached(ctx, p)) {
  53778     detached:
  53779         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  53780         goto fail;
  53781     }
  53782     src_obj = JS_ToObject(ctx, src);
  53783     if (JS_IsException(src_obj))
  53784         goto fail;
  53785     src_p = JS_VALUE_GET_OBJ(src_obj);
  53786     if (src_p->class_id >= JS_CLASS_UINT8C_ARRAY &&
  53787         src_p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
  53788         JSTypedArray *dest_ta = p->u.typed_array;
  53789         JSArrayBuffer *dest_abuf = dest_ta->buffer->u.array_buffer;
  53790         JSTypedArray *src_ta = src_p->u.typed_array;
  53791         JSArrayBuffer *src_abuf = src_ta->buffer->u.array_buffer;
  53792         int shift = typed_array_size_log2(p->class_id);
  53793 
  53794         if (src_abuf->detached)
  53795             goto detached;
  53796 
  53797         src_len = src_p->u.array.count;
  53798         if (offset > (int64_t)(p->u.array.count - src_len))
  53799             goto range_error;
  53800 
  53801         /* copying between typed objects */
  53802         if (src_p->class_id == p->class_id) {
  53803             /* same type, use memmove */
  53804             memmove(dest_abuf->data + dest_ta->offset + (offset << shift),
  53805                     src_abuf->data + src_ta->offset, src_len << shift);
  53806             goto done;
  53807         }
  53808         if (dest_abuf->data == src_abuf->data) {
  53809             /* copying between the same buffer using different types of mappings
  53810                would require a temporary buffer */
  53811         }
  53812         /* otherwise, default behavior is slow but correct */
  53813     } else {
  53814         if (js_get_length64(ctx, &src_len, src_obj))
  53815             goto fail;
  53816         if (offset > (int64_t)(p->u.array.count - src_len)) {
  53817         range_error:
  53818             JS_ThrowRangeError(ctx, "invalid array length");
  53819             goto fail;
  53820         }
  53821     }
  53822     for(i = 0; i < src_len; i++) {
  53823         val = JS_GetPropertyUint32(ctx, src_obj, i);
  53824         if (JS_IsException(val))
  53825             goto fail;
  53826         if (JS_SetPropertyUint32(ctx, dst, offset + i, val) < 0)
  53827             goto fail;
  53828     }
  53829 done:
  53830     JS_FreeValue(ctx, src_obj);
  53831     return JS_UNDEFINED;
  53832 fail:
  53833     JS_FreeValue(ctx, src_obj);
  53834     return JS_EXCEPTION;
  53835 }
  53836 
  53837 static JSValue js_typed_array_at(JSContext *ctx, JSValueConst this_val,
  53838                                  int argc, JSValueConst *argv)
  53839 {
  53840     JSObject *p;
  53841     int64_t idx, len;
  53842 
  53843     p = get_typed_array(ctx, this_val, 0);
  53844     if (!p)
  53845         return JS_EXCEPTION;
  53846 
  53847     if (typed_array_is_detached(ctx, p)) {
  53848         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  53849         return JS_EXCEPTION;
  53850     }
  53851 
  53852     if (JS_ToInt64Sat(ctx, &idx, argv[0]))
  53853         return JS_EXCEPTION;
  53854 
  53855     len = p->u.array.count;
  53856     if (idx < 0)
  53857         idx = len + idx;
  53858     if (idx < 0 || idx >= len)
  53859         return JS_UNDEFINED;
  53860     return JS_GetPropertyInt64(ctx, this_val, idx);
  53861 }
  53862 
  53863 static JSValue js_typed_array_with(JSContext *ctx, JSValueConst this_val,
  53864                                    int argc, JSValueConst *argv)
  53865 {
  53866     JSValue arr, val;
  53867     JSObject *p;
  53868     int64_t idx, len;
  53869 
  53870     p = get_typed_array(ctx, this_val, /*is_dataview*/0);
  53871     if (!p)
  53872         return JS_EXCEPTION;
  53873 
  53874     if (JS_ToInt64Sat(ctx, &idx, argv[0]))
  53875         return JS_EXCEPTION;
  53876 
  53877     len = p->u.array.count;
  53878     if (idx < 0)
  53879         idx = len + idx;
  53880     if (idx < 0 || idx >= len)
  53881         return JS_ThrowRangeError(ctx, "invalid array index");
  53882 
  53883     val = JS_ToPrimitive(ctx, argv[1], HINT_NUMBER);
  53884     if (JS_IsException(val))
  53885         return JS_EXCEPTION;
  53886 
  53887     arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val,
  53888                                         p->class_id);
  53889     if (JS_IsException(arr)) {
  53890         JS_FreeValue(ctx, val);
  53891         return JS_EXCEPTION;
  53892     }
  53893     if (JS_SetPropertyInt64(ctx, arr, idx, val) < 0) {
  53894         JS_FreeValue(ctx, arr);
  53895         return JS_EXCEPTION;
  53896     }
  53897     return arr;
  53898 }
  53899 
  53900 static JSValue js_typed_array_set(JSContext *ctx,
  53901                                   JSValueConst this_val,
  53902                                   int argc, JSValueConst *argv)
  53903 {
  53904     JSValueConst offset = JS_UNDEFINED;
  53905     if (argc > 1) {
  53906         offset = argv[1];
  53907     }
  53908     return js_typed_array_set_internal(ctx, this_val, argv[0], offset);
  53909 }
  53910 
  53911 static JSValue js_create_typed_array_iterator(JSContext *ctx, JSValueConst this_val,
  53912                                               int argc, JSValueConst *argv, int magic)
  53913 {
  53914     if (validate_typed_array(ctx, this_val))
  53915         return JS_EXCEPTION;
  53916     return js_create_array_iterator(ctx, this_val, argc, argv, magic);
  53917 }
  53918 
  53919 /* return < 0 if exception */
  53920 static int js_typed_array_get_length_internal(JSContext *ctx,
  53921                                               JSValueConst obj)
  53922 {
  53923     JSObject *p;
  53924     p = get_typed_array(ctx, obj, 0);
  53925     if (!p)
  53926         return -1;
  53927     if (typed_array_is_detached(ctx, p)) {
  53928         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  53929         return -1;
  53930     }
  53931     return p->u.array.count;
  53932 }
  53933 
  53934 #if 0
  53935 /* validate a typed array and return its length */
  53936 static JSValue js_typed_array___getLength(JSContext *ctx,
  53937                                           JSValueConst this_val,
  53938                                           int argc, JSValueConst *argv)
  53939 {
  53940     BOOL ignore_detached = JS_ToBool(ctx, argv[1]);
  53941 
  53942     if (ignore_detached) {
  53943         return js_typed_array_get_length(ctx, argv[0]);
  53944     } else {
  53945         int len;
  53946         len = js_typed_array_get_length_internal(ctx, argv[0]);
  53947         if (len < 0)
  53948             return JS_EXCEPTION;
  53949         return JS_NewInt32(ctx, len);
  53950     }
  53951 }
  53952 #endif
  53953 
  53954 static JSValue js_typed_array_create(JSContext *ctx, JSValueConst ctor,
  53955                                      int argc, JSValueConst *argv)
  53956 {
  53957     JSValue ret;
  53958     int new_len;
  53959     int64_t len;
  53960 
  53961     ret = JS_CallConstructor(ctx, ctor, argc, argv);
  53962     if (JS_IsException(ret))
  53963         return ret;
  53964     /* validate the typed array */
  53965     new_len = js_typed_array_get_length_internal(ctx, ret);
  53966     if (new_len < 0)
  53967         goto fail;
  53968     if (argc == 1) {
  53969         /* ensure that it is large enough */
  53970         if (JS_ToLengthFree(ctx, &len, JS_DupValue(ctx, argv[0])))
  53971             goto fail;
  53972         if (new_len < len) {
  53973             JS_ThrowTypeError(ctx, "TypedArray length is too small");
  53974         fail:
  53975             JS_FreeValue(ctx, ret);
  53976             return JS_EXCEPTION;
  53977         }
  53978     }
  53979     return ret;
  53980 }
  53981 
  53982 #if 0
  53983 static JSValue js_typed_array___create(JSContext *ctx,
  53984                                        JSValueConst this_val,
  53985                                        int argc, JSValueConst *argv)
  53986 {
  53987     return js_typed_array_create(ctx, argv[0], max_int(argc - 1, 0), argv + 1);
  53988 }
  53989 #endif
  53990 
  53991 static JSValue js_typed_array___speciesCreate(JSContext *ctx,
  53992                                               JSValueConst this_val,
  53993                                               int argc, JSValueConst *argv)
  53994 {
  53995     JSValueConst obj;
  53996     JSObject *p;
  53997     JSValue ctor, ret;
  53998     int argc1;
  53999 
  54000     obj = argv[0];
  54001     p = get_typed_array(ctx, obj, 0);
  54002     if (!p)
  54003         return JS_EXCEPTION;
  54004     ctor = JS_SpeciesConstructor(ctx, obj, JS_UNDEFINED);
  54005     if (JS_IsException(ctor))
  54006         return ctor;
  54007     argc1 = max_int(argc - 1, 0);
  54008     if (JS_IsUndefined(ctor)) {
  54009         ret = js_typed_array_constructor(ctx, JS_UNDEFINED, argc1, argv + 1,
  54010                                          p->class_id);
  54011     } else {
  54012         ret = js_typed_array_create(ctx, ctor, argc1, argv + 1);
  54013         JS_FreeValue(ctx, ctor);
  54014     }
  54015     return ret;
  54016 }
  54017 
  54018 static JSValue js_typed_array_from(JSContext *ctx, JSValueConst this_val,
  54019                                    int argc, JSValueConst *argv)
  54020 {
  54021     // from(items, mapfn = void 0, this_arg = void 0)
  54022     JSValueConst items = argv[0], mapfn, this_arg;
  54023     JSValueConst args[2];
  54024     JSValue stack[2];
  54025     JSValue iter, arr, r, v, v2;
  54026     int64_t k, len;
  54027     int done, mapping;
  54028 
  54029     mapping = FALSE;
  54030     mapfn = JS_UNDEFINED;
  54031     this_arg = JS_UNDEFINED;
  54032     r = JS_UNDEFINED;
  54033     arr = JS_UNDEFINED;
  54034     stack[0] = JS_UNDEFINED;
  54035     stack[1] = JS_UNDEFINED;
  54036 
  54037     if (argc > 1) {
  54038         mapfn = argv[1];
  54039         if (!JS_IsUndefined(mapfn)) {
  54040             if (check_function(ctx, mapfn))
  54041                 goto exception;
  54042             mapping = 1;
  54043             if (argc > 2)
  54044                 this_arg = argv[2];
  54045         }
  54046     }
  54047     iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator);
  54048     if (JS_IsException(iter))
  54049         goto exception;
  54050     if (!JS_IsUndefined(iter)) {
  54051         JS_FreeValue(ctx, iter);
  54052         arr = JS_NewArray(ctx);
  54053         if (JS_IsException(arr))
  54054             goto exception;
  54055         stack[0] = JS_DupValue(ctx, items);
  54056         if (js_for_of_start(ctx, &stack[1], FALSE))
  54057             goto exception;
  54058         for (k = 0;; k++) {
  54059             v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done);
  54060             if (JS_IsException(v))
  54061                 goto exception_close;
  54062             if (done)
  54063                 break;
  54064             if (JS_DefinePropertyValueInt64(ctx, arr, k, v, JS_PROP_C_W_E | JS_PROP_THROW) < 0)
  54065                 goto exception_close;
  54066         }
  54067     } else {
  54068         arr = JS_ToObject(ctx, items);
  54069         if (JS_IsException(arr))
  54070             goto exception;
  54071     }
  54072     if (js_get_length64(ctx, &len, arr) < 0)
  54073         goto exception;
  54074     v = JS_NewInt64(ctx, len);
  54075     args[0] = v;
  54076     r = js_typed_array_create(ctx, this_val, 1, args);
  54077     JS_FreeValue(ctx, v);
  54078     if (JS_IsException(r))
  54079         goto exception;
  54080     for(k = 0; k < len; k++) {
  54081         v = JS_GetPropertyInt64(ctx, arr, k);
  54082         if (JS_IsException(v))
  54083             goto exception;
  54084         if (mapping) {
  54085             args[0] = v;
  54086             args[1] = JS_NewInt32(ctx, k);
  54087             v2 = JS_Call(ctx, mapfn, this_arg, 2, args);
  54088             JS_FreeValue(ctx, v);
  54089             v = v2;
  54090             if (JS_IsException(v))
  54091                 goto exception;
  54092         }
  54093         if (JS_SetPropertyInt64(ctx, r, k, v) < 0)
  54094             goto exception;
  54095     }
  54096     goto done;
  54097 
  54098  exception_close:
  54099     if (!JS_IsUndefined(stack[0]))
  54100         JS_IteratorClose(ctx, stack[0], TRUE);
  54101  exception:
  54102     JS_FreeValue(ctx, r);
  54103     r = JS_EXCEPTION;
  54104  done:
  54105     JS_FreeValue(ctx, arr);
  54106     JS_FreeValue(ctx, stack[0]);
  54107     JS_FreeValue(ctx, stack[1]);
  54108     return r;
  54109 }
  54110 
  54111 static JSValue js_typed_array_of(JSContext *ctx, JSValueConst this_val,
  54112                                  int argc, JSValueConst *argv)
  54113 {
  54114     JSValue obj;
  54115     JSValueConst args[1];
  54116     int i;
  54117 
  54118     args[0] = JS_NewInt32(ctx, argc);
  54119     obj = js_typed_array_create(ctx, this_val, 1, args);
  54120     if (JS_IsException(obj))
  54121         return obj;
  54122 
  54123     for(i = 0; i < argc; i++) {
  54124         if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0) {
  54125             JS_FreeValue(ctx, obj);
  54126             return JS_EXCEPTION;
  54127         }
  54128     }
  54129     return obj;
  54130 }
  54131 
  54132 static JSValue js_typed_array_copyWithin(JSContext *ctx, JSValueConst this_val,
  54133                                          int argc, JSValueConst *argv)
  54134 {
  54135     JSObject *p;
  54136     int len, to, from, final, count, shift;
  54137 
  54138     len = js_typed_array_get_length_internal(ctx, this_val);
  54139     if (len < 0)
  54140         return JS_EXCEPTION;
  54141 
  54142     if (JS_ToInt32Clamp(ctx, &to, argv[0], 0, len, len))
  54143         return JS_EXCEPTION;
  54144 
  54145     if (JS_ToInt32Clamp(ctx, &from, argv[1], 0, len, len))
  54146         return JS_EXCEPTION;
  54147 
  54148     final = len;
  54149     if (argc > 2 && !JS_IsUndefined(argv[2])) {
  54150         if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len))
  54151             return JS_EXCEPTION;
  54152     }
  54153 
  54154     count = min_int(final - from, len - to);
  54155     if (count > 0) {
  54156         p = JS_VALUE_GET_OBJ(this_val);
  54157         if (typed_array_is_detached(ctx, p))
  54158             return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  54159         shift = typed_array_size_log2(p->class_id);
  54160         memmove(p->u.array.u.uint8_ptr + (to << shift),
  54161                 p->u.array.u.uint8_ptr + (from << shift),
  54162                 count << shift);
  54163     }
  54164     return JS_DupValue(ctx, this_val);
  54165 }
  54166 
  54167 static JSValue js_typed_array_fill(JSContext *ctx, JSValueConst this_val,
  54168                                    int argc, JSValueConst *argv)
  54169 {
  54170     JSObject *p;
  54171     int len, k, final, shift;
  54172     uint64_t v64;
  54173 
  54174     len = js_typed_array_get_length_internal(ctx, this_val);
  54175     if (len < 0)
  54176         return JS_EXCEPTION;
  54177     p = JS_VALUE_GET_OBJ(this_val);
  54178 
  54179     if (p->class_id == JS_CLASS_UINT8C_ARRAY) {
  54180         int32_t v;
  54181         if (JS_ToUint8ClampFree(ctx, &v, JS_DupValue(ctx, argv[0])))
  54182             return JS_EXCEPTION;
  54183         v64 = v;
  54184     } else if (p->class_id <= JS_CLASS_UINT32_ARRAY) {
  54185         uint32_t v;
  54186         if (JS_ToUint32(ctx, &v, argv[0]))
  54187             return JS_EXCEPTION;
  54188         v64 = v;
  54189     } else if (p->class_id <= JS_CLASS_BIG_UINT64_ARRAY) {
  54190         if (JS_ToBigInt64(ctx, (int64_t *)&v64, argv[0]))
  54191             return JS_EXCEPTION;
  54192     } else {
  54193         double d;
  54194         if (JS_ToFloat64(ctx, &d, argv[0]))
  54195             return JS_EXCEPTION;
  54196         if (p->class_id == JS_CLASS_FLOAT32_ARRAY) {
  54197             union {
  54198                 float f;
  54199                 uint32_t u32;
  54200             } u;
  54201             u.f = d;
  54202             v64 = u.u32;
  54203         } else {
  54204             JSFloat64Union u;
  54205             u.d = d;
  54206             v64 = u.u64;
  54207         }
  54208     }
  54209 
  54210     k = 0;
  54211     if (argc > 1) {
  54212         if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len))
  54213             return JS_EXCEPTION;
  54214     }
  54215 
  54216     final = len;
  54217     if (argc > 2 && !JS_IsUndefined(argv[2])) {
  54218         if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len))
  54219             return JS_EXCEPTION;
  54220     }
  54221 
  54222     if (typed_array_is_detached(ctx, p))
  54223         return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  54224 
  54225     shift = typed_array_size_log2(p->class_id);
  54226     switch(shift) {
  54227     case 0:
  54228         if (k < final) {
  54229             memset(p->u.array.u.uint8_ptr + k, v64, final - k);
  54230         }
  54231         break;
  54232     case 1:
  54233         for(; k < final; k++) {
  54234             p->u.array.u.uint16_ptr[k] = v64;
  54235         }
  54236         break;
  54237     case 2:
  54238         for(; k < final; k++) {
  54239             p->u.array.u.uint32_ptr[k] = v64;
  54240         }
  54241         break;
  54242     case 3:
  54243         for(; k < final; k++) {
  54244             p->u.array.u.uint64_ptr[k] = v64;
  54245         }
  54246         break;
  54247     default:
  54248         abort();
  54249     }
  54250     return JS_DupValue(ctx, this_val);
  54251 }
  54252 
  54253 static JSValue js_typed_array_find(JSContext *ctx, JSValueConst this_val,
  54254                                    int argc, JSValueConst *argv, int mode)
  54255 {
  54256     JSValueConst func, this_arg;
  54257     JSValueConst args[3];
  54258     JSValue val, index_val, res;
  54259     int len, k, end;
  54260     int dir;
  54261 
  54262     val = JS_UNDEFINED;
  54263     len = js_typed_array_get_length_internal(ctx, this_val);
  54264     if (len < 0)
  54265         goto exception;
  54266 
  54267     func = argv[0];
  54268     if (check_function(ctx, func))
  54269         goto exception;
  54270 
  54271     this_arg = JS_UNDEFINED;
  54272     if (argc > 1)
  54273         this_arg = argv[1];
  54274 
  54275     k = 0;
  54276     dir = 1;
  54277     end = len;
  54278     if (mode == ArrayFindLast || mode == ArrayFindLastIndex) {
  54279         k = len - 1;
  54280         dir = -1;
  54281         end = -1;
  54282     }
  54283 
  54284     for(; k != end; k += dir) {
  54285         index_val = JS_NewInt32(ctx, k);
  54286         val = JS_GetPropertyValue(ctx, this_val, index_val);
  54287         if (JS_IsException(val))
  54288             goto exception;
  54289         args[0] = val;
  54290         args[1] = index_val;
  54291         args[2] = this_val;
  54292         res = JS_Call(ctx, func, this_arg, 3, args);
  54293         if (JS_IsException(res))
  54294             goto exception;
  54295         if (JS_ToBoolFree(ctx, res)) {
  54296             if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) {
  54297                 JS_FreeValue(ctx, val);
  54298                 return index_val;
  54299             } else {
  54300                 return val;
  54301             }
  54302         }
  54303         JS_FreeValue(ctx, val);
  54304     }
  54305     if (mode == ArrayFindIndex || mode == ArrayFindLastIndex)
  54306         return JS_NewInt32(ctx, -1);
  54307     else
  54308         return JS_UNDEFINED;
  54309 
  54310 exception:
  54311     JS_FreeValue(ctx, val);
  54312     return JS_EXCEPTION;
  54313 }
  54314 
  54315 #define special_indexOf 0
  54316 #define special_lastIndexOf 1
  54317 #define special_includes -1
  54318 
  54319 static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val,
  54320                                       int argc, JSValueConst *argv, int special)
  54321 {
  54322     JSObject *p;
  54323     int len, tag, is_int, is_bigint, k, stop, inc, res = -1;
  54324     int64_t v64;
  54325     double d;
  54326     float f;
  54327 
  54328     len = js_typed_array_get_length_internal(ctx, this_val);
  54329     if (len < 0)
  54330         goto exception;
  54331     if (len == 0)
  54332         goto done;
  54333 
  54334     if (special == special_lastIndexOf) {
  54335         k = len - 1;
  54336         if (argc > 1) {
  54337             if (JS_ToFloat64(ctx, &d, argv[1]))
  54338                 goto exception;
  54339             if (isnan(d)) {
  54340                 k = 0;
  54341             } else {
  54342                 if (d >= 0) {
  54343                     if (d < k) {
  54344                         k = d;
  54345                     }
  54346                 } else {
  54347                     d += len;
  54348                     if (d < 0)
  54349                         goto done;
  54350                     k = d;
  54351                 }
  54352             }
  54353         }
  54354         stop = -1;
  54355         inc = -1;
  54356     } else {
  54357         k = 0;
  54358         if (argc > 1) {
  54359             if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len))
  54360                 goto exception;
  54361         }
  54362         stop = len;
  54363         inc = 1;
  54364     }
  54365 
  54366     p = JS_VALUE_GET_OBJ(this_val);
  54367     /* if the array was detached, no need to go further (but no
  54368        exception is raised) */
  54369     if (typed_array_is_detached(ctx, p)) {
  54370         /* "includes" scans all the properties, so "undefined" can match */
  54371         if (special == special_includes && JS_IsUndefined(argv[0]) && len > 0)
  54372             res = 0;
  54373         goto done;
  54374     }
  54375 
  54376     is_bigint = 0;
  54377     is_int = 0; /* avoid warning */
  54378     v64 = 0; /* avoid warning */
  54379     tag = JS_VALUE_GET_NORM_TAG(argv[0]);
  54380     if (tag == JS_TAG_INT) {
  54381         is_int = 1;
  54382         v64 = JS_VALUE_GET_INT(argv[0]);
  54383         d = v64;
  54384     } else
  54385     if (tag == JS_TAG_FLOAT64) {
  54386         d = JS_VALUE_GET_FLOAT64(argv[0]);
  54387         if (d >= INT64_MIN && d < 0x1p63) {
  54388             v64 = d;
  54389             is_int = (v64 == d);
  54390         }
  54391     } else if (tag == JS_TAG_BIG_INT) {
  54392         JSBigFloat *p1 = JS_VALUE_GET_PTR(argv[0]);
  54393 
  54394         if (p->class_id == JS_CLASS_BIG_INT64_ARRAY) {
  54395             if (bf_get_int64(&v64, &p1->num, 0) != 0)
  54396                 goto done;
  54397         } else if (p->class_id == JS_CLASS_BIG_UINT64_ARRAY) {
  54398             if (bf_get_uint64((uint64_t *)&v64, &p1->num) != 0)
  54399                 goto done;
  54400         } else {
  54401             goto done;
  54402         }
  54403         d = 0;
  54404         is_bigint = 1;
  54405     } else {
  54406         goto done;
  54407     }
  54408 
  54409     switch (p->class_id) {
  54410     case JS_CLASS_INT8_ARRAY:
  54411         if (is_int && (int8_t)v64 == v64)
  54412             goto scan8;
  54413         break;
  54414     case JS_CLASS_UINT8C_ARRAY:
  54415     case JS_CLASS_UINT8_ARRAY:
  54416         if (is_int && (uint8_t)v64 == v64) {
  54417             const uint8_t *pv, *pp;
  54418             uint16_t v;
  54419         scan8:
  54420             pv = p->u.array.u.uint8_ptr;
  54421             v = v64;
  54422             if (inc > 0) {
  54423                 pp = memchr(pv + k, v, len - k);
  54424                 if (pp)
  54425                     res = pp - pv;
  54426             } else {
  54427                 for (; k != stop; k += inc) {
  54428                     if (pv[k] == v) {
  54429                         res = k;
  54430                         break;
  54431                     }
  54432                 }
  54433             }
  54434         }
  54435         break;
  54436     case JS_CLASS_INT16_ARRAY:
  54437         if (is_int && (int16_t)v64 == v64)
  54438             goto scan16;
  54439         break;
  54440     case JS_CLASS_UINT16_ARRAY:
  54441         if (is_int && (uint16_t)v64 == v64) {
  54442             const uint16_t *pv;
  54443             uint16_t v;
  54444         scan16:
  54445             pv = p->u.array.u.uint16_ptr;
  54446             v = v64;
  54447             for (; k != stop; k += inc) {
  54448                 if (pv[k] == v) {
  54449                     res = k;
  54450                     break;
  54451                 }
  54452             }
  54453         }
  54454         break;
  54455     case JS_CLASS_INT32_ARRAY:
  54456         if (is_int && (int32_t)v64 == v64)
  54457             goto scan32;
  54458         break;
  54459     case JS_CLASS_UINT32_ARRAY:
  54460         if (is_int && (uint32_t)v64 == v64) {
  54461             const uint32_t *pv;
  54462             uint32_t v;
  54463         scan32:
  54464             pv = p->u.array.u.uint32_ptr;
  54465             v = v64;
  54466             for (; k != stop; k += inc) {
  54467                 if (pv[k] == v) {
  54468                     res = k;
  54469                     break;
  54470                 }
  54471             }
  54472         }
  54473         break;
  54474     case JS_CLASS_FLOAT32_ARRAY:
  54475         if (is_bigint)
  54476             break;
  54477         if (isnan(d)) {
  54478             const float *pv = p->u.array.u.float_ptr;
  54479             /* special case: indexOf returns -1, includes finds NaN */
  54480             if (special != special_includes)
  54481                 goto done;
  54482             for (; k != stop; k += inc) {
  54483                 if (isnan(pv[k])) {
  54484                     res = k;
  54485                     break;
  54486                 }
  54487             }
  54488         } else if ((f = (float)d) == d) {
  54489             const float *pv = p->u.array.u.float_ptr;
  54490             for (; k != stop; k += inc) {
  54491                 if (pv[k] == f) {
  54492                     res = k;
  54493                     break;
  54494                 }
  54495             }
  54496         }
  54497         break;
  54498     case JS_CLASS_FLOAT64_ARRAY:
  54499         if (is_bigint)
  54500             break;
  54501         if (isnan(d)) {
  54502             const double *pv = p->u.array.u.double_ptr;
  54503             /* special case: indexOf returns -1, includes finds NaN */
  54504             if (special != special_includes)
  54505                 goto done;
  54506             for (; k != stop; k += inc) {
  54507                 if (isnan(pv[k])) {
  54508                     res = k;
  54509                     break;
  54510                 }
  54511             }
  54512         } else {
  54513             const double *pv = p->u.array.u.double_ptr;
  54514             for (; k != stop; k += inc) {
  54515                 if (pv[k] == d) {
  54516                     res = k;
  54517                     break;
  54518                 }
  54519             }
  54520         }
  54521         break;
  54522     case JS_CLASS_BIG_INT64_ARRAY:
  54523         if (is_bigint || (is_math_mode(ctx) && is_int &&
  54524                           v64 >= -MAX_SAFE_INTEGER &&
  54525                           v64 <= MAX_SAFE_INTEGER)) {
  54526             goto scan64;
  54527         }
  54528         break;
  54529     case JS_CLASS_BIG_UINT64_ARRAY:
  54530         if (is_bigint || (is_math_mode(ctx) && is_int &&
  54531                           v64 >= 0 && v64 <= MAX_SAFE_INTEGER)) {
  54532             const uint64_t *pv;
  54533             uint64_t v;
  54534         scan64:
  54535             pv = p->u.array.u.uint64_ptr;
  54536             v = v64;
  54537             for (; k != stop; k += inc) {
  54538                 if (pv[k] == v) {
  54539                     res = k;
  54540                     break;
  54541                 }
  54542             }
  54543         }
  54544         break;
  54545     }
  54546 
  54547 done:
  54548     if (special == special_includes)
  54549         return JS_NewBool(ctx, res >= 0);
  54550     else
  54551         return JS_NewInt32(ctx, res);
  54552 
  54553 exception:
  54554     return JS_EXCEPTION;
  54555 }
  54556 
  54557 static JSValue js_typed_array_join(JSContext *ctx, JSValueConst this_val,
  54558                                    int argc, JSValueConst *argv, int toLocaleString)
  54559 {
  54560     JSValue sep = JS_UNDEFINED, el;
  54561     StringBuffer b_s, *b = &b_s;
  54562     JSString *p = NULL;
  54563     int i, n;
  54564     int c;
  54565 
  54566     n = js_typed_array_get_length_internal(ctx, this_val);
  54567     if (n < 0)
  54568         goto exception;
  54569 
  54570     c = ',';    /* default separator */
  54571     if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) {
  54572         sep = JS_ToString(ctx, argv[0]);
  54573         if (JS_IsException(sep))
  54574             goto exception;
  54575         p = JS_VALUE_GET_STRING(sep);
  54576         if (p->len == 1 && !p->is_wide_char)
  54577             c = p->u.str8[0];
  54578         else
  54579             c = -1;
  54580     }
  54581     string_buffer_init(ctx, b, 0);
  54582 
  54583     /* XXX: optimize with direct access */
  54584     for(i = 0; i < n; i++) {
  54585         if (i > 0) {
  54586             if (c >= 0) {
  54587                 if (string_buffer_putc8(b, c))
  54588                     goto fail;
  54589             } else {
  54590                 if (string_buffer_concat(b, p, 0, p->len))
  54591                     goto fail;
  54592             }
  54593         }
  54594         el = JS_GetPropertyUint32(ctx, this_val, i);
  54595         /* Can return undefined for example if the typed array is detached */
  54596         if (!JS_IsNull(el) && !JS_IsUndefined(el)) {
  54597             if (JS_IsException(el))
  54598                 goto fail;
  54599             if (toLocaleString) {
  54600                 el = JS_ToLocaleStringFree(ctx, el);
  54601             }
  54602             if (string_buffer_concat_value_free(b, el))
  54603                 goto fail;
  54604         }
  54605     }
  54606     JS_FreeValue(ctx, sep);
  54607     return string_buffer_end(b);
  54608 
  54609 fail:
  54610     string_buffer_free(b);
  54611     JS_FreeValue(ctx, sep);
  54612 exception:
  54613     return JS_EXCEPTION;
  54614 }
  54615 
  54616 static JSValue js_typed_array_reverse(JSContext *ctx, JSValueConst this_val,
  54617                                       int argc, JSValueConst *argv)
  54618 {
  54619     JSObject *p;
  54620     int len;
  54621 
  54622     len = js_typed_array_get_length_internal(ctx, this_val);
  54623     if (len < 0)
  54624         return JS_EXCEPTION;
  54625     if (len > 0) {
  54626         p = JS_VALUE_GET_OBJ(this_val);
  54627         switch (typed_array_size_log2(p->class_id)) {
  54628         case 0:
  54629             {
  54630                 uint8_t *p1 = p->u.array.u.uint8_ptr;
  54631                 uint8_t *p2 = p1 + len - 1;
  54632                 while (p1 < p2) {
  54633                     uint8_t v = *p1;
  54634                     *p1++ = *p2;
  54635                     *p2-- = v;
  54636                 }
  54637             }
  54638             break;
  54639         case 1:
  54640             {
  54641                 uint16_t *p1 = p->u.array.u.uint16_ptr;
  54642                 uint16_t *p2 = p1 + len - 1;
  54643                 while (p1 < p2) {
  54644                     uint16_t v = *p1;
  54645                     *p1++ = *p2;
  54646                     *p2-- = v;
  54647                 }
  54648             }
  54649             break;
  54650         case 2:
  54651             {
  54652                 uint32_t *p1 = p->u.array.u.uint32_ptr;
  54653                 uint32_t *p2 = p1 + len - 1;
  54654                 while (p1 < p2) {
  54655                     uint32_t v = *p1;
  54656                     *p1++ = *p2;
  54657                     *p2-- = v;
  54658                 }
  54659             }
  54660             break;
  54661         case 3:
  54662             {
  54663                 uint64_t *p1 = p->u.array.u.uint64_ptr;
  54664                 uint64_t *p2 = p1 + len - 1;
  54665                 while (p1 < p2) {
  54666                     uint64_t v = *p1;
  54667                     *p1++ = *p2;
  54668                     *p2-- = v;
  54669                 }
  54670             }
  54671             break;
  54672         default:
  54673             abort();
  54674         }
  54675     }
  54676     return JS_DupValue(ctx, this_val);
  54677 }
  54678 
  54679 static JSValue js_typed_array_toReversed(JSContext *ctx, JSValueConst this_val,
  54680                                          int argc, JSValueConst *argv)
  54681 {
  54682     JSValue arr, ret;
  54683     JSObject *p;
  54684 
  54685     p = get_typed_array(ctx, this_val, /*is_dataview*/0);
  54686     if (!p)
  54687         return JS_EXCEPTION;
  54688     arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val,
  54689                                         p->class_id);
  54690     if (JS_IsException(arr))
  54691         return JS_EXCEPTION;
  54692     ret = js_typed_array_reverse(ctx, arr, argc, argv);
  54693     JS_FreeValue(ctx, arr);
  54694     return ret;
  54695 }
  54696 
  54697 static JSValue js_typed_array_slice(JSContext *ctx, JSValueConst this_val,
  54698                                     int argc, JSValueConst *argv)
  54699 {
  54700     JSValueConst args[2];
  54701     JSValue arr, val;
  54702     JSObject *p, *p1;
  54703     int n, len, start, final, count, shift;
  54704 
  54705     arr = JS_UNDEFINED;
  54706     len = js_typed_array_get_length_internal(ctx, this_val);
  54707     if (len < 0)
  54708         goto exception;
  54709 
  54710     if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len))
  54711         goto exception;
  54712     final = len;
  54713     if (!JS_IsUndefined(argv[1])) {
  54714         if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len))
  54715             goto exception;
  54716     }
  54717     count = max_int(final - start, 0);
  54718 
  54719     p = get_typed_array(ctx, this_val, 0);
  54720     if (p == NULL)
  54721         goto exception;
  54722     shift = typed_array_size_log2(p->class_id);
  54723 
  54724     args[0] = this_val;
  54725     args[1] = JS_NewInt32(ctx, count);
  54726     arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);
  54727     if (JS_IsException(arr))
  54728         goto exception;
  54729 
  54730     if (count > 0) {
  54731         if (validate_typed_array(ctx, this_val)
  54732         ||  validate_typed_array(ctx, arr))
  54733             goto exception;
  54734 
  54735         p1 = get_typed_array(ctx, arr, 0);
  54736         if (p1 != NULL && p->class_id == p1->class_id &&
  54737             typed_array_get_length(ctx, p1) >= count &&
  54738             typed_array_get_length(ctx, p) >= start + count) {
  54739             memcpy(p1->u.array.u.uint8_ptr,
  54740                    p->u.array.u.uint8_ptr + (start << shift),
  54741                    count << shift);
  54742         } else {
  54743             for (n = 0; n < count; n++) {
  54744                 val = JS_GetPropertyValue(ctx, this_val, JS_NewInt32(ctx, start + n));
  54745                 if (JS_IsException(val))
  54746                     goto exception;
  54747                 if (JS_SetPropertyValue(ctx, arr, JS_NewInt32(ctx, n), val,
  54748                                         JS_PROP_THROW) < 0)
  54749                     goto exception;
  54750             }
  54751         }
  54752     }
  54753     return arr;
  54754 
  54755  exception:
  54756     JS_FreeValue(ctx, arr);
  54757     return JS_EXCEPTION;
  54758 }
  54759 
  54760 static JSValue js_typed_array_subarray(JSContext *ctx, JSValueConst this_val,
  54761                                        int argc, JSValueConst *argv)
  54762 {
  54763     JSValueConst args[4];
  54764     JSValue arr, byteOffset, ta_buffer;
  54765     JSObject *p;
  54766     int len, start, final, count, shift, offset;
  54767 
  54768     p = get_typed_array(ctx, this_val, 0);
  54769     if (!p)
  54770         goto exception;
  54771     len = p->u.array.count;
  54772     if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len))
  54773         goto exception;
  54774 
  54775     final = len;
  54776     if (!JS_IsUndefined(argv[1])) {
  54777         if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len))
  54778             goto exception;
  54779     }
  54780     count = max_int(final - start, 0);
  54781     byteOffset = js_typed_array_get_byteOffset(ctx, this_val, 0);
  54782     if (JS_IsException(byteOffset))
  54783         goto exception;
  54784     shift = typed_array_size_log2(p->class_id);
  54785     offset = JS_VALUE_GET_INT(byteOffset) + (start << shift);
  54786     JS_FreeValue(ctx, byteOffset);
  54787     ta_buffer = js_typed_array_get_buffer(ctx, this_val, 0);
  54788     if (JS_IsException(ta_buffer))
  54789         goto exception;
  54790     args[0] = this_val;
  54791     args[1] = ta_buffer;
  54792     args[2] = JS_NewInt32(ctx, offset);
  54793     args[3] = JS_NewInt32(ctx, count);
  54794     arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 4, args);
  54795     JS_FreeValue(ctx, ta_buffer);
  54796     return arr;
  54797 
  54798  exception:
  54799     return JS_EXCEPTION;
  54800 }
  54801 
  54802 /* TypedArray.prototype.sort */
  54803 
  54804 static int js_cmp_doubles(double x, double y)
  54805 {
  54806     if (isnan(x))    return isnan(y) ? 0 : +1;
  54807     if (isnan(y))    return -1;
  54808     if (x < y)       return -1;
  54809     if (x > y)       return 1;
  54810     if (x != 0)      return 0;
  54811     if (signbit(x))  return signbit(y) ? 0 : -1;
  54812     else             return signbit(y) ? 1 : 0;
  54813 }
  54814 
  54815 static int js_TA_cmp_int8(const void *a, const void *b, void *opaque) {
  54816     return *(const int8_t *)a - *(const int8_t *)b;
  54817 }
  54818 
  54819 static int js_TA_cmp_uint8(const void *a, const void *b, void *opaque) {
  54820     return *(const uint8_t *)a - *(const uint8_t *)b;
  54821 }
  54822 
  54823 static int js_TA_cmp_int16(const void *a, const void *b, void *opaque) {
  54824     return *(const int16_t *)a - *(const int16_t *)b;
  54825 }
  54826 
  54827 static int js_TA_cmp_uint16(const void *a, const void *b, void *opaque) {
  54828     return *(const uint16_t *)a - *(const uint16_t *)b;
  54829 }
  54830 
  54831 static int js_TA_cmp_int32(const void *a, const void *b, void *opaque) {
  54832     int32_t x = *(const int32_t *)a;
  54833     int32_t y = *(const int32_t *)b;
  54834     return (y < x) - (y > x);
  54835 }
  54836 
  54837 static int js_TA_cmp_uint32(const void *a, const void *b, void *opaque) {
  54838     uint32_t x = *(const uint32_t *)a;
  54839     uint32_t y = *(const uint32_t *)b;
  54840     return (y < x) - (y > x);
  54841 }
  54842 
  54843 static int js_TA_cmp_int64(const void *a, const void *b, void *opaque) {
  54844     int64_t x = *(const int64_t *)a;
  54845     int64_t y = *(const int64_t *)b;
  54846     return (y < x) - (y > x);
  54847 }
  54848 
  54849 static int js_TA_cmp_uint64(const void *a, const void *b, void *opaque) {
  54850     uint64_t x = *(const uint64_t *)a;
  54851     uint64_t y = *(const uint64_t *)b;
  54852     return (y < x) - (y > x);
  54853 }
  54854 
  54855 static int js_TA_cmp_float32(const void *a, const void *b, void *opaque) {
  54856     return js_cmp_doubles(*(const float *)a, *(const float *)b);
  54857 }
  54858 
  54859 static int js_TA_cmp_float64(const void *a, const void *b, void *opaque) {
  54860     return js_cmp_doubles(*(const double *)a, *(const double *)b);
  54861 }
  54862 
  54863 static JSValue js_TA_get_int8(JSContext *ctx, const void *a) {
  54864     return JS_NewInt32(ctx, *(const int8_t *)a);
  54865 }
  54866 
  54867 static JSValue js_TA_get_uint8(JSContext *ctx, const void *a) {
  54868     return JS_NewInt32(ctx, *(const uint8_t *)a);
  54869 }
  54870 
  54871 static JSValue js_TA_get_int16(JSContext *ctx, const void *a) {
  54872     return JS_NewInt32(ctx, *(const int16_t *)a);
  54873 }
  54874 
  54875 static JSValue js_TA_get_uint16(JSContext *ctx, const void *a) {
  54876     return JS_NewInt32(ctx, *(const uint16_t *)a);
  54877 }
  54878 
  54879 static JSValue js_TA_get_int32(JSContext *ctx, const void *a) {
  54880     return JS_NewInt32(ctx, *(const int32_t *)a);
  54881 }
  54882 
  54883 static JSValue js_TA_get_uint32(JSContext *ctx, const void *a) {
  54884     return JS_NewUint32(ctx, *(const uint32_t *)a);
  54885 }
  54886 
  54887 static JSValue js_TA_get_int64(JSContext *ctx, const void *a) {
  54888     return JS_NewBigInt64(ctx, *(int64_t *)a);
  54889 }
  54890 
  54891 static JSValue js_TA_get_uint64(JSContext *ctx, const void *a) {
  54892     return JS_NewBigUint64(ctx, *(uint64_t *)a);
  54893 }
  54894 
  54895 static JSValue js_TA_get_float32(JSContext *ctx, const void *a) {
  54896     return __JS_NewFloat64(ctx, *(const float *)a);
  54897 }
  54898 
  54899 static JSValue js_TA_get_float64(JSContext *ctx, const void *a) {
  54900     return __JS_NewFloat64(ctx, *(const double *)a);
  54901 }
  54902 
  54903 struct TA_sort_context {
  54904     JSContext *ctx;
  54905     int exception; /* 1 = exception, 2 = detached typed array */
  54906     JSValueConst arr;
  54907     JSValueConst cmp;
  54908     JSValue (*getfun)(JSContext *ctx, const void *a);
  54909     uint8_t *array_ptr; /* cannot change unless the array is detached */
  54910     int elt_size;
  54911 };
  54912 
  54913 static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) {
  54914     struct TA_sort_context *psc = opaque;
  54915     JSContext *ctx = psc->ctx;
  54916     uint32_t a_idx, b_idx;
  54917     JSValueConst argv[2];
  54918     JSValue res;
  54919     int cmp;
  54920 
  54921     cmp = 0;
  54922     if (!psc->exception) {
  54923         /* Note: the typed array can be detached without causing an
  54924            error */
  54925         a_idx = *(uint32_t *)a;
  54926         b_idx = *(uint32_t *)b;
  54927         argv[0] = psc->getfun(ctx, psc->array_ptr +
  54928                               a_idx * (size_t)psc->elt_size);
  54929         argv[1] = psc->getfun(ctx, psc->array_ptr +
  54930                               b_idx * (size_t)(psc->elt_size));
  54931         res = JS_Call(ctx, psc->cmp, JS_UNDEFINED, 2, argv);
  54932         if (JS_IsException(res)) {
  54933             psc->exception = 1;
  54934             goto done;
  54935         }
  54936         if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) {
  54937             int val = JS_VALUE_GET_INT(res);
  54938             cmp = (val > 0) - (val < 0);
  54939         } else {
  54940             double val;
  54941             if (JS_ToFloat64Free(ctx, &val, res) < 0) {
  54942                 psc->exception = 1;
  54943                 goto done;
  54944             } else {
  54945                 cmp = (val > 0) - (val < 0);
  54946             }
  54947         }
  54948         if (cmp == 0) {
  54949             /* make sort stable: compare array offsets */
  54950             cmp = (a_idx > b_idx) - (a_idx < b_idx);
  54951         }
  54952         if (unlikely(typed_array_is_detached(ctx,
  54953                                              JS_VALUE_GET_PTR(psc->arr)))) {
  54954             psc->exception = 2;
  54955         }
  54956     done:
  54957         JS_FreeValue(ctx, (JSValue)argv[0]);
  54958         JS_FreeValue(ctx, (JSValue)argv[1]);
  54959     }
  54960     return cmp;
  54961 }
  54962 
  54963 static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val,
  54964                                    int argc, JSValueConst *argv)
  54965 {
  54966     JSObject *p;
  54967     int len;
  54968     size_t elt_size;
  54969     struct TA_sort_context tsc;
  54970     void *array_ptr;
  54971     int (*cmpfun)(const void *a, const void *b, void *opaque);
  54972 
  54973     tsc.ctx = ctx;
  54974     tsc.exception = 0;
  54975     tsc.arr = this_val;
  54976     tsc.cmp = argv[0];
  54977 
  54978     if (!JS_IsUndefined(tsc.cmp) && check_function(ctx, tsc.cmp))
  54979         return JS_EXCEPTION;
  54980     len = js_typed_array_get_length_internal(ctx, this_val);
  54981     if (len < 0)
  54982         return JS_EXCEPTION;
  54983 
  54984     if (len > 1) {
  54985         p = JS_VALUE_GET_OBJ(this_val);
  54986         switch (p->class_id) {
  54987         case JS_CLASS_INT8_ARRAY:
  54988             tsc.getfun = js_TA_get_int8;
  54989             cmpfun = js_TA_cmp_int8;
  54990             break;
  54991         case JS_CLASS_UINT8C_ARRAY:
  54992         case JS_CLASS_UINT8_ARRAY:
  54993             tsc.getfun = js_TA_get_uint8;
  54994             cmpfun = js_TA_cmp_uint8;
  54995             break;
  54996         case JS_CLASS_INT16_ARRAY:
  54997             tsc.getfun = js_TA_get_int16;
  54998             cmpfun = js_TA_cmp_int16;
  54999             break;
  55000         case JS_CLASS_UINT16_ARRAY:
  55001             tsc.getfun = js_TA_get_uint16;
  55002             cmpfun = js_TA_cmp_uint16;
  55003             break;
  55004         case JS_CLASS_INT32_ARRAY:
  55005             tsc.getfun = js_TA_get_int32;
  55006             cmpfun = js_TA_cmp_int32;
  55007             break;
  55008         case JS_CLASS_UINT32_ARRAY:
  55009             tsc.getfun = js_TA_get_uint32;
  55010             cmpfun = js_TA_cmp_uint32;
  55011             break;
  55012         case JS_CLASS_BIG_INT64_ARRAY:
  55013             tsc.getfun = js_TA_get_int64;
  55014             cmpfun = js_TA_cmp_int64;
  55015             break;
  55016         case JS_CLASS_BIG_UINT64_ARRAY:
  55017             tsc.getfun = js_TA_get_uint64;
  55018             cmpfun = js_TA_cmp_uint64;
  55019             break;
  55020         case JS_CLASS_FLOAT32_ARRAY:
  55021             tsc.getfun = js_TA_get_float32;
  55022             cmpfun = js_TA_cmp_float32;
  55023             break;
  55024         case JS_CLASS_FLOAT64_ARRAY:
  55025             tsc.getfun = js_TA_get_float64;
  55026             cmpfun = js_TA_cmp_float64;
  55027             break;
  55028         default:
  55029             abort();
  55030         }
  55031         array_ptr = p->u.array.u.ptr;
  55032         elt_size = 1 << typed_array_size_log2(p->class_id);
  55033         if (!JS_IsUndefined(tsc.cmp)) {
  55034             uint32_t *array_idx;
  55035             void *array_tmp;
  55036             size_t i, j;
  55037 
  55038             /* XXX: a stable sort would use less memory */
  55039             array_idx = js_malloc(ctx, len * sizeof(array_idx[0]));
  55040             if (!array_idx)
  55041                 return JS_EXCEPTION;
  55042             for(i = 0; i < len; i++)
  55043                 array_idx[i] = i;
  55044             tsc.array_ptr = array_ptr;
  55045             tsc.elt_size = elt_size;
  55046             rqsort(array_idx, len, sizeof(array_idx[0]),
  55047                    js_TA_cmp_generic, &tsc);
  55048             if (tsc.exception) {
  55049                 if (tsc.exception == 1)
  55050                     goto fail;
  55051                 /* detached typed array during the sort: no error */
  55052             } else {
  55053                 array_tmp = js_malloc(ctx, len * elt_size);
  55054                 if (!array_tmp) {
  55055                 fail:
  55056                     js_free(ctx, array_idx);
  55057                     return JS_EXCEPTION;
  55058                 }
  55059                 memcpy(array_tmp, array_ptr, len * elt_size);
  55060                 switch(elt_size) {
  55061                 case 1:
  55062                     for(i = 0; i < len; i++) {
  55063                         j = array_idx[i];
  55064                         ((uint8_t *)array_ptr)[i] = ((uint8_t *)array_tmp)[j];
  55065                     }
  55066                     break;
  55067                 case 2:
  55068                     for(i = 0; i < len; i++) {
  55069                         j = array_idx[i];
  55070                         ((uint16_t *)array_ptr)[i] = ((uint16_t *)array_tmp)[j];
  55071                     }
  55072                     break;
  55073                 case 4:
  55074                     for(i = 0; i < len; i++) {
  55075                         j = array_idx[i];
  55076                         ((uint32_t *)array_ptr)[i] = ((uint32_t *)array_tmp)[j];
  55077                     }
  55078                     break;
  55079                 case 8:
  55080                     for(i = 0; i < len; i++) {
  55081                         j = array_idx[i];
  55082                         ((uint64_t *)array_ptr)[i] = ((uint64_t *)array_tmp)[j];
  55083                     }
  55084                     break;
  55085                 default:
  55086                     abort();
  55087                 }
  55088                 js_free(ctx, array_tmp);
  55089             }
  55090             js_free(ctx, array_idx);
  55091         } else {
  55092             rqsort(array_ptr, len, elt_size, cmpfun, &tsc);
  55093             if (tsc.exception)
  55094                 return JS_EXCEPTION;
  55095         }
  55096     }
  55097     return JS_DupValue(ctx, this_val);
  55098 }
  55099 
  55100 static JSValue js_typed_array_toSorted(JSContext *ctx, JSValueConst this_val,
  55101                                        int argc, JSValueConst *argv)
  55102 {
  55103     JSValue arr, ret;
  55104     JSObject *p;
  55105 
  55106     p = get_typed_array(ctx, this_val, /*is_dataview*/0);
  55107     if (!p)
  55108         return JS_EXCEPTION;
  55109     arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val,
  55110                                         p->class_id);
  55111     if (JS_IsException(arr))
  55112         return JS_EXCEPTION;
  55113     ret = js_typed_array_sort(ctx, arr, argc, argv);
  55114     JS_FreeValue(ctx, arr);
  55115     return ret;
  55116 }
  55117 
  55118 static const JSCFunctionListEntry js_typed_array_base_funcs[] = {
  55119     JS_CFUNC_DEF("from", 1, js_typed_array_from ),
  55120     JS_CFUNC_DEF("of", 0, js_typed_array_of ),
  55121     JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
  55122     //JS_CFUNC_DEF("__getLength", 2, js_typed_array___getLength ),
  55123     //JS_CFUNC_DEF("__create", 2, js_typed_array___create ),
  55124     //JS_CFUNC_DEF("__speciesCreate", 2, js_typed_array___speciesCreate ),
  55125 };
  55126 
  55127 static const JSCFunctionListEntry js_typed_array_base_proto_funcs[] = {
  55128     JS_CGETSET_DEF("length", js_typed_array_get_length, NULL ),
  55129     JS_CFUNC_DEF("at", 1, js_typed_array_at ),
  55130     JS_CFUNC_DEF("with", 2, js_typed_array_with ),
  55131     JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 0 ),
  55132     JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 0 ),
  55133     JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 0 ),
  55134     JS_CFUNC_DEF("set", 1, js_typed_array_set ),
  55135     JS_CFUNC_MAGIC_DEF("values", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_VALUE ),
  55136     JS_ALIAS_DEF("[Symbol.iterator]", "values" ),
  55137     JS_CFUNC_MAGIC_DEF("keys", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY ),
  55138     JS_CFUNC_MAGIC_DEF("entries", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ),
  55139     JS_CGETSET_DEF("[Symbol.toStringTag]", js_typed_array_get_toStringTag, NULL ),
  55140     JS_CFUNC_DEF("copyWithin", 2, js_typed_array_copyWithin ),
  55141     JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every | special_TA ),
  55142     JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some | special_TA ),
  55143     JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach | special_TA ),
  55144     JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map | special_TA ),
  55145     JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter | special_TA ),
  55146     JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce | special_TA ),
  55147     JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight | special_TA ),
  55148     JS_CFUNC_DEF("fill", 1, js_typed_array_fill ),
  55149     JS_CFUNC_MAGIC_DEF("find", 1, js_typed_array_find, ArrayFind ),
  55150     JS_CFUNC_MAGIC_DEF("findIndex", 1, js_typed_array_find, ArrayFindIndex ),
  55151     JS_CFUNC_MAGIC_DEF("findLast", 1, js_typed_array_find, ArrayFindLast ),
  55152     JS_CFUNC_MAGIC_DEF("findLastIndex", 1, js_typed_array_find, ArrayFindLastIndex ),
  55153     JS_CFUNC_DEF("reverse", 0, js_typed_array_reverse ),
  55154     JS_CFUNC_DEF("toReversed", 0, js_typed_array_toReversed ),
  55155     JS_CFUNC_DEF("slice", 2, js_typed_array_slice ),
  55156     JS_CFUNC_DEF("subarray", 2, js_typed_array_subarray ),
  55157     JS_CFUNC_DEF("sort", 1, js_typed_array_sort ),
  55158     JS_CFUNC_DEF("toSorted", 1, js_typed_array_toSorted ),
  55159     JS_CFUNC_MAGIC_DEF("join", 1, js_typed_array_join, 0 ),
  55160     JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_typed_array_join, 1 ),
  55161     JS_CFUNC_MAGIC_DEF("indexOf", 1, js_typed_array_indexOf, special_indexOf ),
  55162     JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_typed_array_indexOf, special_lastIndexOf ),
  55163     JS_CFUNC_MAGIC_DEF("includes", 1, js_typed_array_indexOf, special_includes ),
  55164     //JS_ALIAS_BASE_DEF("toString", "toString", 2 /* Array.prototype. */), @@@
  55165 };
  55166 
  55167 static JSValue js_typed_array_base_constructor(JSContext *ctx,
  55168                                                JSValueConst this_val,
  55169                                                int argc, JSValueConst *argv)
  55170 {
  55171     return JS_ThrowTypeError(ctx, "cannot be called");
  55172 }
  55173 
  55174 /* 'obj' must be an allocated typed array object */
  55175 static int typed_array_init(JSContext *ctx, JSValueConst obj,
  55176                             JSValue buffer, uint64_t offset, uint64_t len)
  55177 {
  55178     JSTypedArray *ta;
  55179     JSObject *p, *pbuffer;
  55180     JSArrayBuffer *abuf;
  55181     int size_log2;
  55182 
  55183     p = JS_VALUE_GET_OBJ(obj);
  55184     size_log2 = typed_array_size_log2(p->class_id);
  55185     ta = js_malloc(ctx, sizeof(*ta));
  55186     if (!ta) {
  55187         JS_FreeValue(ctx, buffer);
  55188         return -1;
  55189     }
  55190     pbuffer = JS_VALUE_GET_OBJ(buffer);
  55191     abuf = pbuffer->u.array_buffer;
  55192     ta->obj = p;
  55193     ta->buffer = pbuffer;
  55194     ta->offset = offset;
  55195     ta->length = len << size_log2;
  55196     list_add_tail(&ta->link, &abuf->array_list);
  55197     p->u.typed_array = ta;
  55198     p->u.array.count = len;
  55199     p->u.array.u.ptr = abuf->data + offset;
  55200     return 0;
  55201 }
  55202 
  55203 JSValue JS_NewTypedArraySimple(JSContext *ctx, JSValue array_buf, size_t bytes_per_element)
  55204 {
  55205     JSValue obj;
  55206     JSObject *p = JS_VALUE_GET_OBJ(array_buf);
  55207     JSArrayBuffer *abuf = NULL;
  55208 
  55209     if (p->class_id != JS_CLASS_ARRAY_BUFFER) {
  55210         return JS_ThrowTypeError(ctx, "expected array buffer");
  55211     }
  55212     abuf = p->u.array_buffer;
  55213     if (abuf->detached) {
  55214         return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  55215     }
  55216     obj = JS_NewObjectClass(ctx, JS_CLASS_UINT8_ARRAY);
  55217     if (typed_array_init(ctx, obj, array_buf, 0, abuf->byte_length)) {
  55218         JS_FreeValue(ctx, obj);
  55219         return JS_EXCEPTION;
  55220     }
  55221     return obj;
  55222 }
  55223 
  55224 
  55225 static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen,
  55226                                       JSValueConst obj, JSValueConst method)
  55227 {
  55228     JSValue arr, iter, next_method = JS_UNDEFINED, val;
  55229     BOOL done;
  55230     uint32_t k;
  55231 
  55232     *plen = 0;
  55233     arr = JS_NewArray(ctx);
  55234     if (JS_IsException(arr))
  55235         return arr;
  55236     iter = JS_GetIterator2(ctx, obj, method);
  55237     if (JS_IsException(iter))
  55238         goto fail;
  55239     next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
  55240     if (JS_IsException(next_method))
  55241         goto fail;
  55242     k = 0;
  55243     for(;;) {
  55244         val = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
  55245         if (JS_IsException(val))
  55246             goto fail;
  55247         if (done) {
  55248             JS_FreeValue(ctx, val);
  55249             break;
  55250         }
  55251         if (JS_CreateDataPropertyUint32(ctx, arr, k, val, JS_PROP_THROW) < 0)
  55252             goto fail;
  55253         k++;
  55254     }
  55255     JS_FreeValue(ctx, next_method);
  55256     JS_FreeValue(ctx, iter);
  55257     *plen = k;
  55258     return arr;
  55259  fail:
  55260     JS_FreeValue(ctx, next_method);
  55261     JS_FreeValue(ctx, iter);
  55262     JS_FreeValue(ctx, arr);
  55263     return JS_EXCEPTION;
  55264 }
  55265 
  55266 static JSValue js_typed_array_constructor_obj(JSContext *ctx,
  55267                                               JSValueConst new_target,
  55268                                               JSValueConst obj,
  55269                                               int classid)
  55270 {
  55271     JSValue iter, ret, arr = JS_UNDEFINED, val, buffer;
  55272     uint32_t i;
  55273     int size_log2;
  55274     int64_t len;
  55275 
  55276     size_log2 = typed_array_size_log2(classid);
  55277     ret = js_create_from_ctor(ctx, new_target, classid);
  55278     if (JS_IsException(ret))
  55279         return JS_EXCEPTION;
  55280 
  55281     iter = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);
  55282     if (JS_IsException(iter))
  55283         goto fail;
  55284     if (!JS_IsUndefined(iter) && !JS_IsNull(iter)) {
  55285         uint32_t len1;
  55286         arr = js_array_from_iterator(ctx, &len1, obj, iter);
  55287         JS_FreeValue(ctx, iter);
  55288         if (JS_IsException(arr))
  55289             goto fail;
  55290         len = len1;
  55291     } else {
  55292         if (js_get_length64(ctx, &len, obj))
  55293             goto fail;
  55294         arr = JS_DupValue(ctx, obj);
  55295     }
  55296 
  55297     buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED,
  55298                                           len << size_log2);
  55299     if (JS_IsException(buffer))
  55300         goto fail;
  55301     if (typed_array_init(ctx, ret, buffer, 0, len))
  55302         goto fail;
  55303 
  55304     for(i = 0; i < len; i++) {
  55305         val = JS_GetPropertyUint32(ctx, arr, i);
  55306         if (JS_IsException(val))
  55307             goto fail;
  55308         if (JS_SetPropertyUint32(ctx, ret, i, val) < 0)
  55309             goto fail;
  55310     }
  55311     JS_FreeValue(ctx, arr);
  55312     return ret;
  55313  fail:
  55314     JS_FreeValue(ctx, arr);
  55315     JS_FreeValue(ctx, ret);
  55316     return JS_EXCEPTION;
  55317 }
  55318 
  55319 static JSValue js_typed_array_constructor_ta(JSContext *ctx,
  55320                                              JSValueConst new_target,
  55321                                              JSValueConst src_obj,
  55322                                              int classid)
  55323 {
  55324     JSObject *p, *src_buffer;
  55325     JSTypedArray *ta;
  55326     JSValue obj, buffer;
  55327     uint32_t len, i;
  55328     int size_log2;
  55329     JSArrayBuffer *src_abuf, *abuf;
  55330 
  55331     obj = js_create_from_ctor(ctx, new_target, classid);
  55332     if (JS_IsException(obj))
  55333         return obj;
  55334     p = JS_VALUE_GET_OBJ(src_obj);
  55335     if (typed_array_is_detached(ctx, p)) {
  55336         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  55337         goto fail;
  55338     }
  55339     ta = p->u.typed_array;
  55340     len = p->u.array.count;
  55341     src_buffer = ta->buffer;
  55342     src_abuf = src_buffer->u.array_buffer;
  55343     size_log2 = typed_array_size_log2(classid);
  55344     buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED,
  55345                                           (uint64_t)len << size_log2);
  55346     if (JS_IsException(buffer))
  55347         goto fail;
  55348     /* necessary because it could have been detached */
  55349     if (typed_array_is_detached(ctx, p)) {
  55350         JS_FreeValue(ctx, buffer);
  55351         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  55352         goto fail;
  55353     }
  55354     abuf = JS_GetOpaque(buffer, JS_CLASS_ARRAY_BUFFER);
  55355     if (typed_array_init(ctx, obj, buffer, 0, len))
  55356         goto fail;
  55357     if (p->class_id == classid) {
  55358         /* same type: copy the content */
  55359         memcpy(abuf->data, src_abuf->data + ta->offset, abuf->byte_length);
  55360     } else {
  55361         for(i = 0; i < len; i++) {
  55362             JSValue val;
  55363             val = JS_GetPropertyUint32(ctx, src_obj, i);
  55364             if (JS_IsException(val))
  55365                 goto fail;
  55366             if (JS_SetPropertyUint32(ctx, obj, i, val) < 0)
  55367                 goto fail;
  55368         }
  55369     }
  55370     return obj;
  55371  fail:
  55372     JS_FreeValue(ctx, obj);
  55373     return JS_EXCEPTION;
  55374 }
  55375 
  55376 static JSValue js_typed_array_constructor(JSContext *ctx,
  55377                                           JSValueConst new_target,
  55378                                           int argc, JSValueConst *argv,
  55379                                           int classid)
  55380 {
  55381     JSValue buffer, obj;
  55382     JSArrayBuffer *abuf;
  55383     int size_log2;
  55384     uint64_t len, offset;
  55385 
  55386     size_log2 = typed_array_size_log2(classid);
  55387     if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) {
  55388         if (JS_ToIndex(ctx, &len, argv[0]))
  55389             return JS_EXCEPTION;
  55390         buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED,
  55391                                               len << size_log2);
  55392         if (JS_IsException(buffer))
  55393             return JS_EXCEPTION;
  55394         offset = 0;
  55395     } else {
  55396         JSObject *p = JS_VALUE_GET_OBJ(argv[0]);
  55397         if (p->class_id == JS_CLASS_ARRAY_BUFFER ||
  55398             p->class_id == JS_CLASS_SHARED_ARRAY_BUFFER) {
  55399             abuf = p->u.array_buffer;
  55400             if (JS_ToIndex(ctx, &offset, argv[1]))
  55401                 return JS_EXCEPTION;
  55402             if (abuf->detached)
  55403                 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  55404             if ((offset & ((1 << size_log2) - 1)) != 0 ||
  55405                 offset > abuf->byte_length)
  55406                 return JS_ThrowRangeError(ctx, "invalid offset");
  55407             if (JS_IsUndefined(argv[2])) {
  55408                 if ((abuf->byte_length & ((1 << size_log2) - 1)) != 0)
  55409                     goto invalid_length;
  55410                 len = (abuf->byte_length - offset) >> size_log2;
  55411             } else {
  55412                 if (JS_ToIndex(ctx, &len, argv[2]))
  55413                     return JS_EXCEPTION;
  55414                 if (abuf->detached)
  55415                     return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  55416                 if ((offset + (len << size_log2)) > abuf->byte_length) {
  55417                 invalid_length:
  55418                     return JS_ThrowRangeError(ctx, "invalid length");
  55419                 }
  55420             }
  55421             buffer = JS_DupValue(ctx, argv[0]);
  55422         } else {
  55423             if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
  55424                 p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
  55425                 return js_typed_array_constructor_ta(ctx, new_target, argv[0], classid);
  55426             } else {
  55427                 return js_typed_array_constructor_obj(ctx, new_target, argv[0], classid);
  55428             }
  55429         }
  55430     }
  55431 
  55432     obj = js_create_from_ctor(ctx, new_target, classid);
  55433     if (JS_IsException(obj)) {
  55434         JS_FreeValue(ctx, buffer);
  55435         return JS_EXCEPTION;
  55436     }
  55437     if (typed_array_init(ctx, obj, buffer, offset, len)) {
  55438         JS_FreeValue(ctx, obj);
  55439         return JS_EXCEPTION;
  55440     }
  55441     return obj;
  55442 }
  55443 
  55444 static void js_typed_array_finalizer(JSRuntime *rt, JSValue val)
  55445 {
  55446     JSObject *p = JS_VALUE_GET_OBJ(val);
  55447     JSTypedArray *ta = p->u.typed_array;
  55448     if (ta) {
  55449         /* during the GC the finalizers are called in an arbitrary
  55450            order so the ArrayBuffer finalizer may have been called */
  55451         if (ta->link.next) {
  55452             list_del(&ta->link);
  55453         }
  55454         JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer));
  55455         js_free_rt(rt, ta);
  55456     }
  55457 }
  55458 
  55459 static void js_typed_array_mark(JSRuntime *rt, JSValueConst val,
  55460                                 JS_MarkFunc *mark_func)
  55461 {
  55462     JSObject *p = JS_VALUE_GET_OBJ(val);
  55463     JSTypedArray *ta = p->u.typed_array;
  55464     if (ta) {
  55465         JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer), mark_func);
  55466     }
  55467 }
  55468 
  55469 static JSValue js_dataview_constructor(JSContext *ctx,
  55470                                        JSValueConst new_target,
  55471                                        int argc, JSValueConst *argv)
  55472 {
  55473     JSArrayBuffer *abuf;
  55474     uint64_t offset;
  55475     uint32_t len;
  55476     JSValueConst buffer;
  55477     JSValue obj;
  55478     JSTypedArray *ta;
  55479     JSObject *p;
  55480 
  55481     buffer = argv[0];
  55482     abuf = js_get_array_buffer(ctx, buffer);
  55483     if (!abuf)
  55484         return JS_EXCEPTION;
  55485     offset = 0;
  55486     if (argc > 1) {
  55487         if (JS_ToIndex(ctx, &offset, argv[1]))
  55488             return JS_EXCEPTION;
  55489     }
  55490     if (abuf->detached)
  55491         return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  55492     if (offset > abuf->byte_length)
  55493         return JS_ThrowRangeError(ctx, "invalid byteOffset");
  55494     len = abuf->byte_length - offset;
  55495     if (argc > 2 && !JS_IsUndefined(argv[2])) {
  55496         uint64_t l;
  55497         if (JS_ToIndex(ctx, &l, argv[2]))
  55498             return JS_EXCEPTION;
  55499         if (l > len)
  55500             return JS_ThrowRangeError(ctx, "invalid byteLength");
  55501         len = l;
  55502     }
  55503 
  55504     obj = js_create_from_ctor(ctx, new_target, JS_CLASS_DATAVIEW);
  55505     if (JS_IsException(obj))
  55506         return JS_EXCEPTION;
  55507     if (abuf->detached) {
  55508         /* could have been detached in js_create_from_ctor() */
  55509         JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  55510         goto fail;
  55511     }
  55512     ta = js_malloc(ctx, sizeof(*ta));
  55513     if (!ta) {
  55514     fail:
  55515         JS_FreeValue(ctx, obj);
  55516         return JS_EXCEPTION;
  55517     }
  55518     p = JS_VALUE_GET_OBJ(obj);
  55519     ta->obj = p;
  55520     ta->buffer = JS_VALUE_GET_OBJ(JS_DupValue(ctx, buffer));
  55521     ta->offset = offset;
  55522     ta->length = len;
  55523     list_add_tail(&ta->link, &abuf->array_list);
  55524     p->u.typed_array = ta;
  55525     return obj;
  55526 }
  55527 
  55528 static JSValue js_dataview_getValue(JSContext *ctx,
  55529                                     JSValueConst this_obj,
  55530                                     int argc, JSValueConst *argv, int class_id)
  55531 {
  55532     JSTypedArray *ta;
  55533     JSArrayBuffer *abuf;
  55534     BOOL littleEndian, is_swap;
  55535     int size;
  55536     uint8_t *ptr;
  55537     uint32_t v;
  55538     uint64_t pos;
  55539 
  55540     ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW);
  55541     if (!ta)
  55542         return JS_EXCEPTION;
  55543     size = 1 << typed_array_size_log2(class_id);
  55544     if (JS_ToIndex(ctx, &pos, argv[0]))
  55545         return JS_EXCEPTION;
  55546     littleEndian = argc > 1 && JS_ToBool(ctx, argv[1]);
  55547     is_swap = littleEndian ^ !is_be();
  55548     abuf = ta->buffer->u.array_buffer;
  55549     if (abuf->detached)
  55550         return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  55551     if ((pos + size) > ta->length)
  55552         return JS_ThrowRangeError(ctx, "out of bound");
  55553     ptr = abuf->data + ta->offset + pos;
  55554 
  55555     switch(class_id) {
  55556     case JS_CLASS_INT8_ARRAY:
  55557         return JS_NewInt32(ctx, *(int8_t *)ptr);
  55558     case JS_CLASS_UINT8_ARRAY:
  55559         return JS_NewInt32(ctx, *(uint8_t *)ptr);
  55560     case JS_CLASS_INT16_ARRAY:
  55561         v = get_u16(ptr);
  55562         if (is_swap)
  55563             v = bswap16(v);
  55564         return JS_NewInt32(ctx, (int16_t)v);
  55565     case JS_CLASS_UINT16_ARRAY:
  55566         v = get_u16(ptr);
  55567         if (is_swap)
  55568             v = bswap16(v);
  55569         return JS_NewInt32(ctx, v);
  55570     case JS_CLASS_INT32_ARRAY:
  55571         v = get_u32(ptr);
  55572         if (is_swap)
  55573             v = bswap32(v);
  55574         return JS_NewInt32(ctx, v);
  55575     case JS_CLASS_UINT32_ARRAY:
  55576         v = get_u32(ptr);
  55577         if (is_swap)
  55578             v = bswap32(v);
  55579         return JS_NewUint32(ctx, v);
  55580     case JS_CLASS_BIG_INT64_ARRAY:
  55581         {
  55582             uint64_t v;
  55583             v = get_u64(ptr);
  55584             if (is_swap)
  55585                 v = bswap64(v);
  55586             return JS_NewBigInt64(ctx, v);
  55587         }
  55588         break;
  55589     case JS_CLASS_BIG_UINT64_ARRAY:
  55590         {
  55591             uint64_t v;
  55592             v = get_u64(ptr);
  55593             if (is_swap)
  55594                 v = bswap64(v);
  55595             return JS_NewBigUint64(ctx, v);
  55596         }
  55597         break;
  55598     case JS_CLASS_FLOAT32_ARRAY:
  55599         {
  55600             union {
  55601                 float f;
  55602                 uint32_t i;
  55603             } u;
  55604             v = get_u32(ptr);
  55605             if (is_swap)
  55606                 v = bswap32(v);
  55607             u.i = v;
  55608             return __JS_NewFloat64(ctx, u.f);
  55609         }
  55610     case JS_CLASS_FLOAT64_ARRAY:
  55611         {
  55612             union {
  55613                 double f;
  55614                 uint64_t i;
  55615             } u;
  55616             u.i = get_u64(ptr);
  55617             if (is_swap)
  55618                 u.i = bswap64(u.i);
  55619             return __JS_NewFloat64(ctx, u.f);
  55620         }
  55621     default:
  55622         abort();
  55623     }
  55624 }
  55625 
  55626 static JSValue js_dataview_setValue(JSContext *ctx,
  55627                                     JSValueConst this_obj,
  55628                                     int argc, JSValueConst *argv, int class_id)
  55629 {
  55630     JSTypedArray *ta;
  55631     JSArrayBuffer *abuf;
  55632     BOOL littleEndian, is_swap;
  55633     int size;
  55634     uint8_t *ptr;
  55635     uint64_t v64;
  55636     uint32_t v;
  55637     uint64_t pos;
  55638     JSValueConst val;
  55639 
  55640     ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW);
  55641     if (!ta)
  55642         return JS_EXCEPTION;
  55643     size = 1 << typed_array_size_log2(class_id);
  55644     if (JS_ToIndex(ctx, &pos, argv[0]))
  55645         return JS_EXCEPTION;
  55646     val = argv[1];
  55647     v = 0; /* avoid warning */
  55648     v64 = 0; /* avoid warning */
  55649     if (class_id <= JS_CLASS_UINT32_ARRAY) {
  55650         if (JS_ToUint32(ctx, &v, val))
  55651             return JS_EXCEPTION;
  55652     } else if (class_id <= JS_CLASS_BIG_UINT64_ARRAY) {
  55653         if (JS_ToBigInt64(ctx, (int64_t *)&v64, val))
  55654             return JS_EXCEPTION;
  55655     } else {
  55656         double d;
  55657         if (JS_ToFloat64(ctx, &d, val))
  55658             return JS_EXCEPTION;
  55659         if (class_id == JS_CLASS_FLOAT32_ARRAY) {
  55660             union {
  55661                 float f;
  55662                 uint32_t i;
  55663             } u;
  55664             u.f = d;
  55665             v = u.i;
  55666         } else {
  55667             JSFloat64Union u;
  55668             u.d = d;
  55669             v64 = u.u64;
  55670         }
  55671     }
  55672     littleEndian = argc > 2 && JS_ToBool(ctx, argv[2]);
  55673     is_swap = littleEndian ^ !is_be();
  55674     abuf = ta->buffer->u.array_buffer;
  55675     if (abuf->detached)
  55676         return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  55677     if ((pos + size) > ta->length)
  55678         return JS_ThrowRangeError(ctx, "out of bound");
  55679     ptr = abuf->data + ta->offset + pos;
  55680 
  55681     switch(class_id) {
  55682     case JS_CLASS_INT8_ARRAY:
  55683     case JS_CLASS_UINT8_ARRAY:
  55684         *ptr = v;
  55685         break;
  55686     case JS_CLASS_INT16_ARRAY:
  55687     case JS_CLASS_UINT16_ARRAY:
  55688         if (is_swap)
  55689             v = bswap16(v);
  55690         put_u16(ptr, v);
  55691         break;
  55692     case JS_CLASS_INT32_ARRAY:
  55693     case JS_CLASS_UINT32_ARRAY:
  55694     case JS_CLASS_FLOAT32_ARRAY:
  55695         if (is_swap)
  55696             v = bswap32(v);
  55697         put_u32(ptr, v);
  55698         break;
  55699     case JS_CLASS_BIG_INT64_ARRAY:
  55700     case JS_CLASS_BIG_UINT64_ARRAY:
  55701     case JS_CLASS_FLOAT64_ARRAY:
  55702         if (is_swap)
  55703             v64 = bswap64(v64);
  55704         put_u64(ptr, v64);
  55705         break;
  55706     default:
  55707         abort();
  55708     }
  55709     return JS_UNDEFINED;
  55710 }
  55711 
  55712 static const JSCFunctionListEntry js_dataview_proto_funcs[] = {
  55713     JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 1 ),
  55714     JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 1 ),
  55715     JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 1 ),
  55716     JS_CFUNC_MAGIC_DEF("getInt8", 1, js_dataview_getValue, JS_CLASS_INT8_ARRAY ),
  55717     JS_CFUNC_MAGIC_DEF("getUint8", 1, js_dataview_getValue, JS_CLASS_UINT8_ARRAY ),
  55718     JS_CFUNC_MAGIC_DEF("getInt16", 1, js_dataview_getValue, JS_CLASS_INT16_ARRAY ),
  55719     JS_CFUNC_MAGIC_DEF("getUint16", 1, js_dataview_getValue, JS_CLASS_UINT16_ARRAY ),
  55720     JS_CFUNC_MAGIC_DEF("getInt32", 1, js_dataview_getValue, JS_CLASS_INT32_ARRAY ),
  55721     JS_CFUNC_MAGIC_DEF("getUint32", 1, js_dataview_getValue, JS_CLASS_UINT32_ARRAY ),
  55722     JS_CFUNC_MAGIC_DEF("getBigInt64", 1, js_dataview_getValue, JS_CLASS_BIG_INT64_ARRAY ),
  55723     JS_CFUNC_MAGIC_DEF("getBigUint64", 1, js_dataview_getValue, JS_CLASS_BIG_UINT64_ARRAY ),
  55724     JS_CFUNC_MAGIC_DEF("getFloat32", 1, js_dataview_getValue, JS_CLASS_FLOAT32_ARRAY ),
  55725     JS_CFUNC_MAGIC_DEF("getFloat64", 1, js_dataview_getValue, JS_CLASS_FLOAT64_ARRAY ),
  55726     JS_CFUNC_MAGIC_DEF("setInt8", 2, js_dataview_setValue, JS_CLASS_INT8_ARRAY ),
  55727     JS_CFUNC_MAGIC_DEF("setUint8", 2, js_dataview_setValue, JS_CLASS_UINT8_ARRAY ),
  55728     JS_CFUNC_MAGIC_DEF("setInt16", 2, js_dataview_setValue, JS_CLASS_INT16_ARRAY ),
  55729     JS_CFUNC_MAGIC_DEF("setUint16", 2, js_dataview_setValue, JS_CLASS_UINT16_ARRAY ),
  55730     JS_CFUNC_MAGIC_DEF("setInt32", 2, js_dataview_setValue, JS_CLASS_INT32_ARRAY ),
  55731     JS_CFUNC_MAGIC_DEF("setUint32", 2, js_dataview_setValue, JS_CLASS_UINT32_ARRAY ),
  55732     JS_CFUNC_MAGIC_DEF("setBigInt64", 2, js_dataview_setValue, JS_CLASS_BIG_INT64_ARRAY ),
  55733     JS_CFUNC_MAGIC_DEF("setBigUint64", 2, js_dataview_setValue, JS_CLASS_BIG_UINT64_ARRAY ),
  55734     JS_CFUNC_MAGIC_DEF("setFloat32", 2, js_dataview_setValue, JS_CLASS_FLOAT32_ARRAY ),
  55735     JS_CFUNC_MAGIC_DEF("setFloat64", 2, js_dataview_setValue, JS_CLASS_FLOAT64_ARRAY ),
  55736     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "DataView", JS_PROP_CONFIGURABLE ),
  55737 };
  55738 
  55739 /* Atomics */
  55740 #ifdef CONFIG_ATOMICS
  55741 
  55742 typedef enum AtomicsOpEnum {
  55743     ATOMICS_OP_ADD,
  55744     ATOMICS_OP_AND,
  55745     ATOMICS_OP_OR,
  55746     ATOMICS_OP_SUB,
  55747     ATOMICS_OP_XOR,
  55748     ATOMICS_OP_EXCHANGE,
  55749     ATOMICS_OP_COMPARE_EXCHANGE,
  55750     ATOMICS_OP_LOAD,
  55751 } AtomicsOpEnum;
  55752 
  55753 static void *js_atomics_get_ptr(JSContext *ctx,
  55754                                 JSArrayBuffer **pabuf,
  55755                                 int *psize_log2, JSClassID *pclass_id,
  55756                                 JSValueConst obj, JSValueConst idx_val,
  55757                                 int is_waitable)
  55758 {
  55759     JSObject *p;
  55760     JSTypedArray *ta;
  55761     JSArrayBuffer *abuf;
  55762     void *ptr;
  55763     uint64_t idx;
  55764     BOOL err;
  55765     int size_log2;
  55766 
  55767     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
  55768         goto fail;
  55769     p = JS_VALUE_GET_OBJ(obj);
  55770     if (is_waitable)
  55771         err = (p->class_id != JS_CLASS_INT32_ARRAY &&
  55772                p->class_id != JS_CLASS_BIG_INT64_ARRAY);
  55773     else
  55774         err = !(p->class_id >= JS_CLASS_INT8_ARRAY &&
  55775                 p->class_id <= JS_CLASS_BIG_UINT64_ARRAY);
  55776     if (err) {
  55777     fail:
  55778         JS_ThrowTypeError(ctx, "integer TypedArray expected");
  55779         return NULL;
  55780     }
  55781     ta = p->u.typed_array;
  55782     abuf = ta->buffer->u.array_buffer;
  55783     if (!abuf->shared) {
  55784         if (is_waitable == 2) {
  55785             JS_ThrowTypeError(ctx, "not a SharedArrayBuffer TypedArray");
  55786             return NULL;
  55787         }
  55788         if (abuf->detached) {
  55789             JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  55790             return NULL;
  55791         }
  55792     }
  55793     if (JS_ToIndex(ctx, &idx, idx_val)) {
  55794         return NULL;
  55795     }
  55796     /* if the array buffer is detached, p->u.array.count = 0 */
  55797     if (idx >= p->u.array.count) {
  55798         JS_ThrowRangeError(ctx, "out-of-bound access");
  55799         return NULL;
  55800     }
  55801     size_log2 = typed_array_size_log2(p->class_id);
  55802     ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2);
  55803     if (pabuf)
  55804         *pabuf = abuf;
  55805     if (psize_log2)
  55806         *psize_log2 = size_log2;
  55807     if (pclass_id)
  55808         *pclass_id = p->class_id;
  55809     return ptr;
  55810 }
  55811 
  55812 static JSValue js_atomics_op(JSContext *ctx,
  55813                              JSValueConst this_obj,
  55814                              int argc, JSValueConst *argv, int op)
  55815 {
  55816     int size_log2;
  55817     uint64_t v, a, rep_val;
  55818     void *ptr;
  55819     JSValue ret;
  55820     JSClassID class_id;
  55821     JSArrayBuffer *abuf;
  55822 
  55823     ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, &class_id,
  55824                              argv[0], argv[1], 0);
  55825     if (!ptr)
  55826         return JS_EXCEPTION;
  55827     rep_val = 0;
  55828     if (op == ATOMICS_OP_LOAD) {
  55829         v = 0;
  55830     } else {
  55831         if (size_log2 == 3) {
  55832             int64_t v64;
  55833             if (JS_ToBigInt64(ctx, &v64, argv[2]))
  55834                 return JS_EXCEPTION;
  55835             v = v64;
  55836             if (op == ATOMICS_OP_COMPARE_EXCHANGE) {
  55837                 if (JS_ToBigInt64(ctx, &v64, argv[3]))
  55838                     return JS_EXCEPTION;
  55839                 rep_val = v64;
  55840             }
  55841         } else {
  55842                 uint32_t v32;
  55843                 if (JS_ToUint32(ctx, &v32, argv[2]))
  55844                     return JS_EXCEPTION;
  55845                 v = v32;
  55846                 if (op == ATOMICS_OP_COMPARE_EXCHANGE) {
  55847                     if (JS_ToUint32(ctx, &v32, argv[3]))
  55848                         return JS_EXCEPTION;
  55849                     rep_val = v32;
  55850                 }
  55851         }
  55852         if (abuf->detached)
  55853             return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  55854    }
  55855 
  55856    switch(op | (size_log2 << 3)) {
  55857 
  55858 #define OP(op_name, func_name)                          \
  55859     case ATOMICS_OP_ ## op_name | (0 << 3):             \
  55860        a = func_name((_Atomic(uint8_t) *)ptr, v);       \
  55861        break;                                           \
  55862     case ATOMICS_OP_ ## op_name | (1 << 3):             \
  55863         a = func_name((_Atomic(uint16_t) *)ptr, v);     \
  55864         break;                                          \
  55865     case ATOMICS_OP_ ## op_name | (2 << 3):             \
  55866         a = func_name((_Atomic(uint32_t) *)ptr, v);     \
  55867         break;                                          \
  55868     case ATOMICS_OP_ ## op_name | (3 << 3):             \
  55869         a = func_name((_Atomic(uint64_t) *)ptr, v);     \
  55870         break;
  55871 
  55872         OP(ADD, atomic_fetch_add)
  55873         OP(AND, atomic_fetch_and)
  55874         OP(OR, atomic_fetch_or)
  55875         OP(SUB, atomic_fetch_sub)
  55876         OP(XOR, atomic_fetch_xor)
  55877         OP(EXCHANGE, atomic_exchange)
  55878 #undef OP
  55879 
  55880     case ATOMICS_OP_LOAD | (0 << 3):
  55881         a = atomic_load((_Atomic(uint8_t) *)ptr);
  55882         break;
  55883     case ATOMICS_OP_LOAD | (1 << 3):
  55884         a = atomic_load((_Atomic(uint16_t) *)ptr);
  55885         break;
  55886     case ATOMICS_OP_LOAD | (2 << 3):
  55887         a = atomic_load((_Atomic(uint32_t) *)ptr);
  55888         break;
  55889     case ATOMICS_OP_LOAD | (3 << 3):
  55890         a = atomic_load((_Atomic(uint64_t) *)ptr);
  55891         break;
  55892 
  55893     case ATOMICS_OP_COMPARE_EXCHANGE | (0 << 3):
  55894         {
  55895             uint8_t v1 = v;
  55896             atomic_compare_exchange_strong((_Atomic(uint8_t) *)ptr, &v1, rep_val);
  55897             a = v1;
  55898         }
  55899         break;
  55900     case ATOMICS_OP_COMPARE_EXCHANGE | (1 << 3):
  55901         {
  55902             uint16_t v1 = v;
  55903             atomic_compare_exchange_strong((_Atomic(uint16_t) *)ptr, &v1, rep_val);
  55904             a = v1;
  55905         }
  55906         break;
  55907     case ATOMICS_OP_COMPARE_EXCHANGE | (2 << 3):
  55908         {
  55909             uint32_t v1 = v;
  55910             atomic_compare_exchange_strong((_Atomic(uint32_t) *)ptr, &v1, rep_val);
  55911             a = v1;
  55912         }
  55913         break;
  55914     case ATOMICS_OP_COMPARE_EXCHANGE | (3 << 3):
  55915         {
  55916             uint64_t v1 = v;
  55917             atomic_compare_exchange_strong((_Atomic(uint64_t) *)ptr, &v1, rep_val);
  55918             a = v1;
  55919         }
  55920         break;
  55921     default:
  55922         abort();
  55923     }
  55924 
  55925     switch(class_id) {
  55926     case JS_CLASS_INT8_ARRAY:
  55927         a = (int8_t)a;
  55928         goto done;
  55929     case JS_CLASS_UINT8_ARRAY:
  55930         a = (uint8_t)a;
  55931         goto done;
  55932     case JS_CLASS_INT16_ARRAY:
  55933         a = (int16_t)a;
  55934         goto done;
  55935     case JS_CLASS_UINT16_ARRAY:
  55936         a = (uint16_t)a;
  55937         goto done;
  55938     case JS_CLASS_INT32_ARRAY:
  55939     done:
  55940         ret = JS_NewInt32(ctx, a);
  55941         break;
  55942     case JS_CLASS_UINT32_ARRAY:
  55943         ret = JS_NewUint32(ctx, a);
  55944         break;
  55945     case JS_CLASS_BIG_INT64_ARRAY:
  55946         ret = JS_NewBigInt64(ctx, a);
  55947         break;
  55948     case JS_CLASS_BIG_UINT64_ARRAY:
  55949         ret = JS_NewBigUint64(ctx, a);
  55950         break;
  55951     default:
  55952         abort();
  55953     }
  55954     return ret;
  55955 }
  55956 
  55957 static JSValue js_atomics_store(JSContext *ctx,
  55958                                 JSValueConst this_obj,
  55959                                 int argc, JSValueConst *argv)
  55960 {
  55961     int size_log2;
  55962     void *ptr;
  55963     JSValue ret;
  55964     JSArrayBuffer *abuf;
  55965 
  55966     ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, NULL,
  55967                              argv[0], argv[1], 0);
  55968     if (!ptr)
  55969         return JS_EXCEPTION;
  55970     if (size_log2 == 3) {
  55971         int64_t v64;
  55972         ret = JS_ToBigIntValueFree(ctx, JS_DupValue(ctx, argv[2]));
  55973         if (JS_IsException(ret))
  55974             return ret;
  55975         if (JS_ToBigInt64(ctx, &v64, ret)) {
  55976             JS_FreeValue(ctx, ret);
  55977             return JS_EXCEPTION;
  55978         }
  55979         if (abuf->detached)
  55980             return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  55981         atomic_store((_Atomic(uint64_t) *)ptr, v64);
  55982     } else {
  55983         uint32_t v;
  55984         /* XXX: spec, would be simpler to return the written value */
  55985         ret = JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[2]));
  55986         if (JS_IsException(ret))
  55987             return ret;
  55988         if (JS_ToUint32(ctx, &v, ret)) {
  55989             JS_FreeValue(ctx, ret);
  55990             return JS_EXCEPTION;
  55991         }
  55992         if (abuf->detached)
  55993             return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  55994         switch(size_log2) {
  55995         case 0:
  55996             atomic_store((_Atomic(uint8_t) *)ptr, v);
  55997             break;
  55998         case 1:
  55999             atomic_store((_Atomic(uint16_t) *)ptr, v);
  56000             break;
  56001         case 2:
  56002             atomic_store((_Atomic(uint32_t) *)ptr, v);
  56003             break;
  56004         default:
  56005             abort();
  56006         }
  56007     }
  56008     return ret;
  56009 }
  56010 
  56011 static JSValue js_atomics_isLockFree(JSContext *ctx,
  56012                                      JSValueConst this_obj,
  56013                                      int argc, JSValueConst *argv)
  56014 {
  56015     int v, ret;
  56016     if (JS_ToInt32Sat(ctx, &v, argv[0]))
  56017         return JS_EXCEPTION;
  56018     ret = (v == 1 || v == 2 || v == 4 || v == 8);
  56019     return JS_NewBool(ctx, ret);
  56020 }
  56021 
  56022 typedef struct JSAtomicsWaiter {
  56023     struct list_head link;
  56024     BOOL linked;
  56025     pthread_cond_t cond;
  56026     int32_t *ptr;
  56027 } JSAtomicsWaiter;
  56028 
  56029 static pthread_mutex_t js_atomics_mutex = PTHREAD_MUTEX_INITIALIZER;
  56030 static struct list_head js_atomics_waiter_list =
  56031     LIST_HEAD_INIT(js_atomics_waiter_list);
  56032 
  56033 static JSValue js_atomics_wait(JSContext *ctx,
  56034                                JSValueConst this_obj,
  56035                                int argc, JSValueConst *argv)
  56036 {
  56037     int64_t v;
  56038     int32_t v32;
  56039     void *ptr;
  56040     int64_t timeout;
  56041     struct timespec ts;
  56042     JSAtomicsWaiter waiter_s, *waiter;
  56043     int ret, size_log2, res;
  56044     double d;
  56045 
  56046     ptr = js_atomics_get_ptr(ctx, NULL, &size_log2, NULL,
  56047                              argv[0], argv[1], 2);
  56048     if (!ptr)
  56049         return JS_EXCEPTION;
  56050     if (size_log2 == 3) {
  56051         if (JS_ToBigInt64(ctx, &v, argv[2]))
  56052             return JS_EXCEPTION;
  56053     } else {
  56054         if (JS_ToInt32(ctx, &v32, argv[2]))
  56055             return JS_EXCEPTION;
  56056         v = v32;
  56057     }
  56058     if (JS_ToFloat64(ctx, &d, argv[3]))
  56059         return JS_EXCEPTION;
  56060     /* must use INT64_MAX + 1 because INT64_MAX cannot be exactly represented as a double */
  56061     if (isnan(d) || d >= 0x1p63)
  56062         timeout = INT64_MAX;
  56063     else if (d < 0)
  56064         timeout = 0;
  56065     else
  56066         timeout = (int64_t)d;
  56067     if (!ctx->rt->can_block)
  56068         return JS_ThrowTypeError(ctx, "cannot block in this thread");
  56069 
  56070     /* XXX: inefficient if large number of waiters, should hash on
  56071        'ptr' value */
  56072     /* XXX: use Linux futexes when available ? */
  56073     pthread_mutex_lock(&js_atomics_mutex);
  56074     if (size_log2 == 3) {
  56075         res = *(int64_t *)ptr != v;
  56076     } else {
  56077         res = *(int32_t *)ptr != v;
  56078     }
  56079     if (res) {
  56080         pthread_mutex_unlock(&js_atomics_mutex);
  56081         return JS_AtomToString(ctx, JS_ATOM_not_equal);
  56082     }
  56083 
  56084     waiter = &waiter_s;
  56085     waiter->ptr = ptr;
  56086     pthread_cond_init(&waiter->cond, NULL);
  56087     waiter->linked = TRUE;
  56088     list_add_tail(&waiter->link, &js_atomics_waiter_list);
  56089 
  56090     if (timeout == INT64_MAX) {
  56091         pthread_cond_wait(&waiter->cond, &js_atomics_mutex);
  56092         ret = 0;
  56093     } else {
  56094         /* XXX: use clock monotonic */
  56095         clock_gettime(CLOCK_REALTIME, &ts);
  56096         ts.tv_sec += timeout / 1000;
  56097         ts.tv_nsec += (timeout % 1000) * 1000000;
  56098         if (ts.tv_nsec >= 1000000000) {
  56099             ts.tv_nsec -= 1000000000;
  56100             ts.tv_sec++;
  56101         }
  56102         ret = pthread_cond_timedwait(&waiter->cond, &js_atomics_mutex,
  56103                                      &ts);
  56104     }
  56105     if (waiter->linked)
  56106         list_del(&waiter->link);
  56107     pthread_mutex_unlock(&js_atomics_mutex);
  56108     pthread_cond_destroy(&waiter->cond);
  56109     if (ret == ETIMEDOUT) {
  56110         return JS_AtomToString(ctx, JS_ATOM_timed_out);
  56111     } else {
  56112         return JS_AtomToString(ctx, JS_ATOM_ok);
  56113     }
  56114 }
  56115 
  56116 static JSValue js_atomics_notify(JSContext *ctx,
  56117                                  JSValueConst this_obj,
  56118                                  int argc, JSValueConst *argv)
  56119 {
  56120     struct list_head *el, *el1, waiter_list;
  56121     int32_t count, n;
  56122     void *ptr;
  56123     JSAtomicsWaiter *waiter;
  56124     JSArrayBuffer *abuf;
  56125 
  56126     ptr = js_atomics_get_ptr(ctx, &abuf, NULL, NULL, argv[0], argv[1], 1);
  56127     if (!ptr)
  56128         return JS_EXCEPTION;
  56129 
  56130     if (JS_IsUndefined(argv[2])) {
  56131         count = INT32_MAX;
  56132     } else {
  56133         if (JS_ToInt32Clamp(ctx, &count, argv[2], 0, INT32_MAX, 0))
  56134             return JS_EXCEPTION;
  56135     }
  56136     if (abuf->detached)
  56137         return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
  56138 
  56139     n = 0;
  56140     if (abuf->shared && count > 0) {
  56141         pthread_mutex_lock(&js_atomics_mutex);
  56142         init_list_head(&waiter_list);
  56143         list_for_each_safe(el, el1, &js_atomics_waiter_list) {
  56144             waiter = list_entry(el, JSAtomicsWaiter, link);
  56145             if (waiter->ptr == ptr) {
  56146                 list_del(&waiter->link);
  56147                 waiter->linked = FALSE;
  56148                 list_add_tail(&waiter->link, &waiter_list);
  56149                 n++;
  56150                 if (n >= count)
  56151                     break;
  56152             }
  56153         }
  56154         list_for_each(el, &waiter_list) {
  56155             waiter = list_entry(el, JSAtomicsWaiter, link);
  56156             pthread_cond_signal(&waiter->cond);
  56157         }
  56158         pthread_mutex_unlock(&js_atomics_mutex);
  56159     }
  56160     return JS_NewInt32(ctx, n);
  56161 }
  56162 
  56163 static const JSCFunctionListEntry js_atomics_funcs[] = {
  56164     JS_CFUNC_MAGIC_DEF("add", 3, js_atomics_op, ATOMICS_OP_ADD ),
  56165     JS_CFUNC_MAGIC_DEF("and", 3, js_atomics_op, ATOMICS_OP_AND ),
  56166     JS_CFUNC_MAGIC_DEF("or", 3, js_atomics_op, ATOMICS_OP_OR ),
  56167     JS_CFUNC_MAGIC_DEF("sub", 3, js_atomics_op, ATOMICS_OP_SUB ),
  56168     JS_CFUNC_MAGIC_DEF("xor", 3, js_atomics_op, ATOMICS_OP_XOR ),
  56169     JS_CFUNC_MAGIC_DEF("exchange", 3, js_atomics_op, ATOMICS_OP_EXCHANGE ),
  56170     JS_CFUNC_MAGIC_DEF("compareExchange", 4, js_atomics_op, ATOMICS_OP_COMPARE_EXCHANGE ),
  56171     JS_CFUNC_MAGIC_DEF("load", 2, js_atomics_op, ATOMICS_OP_LOAD ),
  56172     JS_CFUNC_DEF("store", 3, js_atomics_store ),
  56173     JS_CFUNC_DEF("isLockFree", 1, js_atomics_isLockFree ),
  56174     JS_CFUNC_DEF("wait", 4, js_atomics_wait ),
  56175     JS_CFUNC_DEF("notify", 3, js_atomics_notify ),
  56176     JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Atomics", JS_PROP_CONFIGURABLE ),
  56177 };
  56178 
  56179 static const JSCFunctionListEntry js_atomics_obj[] = {
  56180     JS_OBJECT_DEF("Atomics", js_atomics_funcs, countof(js_atomics_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
  56181 };
  56182 
  56183 void JS_AddIntrinsicAtomics(JSContext *ctx)
  56184 {
  56185     /* add Atomics as autoinit object */
  56186     JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_atomics_obj, countof(js_atomics_obj));
  56187 }
  56188 
  56189 #endif /* CONFIG_ATOMICS */
  56190 
  56191 void JS_AddIntrinsicTypedArrays(JSContext *ctx)
  56192 {
  56193     JSValue typed_array_base_proto, typed_array_base_func;
  56194     JSValueConst array_buffer_func, shared_array_buffer_func;
  56195     int i;
  56196 
  56197     ctx->class_proto[JS_CLASS_ARRAY_BUFFER] = JS_NewObject(ctx);
  56198     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_BUFFER],
  56199                                js_array_buffer_proto_funcs,
  56200                                countof(js_array_buffer_proto_funcs));
  56201 
  56202     array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "ArrayBuffer",
  56203                                                  js_array_buffer_constructor, 1,
  56204                                                  ctx->class_proto[JS_CLASS_ARRAY_BUFFER]);
  56205     JS_SetPropertyFunctionList(ctx, array_buffer_func,
  56206                                js_array_buffer_funcs,
  56207                                countof(js_array_buffer_funcs));
  56208 
  56209     ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER] = JS_NewObject(ctx);
  56210     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER],
  56211                                js_shared_array_buffer_proto_funcs,
  56212                                countof(js_shared_array_buffer_proto_funcs));
  56213 
  56214     shared_array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "SharedArrayBuffer",
  56215                                                  js_shared_array_buffer_constructor, 1,
  56216                                                  ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER]);
  56217     JS_SetPropertyFunctionList(ctx, shared_array_buffer_func,
  56218                                js_shared_array_buffer_funcs,
  56219                                countof(js_shared_array_buffer_funcs));
  56220 
  56221     typed_array_base_proto = JS_NewObject(ctx);
  56222     JS_SetPropertyFunctionList(ctx, typed_array_base_proto,
  56223                                js_typed_array_base_proto_funcs,
  56224                                countof(js_typed_array_base_proto_funcs));
  56225 
  56226     /* TypedArray.prototype.toString must be the same object as Array.prototype.toString */
  56227     JSValue obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_toString);
  56228     /* XXX: should use alias method in JSCFunctionListEntry */ //@@@
  56229     JS_DefinePropertyValue(ctx, typed_array_base_proto, JS_ATOM_toString, obj,
  56230                            JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
  56231 
  56232     typed_array_base_func = JS_NewCFunction(ctx, js_typed_array_base_constructor,
  56233                                             "TypedArray", 0);
  56234     JS_SetPropertyFunctionList(ctx, typed_array_base_func,
  56235                                js_typed_array_base_funcs,
  56236                                countof(js_typed_array_base_funcs));
  56237     JS_SetConstructor(ctx, typed_array_base_func, typed_array_base_proto);
  56238 
  56239     /* Used to squelch a -Wcast-function-type warning. */
  56240     JSCFunctionType ft = { .generic_magic = js_typed_array_constructor };
  56241     for(i = JS_CLASS_UINT8C_ARRAY; i < JS_CLASS_UINT8C_ARRAY + JS_TYPED_ARRAY_COUNT; i++) {
  56242         JSValue func_obj;
  56243         char buf[ATOM_GET_STR_BUF_SIZE];
  56244         const char *name;
  56245 
  56246         ctx->class_proto[i] = JS_NewObjectProto(ctx, typed_array_base_proto);
  56247         JS_DefinePropertyValueStr(ctx, ctx->class_proto[i],
  56248                                   "BYTES_PER_ELEMENT",
  56249                                   JS_NewInt32(ctx, 1 << typed_array_size_log2(i)),
  56250                                   0);
  56251         name = JS_AtomGetStr(ctx, buf, sizeof(buf),
  56252                              JS_ATOM_Uint8ClampedArray + i - JS_CLASS_UINT8C_ARRAY);
  56253         func_obj = JS_NewCFunction3(ctx, ft.generic,
  56254                                     name, 3, JS_CFUNC_constructor_magic, i,
  56255                                     typed_array_base_func);
  56256         JS_NewGlobalCConstructor2(ctx, func_obj, name, ctx->class_proto[i]);
  56257         JS_DefinePropertyValueStr(ctx, func_obj,
  56258                                   "BYTES_PER_ELEMENT",
  56259                                   JS_NewInt32(ctx, 1 << typed_array_size_log2(i)),
  56260                                   0);
  56261     }
  56262     JS_FreeValue(ctx, typed_array_base_proto);
  56263     JS_FreeValue(ctx, typed_array_base_func);
  56264 
  56265     /* DataView */
  56266     ctx->class_proto[JS_CLASS_DATAVIEW] = JS_NewObject(ctx);
  56267     JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATAVIEW],
  56268                                js_dataview_proto_funcs,
  56269                                countof(js_dataview_proto_funcs));
  56270     JS_NewGlobalCConstructorOnly(ctx, "DataView",
  56271                                  js_dataview_constructor, 1,
  56272                                  ctx->class_proto[JS_CLASS_DATAVIEW]);
  56273     /* Atomics */
  56274 #ifdef CONFIG_ATOMICS
  56275     JS_AddIntrinsicAtomics(ctx);
  56276 #endif
  56277 }