quickjs-hs 0.1.2.3 → 0.1.2.4
raw patch · 9 files changed
+50979/−50645 lines, 9 filesdep ~QuickCheck
Dependency ranges changed: QuickCheck
Files
- ChangeLog.md +5/−0
- quickjs-hs.cabal +3/−3
- quickjs/libbf.h +1/−1
- quickjs/libregexp.c +17/−9
- quickjs/quickjs-atom.h +1/−0
- quickjs/quickjs-libc.c +94/−1
- quickjs/quickjs-opcode.h +5/−8
- quickjs/quickjs.c +50846/−50621
- quickjs/quickjs.h +7/−2
ChangeLog.md view
@@ -1,5 +1,10 @@ # Changelog for quickjs-hs +### 0.1.2.4++* Bumped the quickjs library to 2020-11-08 release+* Relaxed QuickCheck bounds (PR by poscat0x04)+ ### 0.1.2.3 * Fix for infinite loop caused by incorrect defintion of SomeJSRuntimeException
quickjs-hs.cabal view
@@ -1,6 +1,6 @@ cabal-version: 1.12 name: quickjs-hs-version: 0.1.2.3+version: 0.1.2.4 homepage: https://github.com/goodlyrottenapple/quickjs-hs#readme bug-reports: https://github.com/goodlyrottenapple/quickjs-hs/issues author: Sam Balco@@ -71,7 +71,7 @@ cc-options: -static -D_GNU_SOURCE - -DCONFIG_VERSION="2020-09-06"+ -DCONFIG_VERSION="2020-11-08" -DCONFIG_BIGNUM test-suite quickjs-hs-test@@ -87,7 +87,7 @@ , aeson , exceptions , HUnit >=1.6.0.0 && <1.7- , QuickCheck >=2.9 && <2.14+ , QuickCheck >=2.9 && <2.15 , tasty >=1.0 && <1.3 , tasty-hunit >=0.10 && <0.11 , tasty-quickcheck >=0.9 && <0.11
quickjs/libbf.h view
@@ -27,7 +27,7 @@ #include <stddef.h> #include <stdint.h> -#if defined(__x86_64__)+#if INTPTR_MAX >= INT64_MAX #define LIMB_LOG2_BITS 6 #else #define LIMB_LOG2_BITS 5
quickjs/libregexp.c view
@@ -75,7 +75,7 @@ int capture_count; int total_capture_count; /* -1 = not computed yet */ int has_named_captures; /* -1 = don't know, 0 = no, 1 = yes */- void *mem_opaque;+ void *opaque; DynBuf group_names; union { char error_msg[TMP_BUF_SIZE];@@ -230,7 +230,7 @@ invert = c & 1; c_pt = char_range_table[c >> 1]; len = *c_pt++;- cr_init(cr, s->mem_opaque, lre_realloc);+ cr_init(cr, s->opaque, lre_realloc); for(i = 0; i < len * 2; i++) { if (cr_add_point(cr, c_pt[i])) goto fail;@@ -625,7 +625,7 @@ p++; q = name; while (is_unicode_char(*p)) {- if ((q - name) > sizeof(name) - 1)+ if ((q - name) >= sizeof(name) - 1) goto unknown_property_name; *q++ = *p++; }@@ -634,7 +634,7 @@ if (*p == '=') { p++; while (is_unicode_char(*p)) {- if ((q - value) > sizeof(value) - 1)+ if ((q - value) >= sizeof(value) - 1) return re_parse_error(s, "unknown unicode property value"); *q++ = *p++; }@@ -651,7 +651,7 @@ } else if (!strcmp(name, "Script_Extensions") || !strcmp(name, "scx")) { script_ext = TRUE; do_script:- cr_init(cr, s->mem_opaque, lre_realloc);+ cr_init(cr, s->opaque, lre_realloc); ret = unicode_script(cr, value, script_ext); if (ret) { cr_free(cr);@@ -661,7 +661,7 @@ goto out_of_memory; } } else if (!strcmp(name, "General_Category") || !strcmp(name, "gc")) {- cr_init(cr, s->mem_opaque, lre_realloc);+ cr_init(cr, s->opaque, lre_realloc); ret = unicode_general_category(cr, value); if (ret) { cr_free(cr);@@ -671,7 +671,7 @@ goto out_of_memory; } } else if (value[0] == '\0') {- cr_init(cr, s->mem_opaque, lre_realloc);+ cr_init(cr, s->opaque, lre_realloc); ret = unicode_general_category(cr, name); if (ret == -1) { cr_free(cr);@@ -864,7 +864,7 @@ CharRange cr1_s, *cr1 = &cr1_s; BOOL invert; - cr_init(cr, s->mem_opaque, lre_realloc);+ cr_init(cr, s->opaque, lre_realloc); p = *pp; p++; /* skip '[' */ invert = FALSE;@@ -1147,9 +1147,13 @@ } } capture_index++;+ if (capture_index >= CAPTURE_COUNT_MAX)+ goto done; } } else { capture_index++;+ if (capture_index >= CAPTURE_COUNT_MAX)+ goto done; } break; case '\\':@@ -1163,6 +1167,7 @@ break; } }+ done: if (capture_name) return -1; else@@ -1734,6 +1739,9 @@ { int start, len, pos; + if (lre_check_stack_overflow(s->opaque, 0))+ return re_parse_error(s, "stack overflow");+ start = s->byte_code.size; if (re_parse_alternative(s, is_backward_dir)) return -1;@@ -1819,7 +1827,7 @@ BOOL is_sticky; memset(s, 0, sizeof(*s));- s->mem_opaque = opaque;+ s->opaque = opaque; s->buf_ptr = (const uint8_t *)buf; s->buf_end = s->buf_ptr + buf_len; s->buf_start = s->buf_ptr;
quickjs/quickjs-atom.h view
@@ -113,6 +113,7 @@ DEF(_eval_, "<eval>") DEF(_ret_, "<ret>") DEF(_var_, "<var>")+DEF(_arg_var_, "<arg_var>") DEF(_with_, "<with>") DEF(lastIndex, "lastIndex") DEF(target, "target")
quickjs/quickjs-libc.c view
@@ -623,6 +623,97 @@ return JS_NewString(ctx, str); } +#if defined(_WIN32)+static void setenv(const char *name, const char *value, int overwrite)+{+ char *str;+ size_t name_len, value_len;+ name_len = strlen(name);+ value_len = strlen(value);+ str = malloc(name_len + 1 + value_len + 1);+ memcpy(str, name, name_len);+ str[name_len] = '=';+ memcpy(str + name_len + 1, value, value_len);+ str[name_len + 1 + value_len] = '\0';+ _putenv(str);+ free(str);+}++static void unsetenv(const char *name)+{+ setenv(name, "", TRUE);+}+#endif /* _WIN32 */++static JSValue js_std_setenv(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ const char *name, *value;+ name = JS_ToCString(ctx, argv[0]);+ if (!name)+ return JS_EXCEPTION;+ value = JS_ToCString(ctx, argv[1]);+ if (!value) {+ JS_FreeCString(ctx, name);+ return JS_EXCEPTION;+ }+ setenv(name, value, TRUE);+ JS_FreeCString(ctx, name);+ JS_FreeCString(ctx, value);+ return JS_UNDEFINED;+}++static JSValue js_std_unsetenv(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ const char *name;+ name = JS_ToCString(ctx, argv[0]);+ if (!name)+ return JS_EXCEPTION;+ unsetenv(name);+ JS_FreeCString(ctx, name);+ return JS_UNDEFINED;+}++/* return an object containing the list of the available environment+ variables. */+static JSValue js_std_getenviron(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ char **envp;+ const char *name, *p, *value;+ JSValue obj;+ uint32_t idx;+ size_t name_len;+ JSAtom atom;+ int ret;++ obj = JS_NewObject(ctx);+ if (JS_IsException(obj))+ return JS_EXCEPTION;+ envp = environ;+ for(idx = 0; envp[idx] != NULL; idx++) {+ name = envp[idx];+ p = strchr(name, '=');+ name_len = p - name;+ if (!p)+ continue;+ value = p + 1;+ atom = JS_NewAtomLen(ctx, name, name_len);+ if (atom == JS_ATOM_NULL)+ goto fail;+ ret = JS_DefinePropertyValue(ctx, obj, atom, JS_NewString(ctx, value),+ JS_PROP_C_W_E);+ JS_FreeAtom(ctx, atom);+ if (ret < 0)+ goto fail;+ }+ return obj;+ fail:+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}+ static JSValue js_std_gc(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {@@ -1395,6 +1486,9 @@ JS_CFUNC_DEF("evalScript", 1, js_evalScript ), JS_CFUNC_DEF("loadScript", 1, js_loadScript ), JS_CFUNC_DEF("getenv", 1, js_std_getenv ),+ JS_CFUNC_DEF("setenv", 1, js_std_setenv ),+ JS_CFUNC_DEF("unsetenv", 1, js_std_unsetenv ),+ JS_CFUNC_DEF("getenviron", 1, js_std_getenviron ), JS_CFUNC_DEF("urlGet", 1, js_std_urlGet ), JS_CFUNC_DEF("loadFile", 1, js_std_loadFile ), JS_CFUNC_DEF("strerror", 1, js_std_strerror ),@@ -1412,7 +1506,6 @@ JS_PROP_INT32_DEF("SEEK_CUR", SEEK_CUR, JS_PROP_CONFIGURABLE ), JS_PROP_INT32_DEF("SEEK_END", SEEK_END, JS_PROP_CONFIGURABLE ), JS_OBJECT_DEF("Error", js_std_error_props, countof(js_std_error_props), JS_PROP_CONFIGURABLE),- /* setenv, ... */ }; static const JSCFunctionListEntry js_std_file_proto_funcs[] = {
quickjs/quickjs-opcode.h view
@@ -114,7 +114,7 @@ DEF( add_brand, 1, 2, 0, none) /* this_obj home_obj -> */ DEF( return_async, 1, 1, 0, none) DEF( throw, 1, 1, 0, none)-DEF( throw_var, 6, 0, 0, atom_u8)+DEF( throw_error, 6, 0, 0, atom_u8) DEF( eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */ DEF( apply_eval, 3, 2, 1, u16) /* func array -> ret_eval */ DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a@@ -205,16 +205,15 @@ DEF(for_await_of_start, 1, 1, 3, none) DEF( for_in_next, 1, 1, 3, none) DEF( for_of_next, 2, 3, 5, u8)-DEF(for_await_of_next, 1, 3, 4, none)+DEF(iterator_check_object, 1, 1, 1, none) DEF(iterator_get_value_done, 1, 1, 2, none) DEF( iterator_close, 1, 3, 0, none) DEF(iterator_close_return, 1, 4, 4, none)-DEF(async_iterator_close, 1, 3, 2, none)-DEF(async_iterator_next, 1, 4, 4, none)-DEF(async_iterator_get, 2, 4, 5, u8)+DEF( iterator_next, 1, 4, 4, none)+DEF( iterator_call, 2, 4, 5, u8) DEF( initial_yield, 1, 0, 0, none) DEF( yield, 1, 1, 2, none)-DEF( yield_star, 1, 2, 2, none)+DEF( yield_star, 1, 1, 2, none) DEF(async_yield_star, 1, 1, 2, none) DEF( await, 1, 1, 1, none) @@ -265,8 +264,6 @@ DEF( nop, 1, 0, 0, none) /* temporary opcodes: never emitted in the final bytecode */--def(set_arg_valid_upto, 3, 0, 0, arg) /* emitted in phase 1, removed in phase 2 */ def( enter_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ def( leave_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */
quickjs/quickjs.c view
@@ -199,50627 +199,50852 @@ } JSErrorEnum; #define JS_MAX_LOCAL_VARS 65536-#define JS_STACK_SIZE_MAX 65536-#define JS_STRING_LEN_MAX ((1 << 30) - 1)--#define __exception __attribute__((warn_unused_result))--typedef struct JSShape JSShape;-typedef struct JSString JSString;-typedef struct JSString JSAtomStruct;--typedef enum {- JS_GC_PHASE_NONE,- JS_GC_PHASE_DECREF,- JS_GC_PHASE_REMOVE_CYCLES,-} JSGCPhaseEnum;--typedef enum OPCodeEnum OPCodeEnum;--#ifdef CONFIG_BIGNUM-/* function pointers are used for numeric operations so that it is- possible to remove some numeric types */-typedef struct {- JSValue (*to_string)(JSContext *ctx, JSValueConst val);- JSValue (*from_string)(JSContext *ctx, const char *buf,- int radix, int flags, slimb_t *pexponent);- int (*unary_arith)(JSContext *ctx,- JSValue *pres, OPCodeEnum op, JSValue op1);- int (*binary_arith)(JSContext *ctx, OPCodeEnum op,- JSValue *pres, JSValue op1, JSValue op2);- int (*compare)(JSContext *ctx, OPCodeEnum op,- JSValue op1, JSValue op2);- /* only for bigfloat: */- JSValue (*mul_pow10_to_float64)(JSContext *ctx, const bf_t *a,- int64_t exponent);- int (*mul_pow10)(JSContext *ctx, JSValue *sp);-} JSNumericOperations;-#endif--struct JSRuntime {- JSMallocFunctions mf;- JSMallocState malloc_state;- const char *rt_info;-- int atom_hash_size; /* power of two */- int atom_count;- int atom_size;- int atom_count_resize; /* resize hash table at this count */- uint32_t *atom_hash;- JSAtomStruct **atom_array;- int atom_free_index; /* 0 = none */-- int class_count; /* size of class_array */- JSClass *class_array;-- struct list_head context_list; /* list of JSContext.link */- /* list of JSGCObjectHeader.link. List of allocated GC objects (used- by the garbage collector) */- struct list_head gc_obj_list;- /* list of JSGCObjectHeader.link. Used during JS_FreeValueRT() */- struct list_head gc_zero_ref_count_list; - struct list_head tmp_obj_list; /* used during GC */- JSGCPhaseEnum gc_phase : 8;- size_t malloc_gc_threshold;-#ifdef DUMP_LEAKS- struct list_head string_list; /* list of JSString.link */-#endif- /* stack limitation */- const uint8_t *stack_top;- size_t stack_size; /* in bytes */-- JSValue current_exception;- /* true if inside an out of memory error, to avoid recursing */- BOOL in_out_of_memory : 8;-- struct JSStackFrame *current_stack_frame;-- JSInterruptHandler *interrupt_handler;- void *interrupt_opaque;-- JSHostPromiseRejectionTracker *host_promise_rejection_tracker;- void *host_promise_rejection_tracker_opaque;- - struct list_head job_list; /* list of JSJobEntry.link */-- JSModuleNormalizeFunc *module_normalize_func;- JSModuleLoaderFunc *module_loader_func;- void *module_loader_opaque;-- BOOL can_block : 8; /* TRUE if Atomics.wait can block */- /* used to allocate, free and clone SharedArrayBuffers */- JSSharedArrayBufferFunctions sab_funcs;- - /* Shape hash table */- int shape_hash_bits;- int shape_hash_size;- int shape_hash_count; /* number of hashed shapes */- JSShape **shape_hash;-#ifdef CONFIG_BIGNUM- bf_context_t bf_ctx;- JSNumericOperations bigint_ops;- JSNumericOperations bigfloat_ops;- JSNumericOperations bigdecimal_ops;- uint32_t operator_count;-#endif- void *user_opaque;-};--struct JSClass {- uint32_t class_id; /* 0 means free entry */- JSAtom class_name;- JSClassFinalizer *finalizer;- JSClassGCMark *gc_mark;- JSClassCall *call;- /* pointers for exotic behavior, can be NULL if none are present */- const JSClassExoticMethods *exotic;-};--#define JS_MODE_STRICT (1 << 0)-#define JS_MODE_STRIP (1 << 1)-#define JS_MODE_MATH (1 << 2)--typedef struct JSStackFrame {- struct JSStackFrame *prev_frame; /* NULL if first stack frame */- JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */- JSValue *arg_buf; /* arguments */- JSValue *var_buf; /* variables */- struct list_head var_ref_list; /* list of JSVarRef.link */- const uint8_t *cur_pc; /* only used in bytecode functions : PC of the- instruction after the call */- int arg_count;- int js_mode; /* 0 or JS_MODE_MATH for C functions */- /* only used in generators. Current stack pointer value. NULL if- the function is running. */ - JSValue *cur_sp;-} JSStackFrame;--typedef enum {- JS_GC_OBJ_TYPE_JS_OBJECT,- JS_GC_OBJ_TYPE_FUNCTION_BYTECODE,- JS_GC_OBJ_TYPE_SHAPE,- JS_GC_OBJ_TYPE_VAR_REF,- JS_GC_OBJ_TYPE_ASYNC_FUNCTION,- JS_GC_OBJ_TYPE_JS_CONTEXT,-} JSGCObjectTypeEnum;--/* header for GC objects. GC objects are C data structures with a- reference count that can reference other GC objects. JS Objects are- a particular type of GC object. */-struct JSGCObjectHeader {- int ref_count; /* must come first, 32-bit */- JSGCObjectTypeEnum gc_obj_type : 4;- uint8_t mark : 4; /* used by the GC */- uint8_t dummy1; /* not used by the GC */- uint16_t dummy2; /* not used by the GC */- struct list_head link;-};--typedef struct JSVarRef {- union {- JSGCObjectHeader header; /* must come first */- struct {- int __gc_ref_count; /* corresponds to header.ref_count */- uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */-- /* 0 : the JSVarRef is on the stack. header.link is an element- of JSStackFrame.var_ref_list.- 1 : the JSVarRef is detached. header.link has the normal meanning - */- uint8_t is_detached : 1; - uint8_t is_arg : 1;- uint16_t var_idx; /* index of the corresponding function variable on- the stack */- };- };- JSValue *pvalue; /* pointer to the value, either on the stack or- to 'value' */- JSValue value; /* used when the variable is no longer on the stack */-} JSVarRef;--#ifdef CONFIG_BIGNUM-typedef struct JSFloatEnv {- limb_t prec;- bf_flags_t flags;- unsigned int status;-} JSFloatEnv;--/* the same structure is used for big integers and big floats. Big- integers are never infinite or NaNs */-typedef struct JSBigFloat {- JSRefCountHeader header; /* must come first, 32-bit */- bf_t num;-} JSBigFloat;--typedef struct JSBigDecimal {- JSRefCountHeader header; /* must come first, 32-bit */- bfdec_t num;-} JSBigDecimal;-#endif--typedef enum {- JS_AUTOINIT_ID_PROTOTYPE,- JS_AUTOINIT_ID_MODULE_NS,- JS_AUTOINIT_ID_PROP,-} JSAutoInitIDEnum;--/* must be large enough to have a negligible runtime cost and small- enough to call the interrupt callback often. */-#define JS_INTERRUPT_COUNTER_INIT 10000--struct JSContext {- JSGCObjectHeader header; /* must come first */- JSRuntime *rt;- struct list_head link;-- uint16_t binary_object_count;- int binary_object_size;-- JSShape *array_shape; /* initial shape for Array objects */-- JSValue *class_proto;- JSValue function_proto;- JSValue function_ctor;- JSValue array_ctor;- JSValue regexp_ctor;- JSValue promise_ctor;- JSValue native_error_proto[JS_NATIVE_ERROR_COUNT];- JSValue iterator_proto;- JSValue async_iterator_proto;- JSValue array_proto_values;- JSValue throw_type_error;- JSValue eval_obj;-- JSValue global_obj; /* global object */- JSValue global_var_obj; /* contains the global let/const definitions */-- uint64_t random_state;-#ifdef CONFIG_BIGNUM- bf_context_t *bf_ctx; /* points to rt->bf_ctx, shared by all contexts */- JSFloatEnv fp_env; /* global FP environment */- BOOL bignum_ext : 8; /* enable math mode */- BOOL allow_operator_overloading : 8;-#endif- /* when the counter reaches zero, JSRutime.interrupt_handler is called */- int interrupt_counter;- BOOL is_error_property_enabled;-- struct list_head loaded_modules; /* list of JSModuleDef.link */-- /* if NULL, RegExp compilation is not supported */- JSValue (*compile_regexp)(JSContext *ctx, JSValueConst pattern,- JSValueConst flags);- /* if NULL, eval is not supported */- JSValue (*eval_internal)(JSContext *ctx, JSValueConst this_obj,- const char *input, size_t input_len,- const char *filename, int flags, int scope_idx);- void *user_opaque;-};--typedef union JSFloat64Union {- double d;- uint64_t u64;- uint32_t u32[2];-} JSFloat64Union;--enum {- JS_ATOM_TYPE_STRING = 1,- JS_ATOM_TYPE_GLOBAL_SYMBOL,- JS_ATOM_TYPE_SYMBOL,- JS_ATOM_TYPE_PRIVATE,-};--enum {- JS_ATOM_HASH_SYMBOL,- JS_ATOM_HASH_PRIVATE,-};--typedef enum {- JS_ATOM_KIND_STRING,- JS_ATOM_KIND_SYMBOL,- JS_ATOM_KIND_PRIVATE,-} JSAtomKindEnum;--#define JS_ATOM_HASH_MASK ((1 << 30) - 1)--struct JSString {- JSRefCountHeader header; /* must come first, 32-bit */- uint32_t len : 31;- uint8_t is_wide_char : 1; /* 0 = 8 bits, 1 = 16 bits characters */- /* for JS_ATOM_TYPE_SYMBOL: hash = 0, atom_type = 3,- for JS_ATOM_TYPE_PRIVATE: hash = 1, atom_type = 3- XXX: could change encoding to have one more bit in hash */- uint32_t hash : 30;- uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */- uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */-#ifdef DUMP_LEAKS- struct list_head link; /* string list */-#endif- union {- uint8_t str8[0]; /* 8 bit strings will get an extra null terminator */- uint16_t str16[0];- } u;-};--typedef struct JSClosureVar {- uint8_t is_local : 1;- uint8_t is_arg : 1;- uint8_t is_const : 1;- uint8_t is_lexical : 1;- uint8_t var_kind : 3; /* see JSVarKindEnum */- /* 9 bits available */- uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the- parent function. otherwise: index to a closure- variable of the parent function */- JSAtom var_name;-} JSClosureVar;--typedef struct JSVarScope {- int parent; /* index into fd->scopes of the enclosing scope */- int first; /* index into fd->vars of the last variable in this scope */-} JSVarScope;--typedef enum {- /* XXX: add more variable kinds here instead of using bit fields */- JS_VAR_NORMAL,- JS_VAR_FUNCTION_DECL, /* lexical var with function declaration */- JS_VAR_NEW_FUNCTION_DECL, /* lexical var with async/generator- function declaration */- JS_VAR_CATCH,- JS_VAR_PRIVATE_FIELD,- JS_VAR_PRIVATE_METHOD,- JS_VAR_PRIVATE_GETTER,- JS_VAR_PRIVATE_SETTER, /* must come after JS_VAR_PRIVATE_GETTER */- JS_VAR_PRIVATE_GETTER_SETTER, /* must come after JS_VAR_PRIVATE_SETTER */-} JSVarKindEnum;--typedef struct JSVarDef {- JSAtom var_name;- int scope_level; /* index into fd->scopes of this variable lexical scope */- int scope_next; /* index into fd->vars of the next variable in the- * same or enclosing lexical scope */- uint8_t is_func_var : 1; /* used for the function self reference */- uint8_t is_const : 1;- uint8_t is_lexical : 1;- uint8_t is_captured : 1;- uint8_t var_kind : 4; /* see JSVarKindEnum */- /* only used during compilation: function pool index for lexical- variables with var_kind =- JS_VAR_FUNCTION_DECL/JS_VAR_NEW_FUNCTION_DECL or scope level of- the definition of the 'var' variables (they have scope_level =- 0) */- int func_pool_or_scope_idx : 24; /* only used during compilation */-} JSVarDef;--/* for the encoding of the pc2line table */-#define PC2LINE_BASE (-1)-#define PC2LINE_RANGE 5-#define PC2LINE_OP_FIRST 1-#define PC2LINE_DIFF_PC_MAX ((255 - PC2LINE_OP_FIRST) / PC2LINE_RANGE)--typedef enum JSFunctionKindEnum {- JS_FUNC_NORMAL = 0,- JS_FUNC_GENERATOR = (1 << 0),- JS_FUNC_ASYNC = (1 << 1),- JS_FUNC_ASYNC_GENERATOR = (JS_FUNC_GENERATOR | JS_FUNC_ASYNC),-} JSFunctionKindEnum;--typedef struct JSFunctionBytecode {- JSGCObjectHeader header; /* must come first */- uint8_t js_mode;- uint8_t has_prototype : 1; /* true if a prototype field is necessary */- uint8_t has_simple_parameter_list : 1;- uint8_t is_derived_class_constructor : 1;- /* true if home_object needs to be initialized */- uint8_t need_home_object : 1;- uint8_t func_kind : 2;- uint8_t new_target_allowed : 1;- uint8_t super_call_allowed : 1;- uint8_t super_allowed : 1;- uint8_t arguments_allowed : 1;- uint8_t has_debug : 1;- uint8_t backtrace_barrier : 1; /* stop backtrace on this function */- uint8_t read_only_bytecode : 1;- /* XXX: 4 bits available */- uint8_t *byte_code_buf; /* (self pointer) */- int byte_code_len;- JSAtom func_name;- JSVarDef *vardefs; /* arguments + local variables (arg_count + var_count) (self pointer) */- JSClosureVar *closure_var; /* list of variables in the closure (self pointer) */- uint16_t arg_count;- uint16_t var_count;- uint16_t defined_arg_count; /* for length function property */- uint16_t stack_size; /* maximum stack size */- JSContext *realm; /* function realm */- JSValue *cpool; /* constant pool (self pointer) */- int cpool_count;- int closure_var_count;- struct {- /* debug info, move to separate structure to save memory? */- JSAtom filename;- int line_num;- int source_len;- int pc2line_len;- uint8_t *pc2line_buf;- char *source;- } debug;-} JSFunctionBytecode;--typedef struct JSBoundFunction {- JSValue func_obj;- JSValue this_val;- int argc;- JSValue argv[0];-} JSBoundFunction;--typedef enum JSIteratorKindEnum {- JS_ITERATOR_KIND_KEY,- JS_ITERATOR_KIND_VALUE,- JS_ITERATOR_KIND_KEY_AND_VALUE,-} JSIteratorKindEnum;--typedef struct JSForInIterator {- JSValue obj;- BOOL is_array;- uint32_t array_length;- uint32_t idx;-} JSForInIterator;--typedef struct JSRegExp {- JSString *pattern;- JSString *bytecode; /* also contains the flags */-} JSRegExp;--typedef struct JSProxyData {- JSValue target;- JSValue handler;- uint8_t is_func;- uint8_t is_revoked;-} JSProxyData;--typedef struct JSArrayBuffer {- int byte_length; /* 0 if detached */- uint8_t detached;- uint8_t shared; /* if shared, the array buffer cannot be detached */- uint8_t *data; /* NULL if detached */- struct list_head array_list;- void *opaque;- JSFreeArrayBufferDataFunc *free_func;-} JSArrayBuffer;--typedef struct JSTypedArray {- struct list_head link; /* link to arraybuffer */- JSObject *obj; /* back pointer to the TypedArray/DataView object */- JSObject *buffer; /* based array buffer */- uint32_t offset; /* offset in the array buffer */- uint32_t length; /* length in the array buffer */-} JSTypedArray;--typedef struct JSAsyncFunctionState {- JSValue this_val; /* 'this' generator argument */- int argc; /* number of function arguments */- BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */- JSStackFrame frame;-} JSAsyncFunctionState;--/* XXX: could use an object instead to avoid the- JS_TAG_ASYNC_FUNCTION tag for the GC */-typedef struct JSAsyncFunctionData {- JSGCObjectHeader header; /* must come first */- JSValue resolving_funcs[2];- BOOL is_active; /* true if the async function state is valid */- JSAsyncFunctionState func_state;-} JSAsyncFunctionData;--typedef enum {- /* binary operators */- JS_OVOP_ADD,- JS_OVOP_SUB,- JS_OVOP_MUL,- JS_OVOP_DIV,- JS_OVOP_MOD,- JS_OVOP_POW,- JS_OVOP_OR,- JS_OVOP_AND,- JS_OVOP_XOR,- JS_OVOP_SHL,- JS_OVOP_SAR,- JS_OVOP_SHR,- JS_OVOP_EQ,- JS_OVOP_LESS,-- JS_OVOP_BINARY_COUNT,- /* unary operators */- JS_OVOP_POS = JS_OVOP_BINARY_COUNT,- JS_OVOP_NEG,- JS_OVOP_INC,- JS_OVOP_DEC,- JS_OVOP_NOT,-- JS_OVOP_COUNT,-} JSOverloadableOperatorEnum;--typedef struct {- uint32_t operator_index;- JSObject *ops[JS_OVOP_BINARY_COUNT]; /* self operators */-} JSBinaryOperatorDefEntry;--typedef struct {- int count;- JSBinaryOperatorDefEntry *tab;-} JSBinaryOperatorDef;--typedef struct {- uint32_t operator_counter;- BOOL is_primitive; /* OperatorSet for a primitive type */- /* NULL if no operator is defined */- JSObject *self_ops[JS_OVOP_COUNT]; /* self operators */- JSBinaryOperatorDef left;- JSBinaryOperatorDef right;-} JSOperatorSetData;--typedef struct JSReqModuleEntry {- JSAtom module_name;- JSModuleDef *module; /* used using resolution */-} JSReqModuleEntry;--typedef enum JSExportTypeEnum {- JS_EXPORT_TYPE_LOCAL,- JS_EXPORT_TYPE_INDIRECT,-} JSExportTypeEnum;--typedef struct JSExportEntry {- union {- struct {- int var_idx; /* closure variable index */- JSVarRef *var_ref; /* if != NULL, reference to the variable */- } local; /* for local export */- int req_module_idx; /* module for indirect export */- } u;- JSExportTypeEnum export_type;- JSAtom local_name; /* '*' if export ns from. not used for local- export after compilation */- JSAtom export_name; /* exported variable name */-} JSExportEntry;--typedef struct JSStarExportEntry {- int req_module_idx; /* in req_module_entries */-} JSStarExportEntry;--typedef struct JSImportEntry {- int var_idx; /* closure variable index */- JSAtom import_name;- int req_module_idx; /* in req_module_entries */-} JSImportEntry;--struct JSModuleDef {- JSRefCountHeader header; /* must come first, 32-bit */- JSAtom module_name;- struct list_head link;-- JSReqModuleEntry *req_module_entries;- int req_module_entries_count;- int req_module_entries_size;-- JSExportEntry *export_entries;- int export_entries_count;- int export_entries_size;-- JSStarExportEntry *star_export_entries;- int star_export_entries_count;- int star_export_entries_size;-- JSImportEntry *import_entries;- int import_entries_count;- int import_entries_size;-- JSValue module_ns;- JSValue func_obj; /* only used for JS modules */- JSModuleInitFunc *init_func; /* only used for C modules */- BOOL resolved : 8;- BOOL func_created : 8;- BOOL instantiated : 8;- BOOL evaluated : 8;- BOOL eval_mark : 8; /* temporary use during js_evaluate_module() */- /* true if evaluation yielded an exception. It is saved in- eval_exception */- BOOL eval_has_exception : 8; - JSValue eval_exception;- JSValue meta_obj; /* for import.meta */-};--typedef struct JSJobEntry {- struct list_head link;- JSContext *ctx;- JSJobFunc *job_func;- int argc;- JSValue argv[0];-} JSJobEntry;--typedef struct JSProperty {- union {- JSValue value; /* JS_PROP_NORMAL */- struct { /* JS_PROP_GETSET */- JSObject *getter; /* NULL if undefined */- JSObject *setter; /* NULL if undefined */- } getset;- JSVarRef *var_ref; /* JS_PROP_VARREF */- struct { /* JS_PROP_AUTOINIT */- /* in order to use only 2 pointers, we compress the realm- and the init function pointer */- uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x)- in the 2 low bits */- void *opaque;- } init;- } u;-} JSProperty;--#define JS_PROP_INITIAL_SIZE 2-#define JS_PROP_INITIAL_HASH_SIZE 4 /* must be a power of two */-#define JS_ARRAY_INITIAL_SIZE 2--typedef struct JSShapeProperty {- uint32_t hash_next : 26; /* 0 if last in list */- uint32_t flags : 6; /* JS_PROP_XXX */- JSAtom atom; /* JS_ATOM_NULL = free property entry */-} JSShapeProperty;--struct JSShape {- /* hash table of size hash_mask + 1 before the start of the- structure (see prop_hash_end()). */- JSGCObjectHeader header;- /* true if the shape is inserted in the shape hash table. If not,- JSShape.hash is not valid */- uint8_t is_hashed;- /* If true, the shape may have small array index properties 'n' with 0- <= n <= 2^31-1. If false, the shape is guaranteed not to have- small array index properties */- uint8_t has_small_array_index;- uint32_t hash; /* current hash value */- uint32_t prop_hash_mask;- int prop_size; /* allocated properties */- int prop_count; /* include deleted properties */- int deleted_prop_count;- JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */- JSObject *proto;- JSShapeProperty prop[0]; /* prop_size elements */-};--struct JSObject {- union {- JSGCObjectHeader header;- struct {- int __gc_ref_count; /* corresponds to header.ref_count */- uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */- - uint8_t extensible : 1;- uint8_t free_mark : 1; /* only used when freeing objects with cycles */- uint8_t is_exotic : 1; /* TRUE if object has exotic property handlers */- uint8_t fast_array : 1; /* TRUE if u.array is used for get/put */- uint8_t is_constructor : 1; /* TRUE if object is a constructor function */- uint8_t is_uncatchable_error : 1; /* if TRUE, error is not catchable */- uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */- uint8_t is_HTMLDDA : 1; /* specific annex B IsHtmlDDA behavior */- uint16_t class_id; /* see JS_CLASS_x */- };- };- /* byte offsets: 16/24 */- JSShape *shape; /* prototype and property names + flag */- JSProperty *prop; /* array of properties */- /* byte offsets: 24/40 */- struct JSMapRecord *first_weak_ref; /* XXX: use a bit and an external hash table? */- /* byte offsets: 28/48 */- union {- void *opaque;- struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */- struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */- struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */- struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */- struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */-#ifdef CONFIG_BIGNUM- struct JSFloatEnv *float_env; /* JS_CLASS_FLOAT_ENV */- struct JSOperatorSetData *operator_set; /* JS_CLASS_OPERATOR_SET */-#endif- struct JSMapState *map_state; /* JS_CLASS_MAP..JS_CLASS_WEAKSET */- struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */- struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */- struct JSRegExpStringIteratorData *regexp_string_iterator_data; /* JS_CLASS_REGEXP_STRING_ITERATOR */- struct JSGeneratorData *generator_data; /* JS_CLASS_GENERATOR */- struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */- struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */- struct JSPromiseFunctionData *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, JS_CLASS_PROMISE_REJECT_FUNCTION */- struct JSAsyncFunctionData *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */- struct JSAsyncFromSyncIteratorData *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */- struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */- struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */- /* also used by JS_CLASS_GENERATOR_FUNCTION, JS_CLASS_ASYNC_FUNCTION and JS_CLASS_ASYNC_GENERATOR_FUNCTION */- struct JSFunctionBytecode *function_bytecode;- JSVarRef **var_refs;- JSObject *home_object; /* for 'super' access */- } func;- struct { /* JS_CLASS_C_FUNCTION: 12/20 bytes */- JSContext *realm;- JSCFunctionType c_function;- uint8_t length;- uint8_t cproto;- int16_t magic;- } cfunc;- /* array part for fast arrays and typed arrays */- struct { /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */- union {- uint32_t size; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */- struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */- } u1;- union {- JSValue *values; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ - void *ptr; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */- int8_t *int8_ptr; /* JS_CLASS_INT8_ARRAY */- uint8_t *uint8_ptr; /* JS_CLASS_UINT8_ARRAY, JS_CLASS_UINT8C_ARRAY */- int16_t *int16_ptr; /* JS_CLASS_INT16_ARRAY */- uint16_t *uint16_ptr; /* JS_CLASS_UINT16_ARRAY */- int32_t *int32_ptr; /* JS_CLASS_INT32_ARRAY */- uint32_t *uint32_ptr; /* JS_CLASS_UINT32_ARRAY */- int64_t *int64_ptr; /* JS_CLASS_INT64_ARRAY */- uint64_t *uint64_ptr; /* JS_CLASS_UINT64_ARRAY */- float *float_ptr; /* JS_CLASS_FLOAT32_ARRAY */- double *double_ptr; /* JS_CLASS_FLOAT64_ARRAY */- } u;- uint32_t count; /* <= 2^31-1. 0 for a detached typed array */- } array; /* 12/20 bytes */- JSRegExp regexp; /* JS_CLASS_REGEXP: 8/16 bytes */- JSValue object_data; /* for JS_SetObjectData(): 8/16/16 bytes */- } u;- /* byte sizes: 40/48/72 */-};-enum {- __JS_ATOM_NULL = JS_ATOM_NULL,-#define DEF(name, str) JS_ATOM_ ## name,-#include "quickjs-atom.h"-#undef DEF- JS_ATOM_END,-};-#define JS_ATOM_LAST_KEYWORD JS_ATOM_super-#define JS_ATOM_LAST_STRICT_KEYWORD JS_ATOM_yield--static const char js_atom_init[] =-#define DEF(name, str) str "\0"-#include "quickjs-atom.h"-#undef DEF-;--typedef enum OPCodeFormat {-#define FMT(f) OP_FMT_ ## f,-#define DEF(id, size, n_pop, n_push, f)-#include "quickjs-opcode.h"-#undef DEF-#undef FMT-} OPCodeFormat;--enum OPCodeEnum {-#define FMT(f)-#define DEF(id, size, n_pop, n_push, f) OP_ ## id,-#define def(id, size, n_pop, n_push, f)-#include "quickjs-opcode.h"-#undef def-#undef DEF-#undef FMT- OP_COUNT, /* excluding temporary opcodes */- /* temporary opcodes : overlap with the short opcodes */- OP_TEMP_START = OP_nop + 1,- OP___dummy = OP_TEMP_START - 1,-#define FMT(f)-#define DEF(id, size, n_pop, n_push, f)-#define def(id, size, n_pop, n_push, f) OP_ ## id,-#include "quickjs-opcode.h"-#undef def-#undef DEF-#undef FMT- OP_TEMP_END,-};--static int JS_InitAtoms(JSRuntime *rt);-static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len,- int atom_type);-static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p);-static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b);-static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,- JSValueConst this_obj,- int argc, JSValueConst *argv, int flags);-static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj,- JSValueConst this_obj,- int argc, JSValueConst *argv, int flags);-static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj,- JSValueConst this_obj, JSValueConst new_target,- int argc, JSValue *argv, int flags);-static JSValue JS_CallConstructorInternal(JSContext *ctx,- JSValueConst func_obj,- JSValueConst new_target,- int argc, JSValue *argv, int flags);-static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj,- int argc, JSValueConst *argv);-static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,- int argc, JSValueConst *argv);-static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,- JSValue val);-static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,- JSValueConst val, int flags, int scope_idx);-JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...);-static __maybe_unused void JS_DumpAtoms(JSRuntime *rt);-static __maybe_unused void JS_DumpString(JSRuntime *rt,- const JSString *p);-static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt);-static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p);-static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p);-static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,- JSValueConst val);-static __maybe_unused void JS_DumpValue(JSContext *ctx, JSValueConst val);-static __maybe_unused void JS_PrintValue(JSContext *ctx,- const char *str,- JSValueConst val);-static __maybe_unused void JS_DumpShapes(JSRuntime *rt);-static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic);-static void js_array_finalizer(JSRuntime *rt, JSValue val);-static void js_array_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func);-static void js_object_data_finalizer(JSRuntime *rt, JSValue val);-static void js_object_data_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func);-static void js_c_function_finalizer(JSRuntime *rt, JSValue val);-static void js_c_function_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func);-static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val);-static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func);-static void js_bound_function_finalizer(JSRuntime *rt, JSValue val);-static void js_bound_function_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func);-static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val);-static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func);-static void js_regexp_finalizer(JSRuntime *rt, JSValue val);-static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val);-static void js_typed_array_finalizer(JSRuntime *rt, JSValue val);-static void js_typed_array_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func);-static void js_proxy_finalizer(JSRuntime *rt, JSValue val);-static void js_proxy_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func);-static void js_map_finalizer(JSRuntime *rt, JSValue val);-static void js_map_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func);-static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val);-static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func);-static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val);-static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func);-static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val);-static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func);-static void js_generator_finalizer(JSRuntime *rt, JSValue obj);-static void js_generator_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func);-static void js_promise_finalizer(JSRuntime *rt, JSValue val);-static void js_promise_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func);-static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val);-static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func);-#ifdef CONFIG_BIGNUM-static void js_operator_set_finalizer(JSRuntime *rt, JSValue val);-static void js_operator_set_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func);-#endif-static JSValue JS_ToStringFree(JSContext *ctx, JSValue val);-static int JS_ToBoolFree(JSContext *ctx, JSValue val);-static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val);-static int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val);-static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val);-static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern,- JSValueConst flags);-static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor,- JSValue pattern, JSValue bc);-static void gc_decref(JSRuntime *rt);-static int JS_NewClass1(JSRuntime *rt, JSClassID class_id,- const JSClassDef *class_def, JSAtom name);--typedef enum JSStrictEqModeEnum {- JS_EQ_STRICT,- JS_EQ_SAME_VALUE,- JS_EQ_SAME_VALUE_ZERO,-} JSStrictEqModeEnum;--static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,- JSStrictEqModeEnum eq_mode);-static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2);-static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2);-static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2);-static JSValue JS_ToObject(JSContext *ctx, JSValueConst val);-static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val);-static JSProperty *add_property(JSContext *ctx,- JSObject *p, JSAtom prop, int prop_flags);-#ifdef CONFIG_BIGNUM-static void js_float_env_finalizer(JSRuntime *rt, JSValue val);-static JSValue JS_NewBigFloat(JSContext *ctx);-static inline bf_t *JS_GetBigFloat(JSValueConst val)-{- JSBigFloat *p = JS_VALUE_GET_PTR(val);- return &p->num;-}-static JSValue JS_NewBigDecimal(JSContext *ctx);-static inline bfdec_t *JS_GetBigDecimal(JSValueConst val)-{- JSBigDecimal *p = JS_VALUE_GET_PTR(val);- return &p->num;-}-static JSValue JS_NewBigInt(JSContext *ctx);-static inline bf_t *JS_GetBigInt(JSValueConst val)-{- JSBigFloat *p = JS_VALUE_GET_PTR(val);- return &p->num;-}-static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val,- BOOL convert_to_safe_integer);-static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val);-static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val);-static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val);-static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf);-static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val);-static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,- BOOL allow_null_or_undefined);-static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val);-#endif-JSValue JS_ThrowOutOfMemory(JSContext *ctx);-static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx);-static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj);-static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,- JSValueConst proto_val, BOOL throw_flag);-static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj);-static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj);-static int js_proxy_isArray(JSContext *ctx, JSValueConst obj);-static int JS_CreateProperty(JSContext *ctx, JSObject *p,- JSAtom prop, JSValueConst val,- JSValueConst getter, JSValueConst setter,- int flags);-static int js_string_memcmp(const JSString *p1, const JSString *p2, int len);-static void reset_weak_ref(JSRuntime *rt, JSObject *p);-static JSValue js_array_buffer_constructor3(JSContext *ctx,- JSValueConst new_target,- uint64_t len, JSClassID class_id,- uint8_t *buf,- JSFreeArrayBufferDataFunc *free_func,- void *opaque, BOOL alloc_flag);-static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj);-static JSValue js_typed_array_constructor(JSContext *ctx,- JSValueConst this_val,- int argc, JSValueConst *argv,- int classid);-static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p);-static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p);-static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx);-static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx,- BOOL is_arg);-static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,- JSValueConst this_obj,- int argc, JSValueConst *argv,- int flags);-static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val);-static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func);-static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,- const char *input, size_t input_len,- const char *filename, int flags, int scope_idx);-static void js_free_module_def(JSContext *ctx, JSModuleDef *m);-static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,- JS_MarkFunc *mark_func);-static JSValue js_import_meta(JSContext *ctx);-static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier);-static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref);-static JSValue js_new_promise_capability(JSContext *ctx,- JSValue *resolving_funcs,- JSValueConst ctor);-static __exception int perform_promise_then(JSContext *ctx,- JSValueConst promise,- JSValueConst *resolve_reject,- JSValueConst *cap_resolving_funcs);-static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic);-static int js_string_compare(JSContext *ctx,- const JSString *p1, const JSString *p2);-static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val);-static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,- JSValue prop, JSValue val, int flags);-static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val);-static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val);-static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val);-static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,- JSObject *p, JSAtom prop);-static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc);-static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s,- JS_MarkFunc *mark_func);-static void JS_AddIntrinsicBasicObjects(JSContext *ctx);-static void js_free_shape(JSRuntime *rt, JSShape *sh);-static void js_free_shape_null(JSRuntime *rt, JSShape *sh);-static int js_shape_prepare_update(JSContext *ctx, JSObject *p,- JSShapeProperty **pprs);-static int init_shape_hash(JSRuntime *rt);-static __exception int js_get_length32(JSContext *ctx, uint32_t *pres,- JSValueConst obj);-static __exception int js_get_length64(JSContext *ctx, int64_t *pres,- JSValueConst obj);-static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len);-static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen,- JSValueConst array_arg);-static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj,- JSValue **arrpp, uint32_t *countp);-static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx,- JSValueConst sync_iter);-static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val);-static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func);-static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj,- JSValueConst this_val,- int argc, JSValueConst *argv, int flags);-static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val);-static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,- JSGCObjectTypeEnum type);-static void remove_gc_object(JSGCObjectHeader *h);-static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s);-static int js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque);-static int js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,- void *opaque);-static int JS_InstantiateFunctionListItem(JSContext *ctx, JSObject *p,- JSAtom atom, void *opaque);-void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag);--static const JSClassExoticMethods js_arguments_exotic_methods;-static const JSClassExoticMethods js_string_exotic_methods;-static const JSClassExoticMethods js_proxy_exotic_methods;-static const JSClassExoticMethods js_module_ns_exotic_methods;-static JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT;--static void js_trigger_gc(JSRuntime *rt, size_t size)-{- BOOL force_gc;-#ifdef FORCE_GC_AT_MALLOC- force_gc = TRUE;-#else- force_gc = ((rt->malloc_state.malloc_size + size) >- rt->malloc_gc_threshold);-#endif- if (force_gc) {-#ifdef DUMP_GC- printf("GC: size=%" PRIu64 "\n",- (uint64_t)rt->malloc_state.malloc_size);-#endif- JS_RunGC(rt);- rt->malloc_gc_threshold = rt->malloc_state.malloc_size +- (rt->malloc_state.malloc_size >> 1);- }-}--static size_t js_malloc_usable_size_unknown(const void *ptr)-{- return 0;-}--void *js_malloc_rt(JSRuntime *rt, size_t size)-{- return rt->mf.js_malloc(&rt->malloc_state, size);-}--void js_free_rt(JSRuntime *rt, void *ptr)-{- rt->mf.js_free(&rt->malloc_state, ptr);-}--void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size)-{- return rt->mf.js_realloc(&rt->malloc_state, ptr, size);-}--size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr)-{- return rt->mf.js_malloc_usable_size(ptr);-}--void *js_mallocz_rt(JSRuntime *rt, size_t size)-{- void *ptr;- ptr = js_malloc_rt(rt, size);- if (!ptr)- return NULL;- return memset(ptr, 0, size);-}--#ifdef CONFIG_BIGNUM-/* called by libbf */-static void *js_bf_realloc(void *opaque, void *ptr, size_t size)-{- JSRuntime *rt = opaque;- return js_realloc_rt(rt, ptr, size);-}-#endif /* CONFIG_BIGNUM */--/* Throw out of memory in case of error */-void *js_malloc(JSContext *ctx, size_t size)-{- void *ptr;- ptr = js_malloc_rt(ctx->rt, size);- if (unlikely(!ptr)) {- JS_ThrowOutOfMemory(ctx);- return NULL;- }- return ptr;-}--/* Throw out of memory in case of error */-void *js_mallocz(JSContext *ctx, size_t size)-{- void *ptr;- ptr = js_mallocz_rt(ctx->rt, size);- if (unlikely(!ptr)) {- JS_ThrowOutOfMemory(ctx);- return NULL;- }- return ptr;-}--void js_free(JSContext *ctx, void *ptr)-{- js_free_rt(ctx->rt, ptr);-}--/* Throw out of memory in case of error */-void *js_realloc(JSContext *ctx, void *ptr, size_t size)-{- void *ret;- ret = js_realloc_rt(ctx->rt, ptr, size);- if (unlikely(!ret && size != 0)) {- JS_ThrowOutOfMemory(ctx);- return NULL;- }- return ret;-}--/* store extra allocated size in *pslack if successful */-void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack)-{- void *ret;- ret = js_realloc_rt(ctx->rt, ptr, size);- if (unlikely(!ret && size != 0)) {- JS_ThrowOutOfMemory(ctx);- return NULL;- }- if (pslack) {- size_t new_size = js_malloc_usable_size_rt(ctx->rt, ret);- *pslack = (new_size > size) ? new_size - size : 0;- }- return ret;-}--size_t js_malloc_usable_size(JSContext *ctx, const void *ptr)-{- return js_malloc_usable_size_rt(ctx->rt, ptr);-}--/* Throw out of memory exception in case of error */-char *js_strndup(JSContext *ctx, const char *s, size_t n)-{- char *ptr;- ptr = js_malloc(ctx, n + 1);- if (ptr) {- memcpy(ptr, s, n);- ptr[n] = '\0';- }- return ptr;-}--char *js_strdup(JSContext *ctx, const char *str)-{- return js_strndup(ctx, str, strlen(str));-}--static no_inline int js_realloc_array(JSContext *ctx, void **parray,- int elem_size, int *psize, int req_size)-{- int new_size;- size_t slack;- void *new_array;- /* XXX: potential arithmetic overflow */- new_size = max_int(req_size, *psize * 3 / 2);- new_array = js_realloc2(ctx, *parray, new_size * elem_size, &slack);- if (!new_array)- return -1;- new_size += slack / elem_size;- *psize = new_size;- *parray = new_array;- return 0;-}--/* resize the array and update its size if req_size > *psize */-static inline int js_resize_array(JSContext *ctx, void **parray, int elem_size,- int *psize, int req_size)-{- if (unlikely(req_size > *psize))- return js_realloc_array(ctx, parray, elem_size, psize, req_size);- else- return 0;-}--static inline void js_dbuf_init(JSContext *ctx, DynBuf *s)-{- dbuf_init2(s, ctx->rt, (DynBufReallocFunc *)js_realloc_rt);-}--static inline int is_digit(int c) {- return c >= '0' && c <= '9';-}--typedef struct JSClassShortDef {- JSAtom class_name;- JSClassFinalizer *finalizer;- JSClassGCMark *gc_mark;-} JSClassShortDef;--static JSClassShortDef const js_std_class_def[] = {- { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_OBJECT */- { JS_ATOM_Array, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARRAY */- { JS_ATOM_Error, NULL, NULL }, /* JS_CLASS_ERROR */- { JS_ATOM_Number, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_NUMBER */- { JS_ATOM_String, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_STRING */- { JS_ATOM_Boolean, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BOOLEAN */- { JS_ATOM_Symbol, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_SYMBOL */- { JS_ATOM_Arguments, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARGUMENTS */- { JS_ATOM_Arguments, NULL, NULL }, /* JS_CLASS_MAPPED_ARGUMENTS */- { JS_ATOM_Date, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_DATE */- { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_MODULE_NS */- { JS_ATOM_Function, js_c_function_finalizer, js_c_function_mark }, /* JS_CLASS_C_FUNCTION */- { JS_ATOM_Function, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_BYTECODE_FUNCTION */- { JS_ATOM_Function, js_bound_function_finalizer, js_bound_function_mark }, /* JS_CLASS_BOUND_FUNCTION */- { JS_ATOM_Function, js_c_function_data_finalizer, js_c_function_data_mark }, /* JS_CLASS_C_FUNCTION_DATA */- { JS_ATOM_GeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_GENERATOR_FUNCTION */- { JS_ATOM_ForInIterator, js_for_in_iterator_finalizer, js_for_in_iterator_mark }, /* JS_CLASS_FOR_IN_ITERATOR */- { JS_ATOM_RegExp, js_regexp_finalizer, NULL }, /* JS_CLASS_REGEXP */- { JS_ATOM_ArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_ARRAY_BUFFER */- { JS_ATOM_SharedArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_SHARED_ARRAY_BUFFER */- { JS_ATOM_Uint8ClampedArray, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8C_ARRAY */- { JS_ATOM_Int8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT8_ARRAY */- { JS_ATOM_Uint8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8_ARRAY */- { JS_ATOM_Int16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT16_ARRAY */- { JS_ATOM_Uint16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT16_ARRAY */- { JS_ATOM_Int32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT32_ARRAY */- { JS_ATOM_Uint32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT32_ARRAY */-#ifdef CONFIG_BIGNUM- { JS_ATOM_BigInt64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_INT64_ARRAY */- { JS_ATOM_BigUint64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_UINT64_ARRAY */-#endif- { JS_ATOM_Float32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT32_ARRAY */- { JS_ATOM_Float64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT64_ARRAY */- { JS_ATOM_DataView, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_DATAVIEW */-#ifdef CONFIG_BIGNUM- { JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_INT */- { JS_ATOM_BigFloat, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_FLOAT */- { JS_ATOM_BigFloatEnv, js_float_env_finalizer, NULL }, /* JS_CLASS_FLOAT_ENV */- { JS_ATOM_BigDecimal, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_DECIMAL */- { JS_ATOM_OperatorSet, js_operator_set_finalizer, js_operator_set_mark }, /* JS_CLASS_OPERATOR_SET */-#endif- { JS_ATOM_Map, js_map_finalizer, js_map_mark }, /* JS_CLASS_MAP */- { JS_ATOM_Set, js_map_finalizer, js_map_mark }, /* JS_CLASS_SET */- { JS_ATOM_WeakMap, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKMAP */- { JS_ATOM_WeakSet, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKSET */- { JS_ATOM_Map_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_MAP_ITERATOR */- { JS_ATOM_Set_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_SET_ITERATOR */- { JS_ATOM_Array_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_ARRAY_ITERATOR */- { JS_ATOM_String_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_STRING_ITERATOR */- { JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */- { JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /* JS_CLASS_GENERATOR */-};--static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab,- int start, int count)-{- JSClassDef cm_s, *cm = &cm_s;- int i, class_id;-- for(i = 0; i < count; i++) {- class_id = i + start;- memset(cm, 0, sizeof(*cm));- cm->finalizer = tab[i].finalizer;- cm->gc_mark = tab[i].gc_mark;- if (JS_NewClass1(rt, class_id, cm, tab[i].class_name) < 0)- return -1;- }- return 0;-}--#ifdef CONFIG_BIGNUM-static JSValue JS_ThrowUnsupportedOperation(JSContext *ctx)-{- return JS_ThrowTypeError(ctx, "unsupported operation");-}--static JSValue invalid_to_string(JSContext *ctx, JSValueConst val)-{- return JS_ThrowUnsupportedOperation(ctx);-}--static JSValue invalid_from_string(JSContext *ctx, const char *buf,- int radix, int flags, slimb_t *pexponent)-{- return JS_NAN;-}--static int invalid_unary_arith(JSContext *ctx,- JSValue *pres, OPCodeEnum op, JSValue op1)-{- JS_FreeValue(ctx, op1);- JS_ThrowUnsupportedOperation(ctx);- return -1;-}--static int invalid_binary_arith(JSContext *ctx, OPCodeEnum op,- JSValue *pres, JSValue op1, JSValue op2)-{- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- JS_ThrowUnsupportedOperation(ctx);- return -1;-}--static JSValue invalid_mul_pow10_to_float64(JSContext *ctx, const bf_t *a,- int64_t exponent)-{- return JS_ThrowUnsupportedOperation(ctx);-}--static int invalid_mul_pow10(JSContext *ctx, JSValue *sp)-{- JS_ThrowUnsupportedOperation(ctx);- return -1;-}--static void set_dummy_numeric_ops(JSNumericOperations *ops)-{- ops->to_string = invalid_to_string;- ops->from_string = invalid_from_string;- ops->unary_arith = invalid_unary_arith;- ops->binary_arith = invalid_binary_arith;- ops->mul_pow10_to_float64 = invalid_mul_pow10_to_float64;- ops->mul_pow10 = invalid_mul_pow10;-}--#endif /* CONFIG_BIGNUM */--#if !defined(CONFIG_STACK_CHECK)-/* no stack limitation */-static inline uint8_t *js_get_stack_pointer(void)-{- return NULL;-}--static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)-{- return FALSE;-}-#else-/* Note: OS and CPU dependent */-static inline uint8_t *js_get_stack_pointer(void)-{- return __builtin_frame_address(0);-}--static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)-{- size_t size;- size = rt->stack_top - js_get_stack_pointer();- return unlikely((size + alloca_size) > rt->stack_size);-}-#endif--JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque)-{- JSRuntime *rt;- JSMallocState ms;-- memset(&ms, 0, sizeof(ms));- ms.opaque = opaque;- ms.malloc_limit = -1;-- rt = mf->js_malloc(&ms, sizeof(JSRuntime));- if (!rt)- return NULL;- memset(rt, 0, sizeof(*rt));- rt->mf = *mf;- if (!rt->mf.js_malloc_usable_size) {- /* use dummy function if none provided */- rt->mf.js_malloc_usable_size = js_malloc_usable_size_unknown;- }- rt->malloc_state = ms;- rt->malloc_gc_threshold = 256 * 1024;--#ifdef CONFIG_BIGNUM- bf_context_init(&rt->bf_ctx, js_bf_realloc, rt);- set_dummy_numeric_ops(&rt->bigint_ops);- set_dummy_numeric_ops(&rt->bigfloat_ops);- set_dummy_numeric_ops(&rt->bigdecimal_ops);-#endif-- init_list_head(&rt->context_list);- init_list_head(&rt->gc_obj_list);- init_list_head(&rt->gc_zero_ref_count_list);- rt->gc_phase = JS_GC_PHASE_NONE;- -#ifdef DUMP_LEAKS- init_list_head(&rt->string_list);-#endif- init_list_head(&rt->job_list);-- if (JS_InitAtoms(rt))- goto fail;-- /* create the object, array and function classes */- if (init_class_range(rt, js_std_class_def, JS_CLASS_OBJECT,- countof(js_std_class_def)) < 0)- goto fail;- rt->class_array[JS_CLASS_ARGUMENTS].exotic = &js_arguments_exotic_methods;- rt->class_array[JS_CLASS_STRING].exotic = &js_string_exotic_methods;- rt->class_array[JS_CLASS_MODULE_NS].exotic = &js_module_ns_exotic_methods;-- rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function;- rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_c_function_data_call;- rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function;- rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_generator_function_call;- if (init_shape_hash(rt))- goto fail;-- rt->stack_top = js_get_stack_pointer();- rt->stack_size = JS_DEFAULT_STACK_SIZE;- rt->current_exception = JS_NULL;-- return rt;- fail:- JS_FreeRuntime(rt);- return NULL;-}--void *JS_GetRuntimeOpaque(JSRuntime *rt)-{- return rt->user_opaque;-}--void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque)-{- rt->user_opaque = opaque;-}--/* default memory allocation functions with memory limitation */-static inline size_t js_def_malloc_usable_size(void *ptr)-{-#if defined(__APPLE__)- return malloc_size(ptr);-#elif defined(_WIN32)- return _msize(ptr);-#elif defined(EMSCRIPTEN)- return 0;-#elif defined(__linux__)- return malloc_usable_size(ptr);-#else- /* change this to `return 0;` if compilation fails */- return malloc_usable_size(ptr);-#endif-}--static void *js_def_malloc(JSMallocState *s, size_t size)-{- void *ptr;-- /* Do not allocate zero bytes: behavior is platform dependent */- assert(size != 0);-- if (unlikely(s->malloc_size + size > s->malloc_limit))- return NULL;-- ptr = malloc(size);- if (!ptr)- return NULL;-- s->malloc_count++;- s->malloc_size += js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD;- return ptr;-}--static void js_def_free(JSMallocState *s, void *ptr)-{- if (!ptr)- return;-- s->malloc_count--;- s->malloc_size -= js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD;- free(ptr);-}--static void *js_def_realloc(JSMallocState *s, void *ptr, size_t size)-{- size_t old_size;-- if (!ptr) {- if (size == 0)- return NULL;- return js_def_malloc(s, size);- }- old_size = js_def_malloc_usable_size(ptr);- if (size == 0) {- s->malloc_count--;- s->malloc_size -= old_size + MALLOC_OVERHEAD;- free(ptr);- return NULL;- }- if (s->malloc_size + size - old_size > s->malloc_limit)- return NULL;-- ptr = realloc(ptr, size);- if (!ptr)- return NULL;-- s->malloc_size += js_def_malloc_usable_size(ptr) - old_size;- return ptr;-}--static const JSMallocFunctions def_malloc_funcs = {- js_def_malloc,- js_def_free,- js_def_realloc,-#if defined(__APPLE__)- malloc_size,-#elif defined(_WIN32)- (size_t (*)(const void *))_msize,-#elif defined(EMSCRIPTEN)- NULL,-#elif defined(__linux__)- (size_t (*)(const void *))malloc_usable_size,-#else- /* change this to `NULL,` if compilation fails */- malloc_usable_size,-#endif-};--JSRuntime *JS_NewRuntime(void)-{- return JS_NewRuntime2(&def_malloc_funcs, NULL);-}--void JS_SetMemoryLimit(JSRuntime *rt, size_t limit)-{- rt->malloc_state.malloc_limit = limit;-}--/* use -1 to disable automatic GC */-void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold)-{- rt->malloc_gc_threshold = gc_threshold;-}--#define malloc(s) malloc_is_forbidden(s)-#define free(p) free_is_forbidden(p)-#define realloc(p,s) realloc_is_forbidden(p,s)--void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque)-{- rt->interrupt_handler = cb;- rt->interrupt_opaque = opaque;-}--void JS_SetCanBlock(JSRuntime *rt, BOOL can_block)-{- rt->can_block = can_block;-}--void JS_SetSharedArrayBufferFunctions(JSRuntime *rt,- const JSSharedArrayBufferFunctions *sf)-{- rt->sab_funcs = *sf;-}--/* return 0 if OK, < 0 if exception */-int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func,- int argc, JSValueConst *argv)-{- JSRuntime *rt = ctx->rt;- JSJobEntry *e;- int i;-- e = js_malloc(ctx, sizeof(*e) + argc * sizeof(JSValue));- if (!e)- return -1;- e->ctx = ctx;- e->job_func = job_func;- e->argc = argc;- for(i = 0; i < argc; i++) {- e->argv[i] = JS_DupValue(ctx, argv[i]);- }- list_add_tail(&e->link, &rt->job_list);- return 0;-}--BOOL JS_IsJobPending(JSRuntime *rt)-{- return !list_empty(&rt->job_list);-}--/* return < 0 if exception, 0 if no job pending, 1 if a job was- executed successfully. the context of the job is stored in '*pctx' */-int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx)-{- JSContext *ctx;- JSJobEntry *e;- JSValue res;- int i, ret;-- if (list_empty(&rt->job_list)) {- *pctx = NULL;- return 0;- }-- /* get the first pending job and execute it */- e = list_entry(rt->job_list.next, JSJobEntry, link);- list_del(&e->link);- ctx = e->ctx;- res = e->job_func(e->ctx, e->argc, (JSValueConst *)e->argv);- for(i = 0; i < e->argc; i++)- JS_FreeValue(ctx, e->argv[i]);- if (JS_IsException(res))- ret = -1;- else- ret = 1;- JS_FreeValue(ctx, res);- js_free(ctx, e);- *pctx = ctx;- return ret;-}--static inline uint32_t atom_get_free(const JSAtomStruct *p)-{- return (uintptr_t)p >> 1;-}--static inline BOOL atom_is_free(const JSAtomStruct *p)-{- return (uintptr_t)p & 1;-}--static inline JSAtomStruct *atom_set_free(uint32_t v)-{- return (JSAtomStruct *)(((uintptr_t)v << 1) | 1);-}--/* Note: the string contents are uninitialized */-static JSString *js_alloc_string_rt(JSRuntime *rt, int max_len, int is_wide_char)-{- JSString *str;- str = js_malloc_rt(rt, sizeof(JSString) + (max_len << is_wide_char) + 1 - is_wide_char);- if (unlikely(!str))- return NULL;- str->header.ref_count = 1;- str->is_wide_char = is_wide_char;- str->len = max_len;- str->atom_type = 0;- str->hash = 0; /* optional but costless */- str->hash_next = 0; /* optional */-#ifdef DUMP_LEAKS- list_add_tail(&str->link, &rt->string_list);-#endif- return str;-}--static JSString *js_alloc_string(JSContext *ctx, int max_len, int is_wide_char)-{- JSString *p;- p = js_alloc_string_rt(ctx->rt, max_len, is_wide_char);- if (unlikely(!p)) {- JS_ThrowOutOfMemory(ctx);- return NULL;- }- return p;-}--/* same as JS_FreeValueRT() but faster */-static inline void js_free_string(JSRuntime *rt, JSString *str)-{- if (--str->header.ref_count <= 0) {- if (str->atom_type) {- JS_FreeAtomStruct(rt, str);- } else {-#ifdef DUMP_LEAKS- list_del(&str->link);-#endif- js_free_rt(rt, str);- }- }-}--void JS_SetRuntimeInfo(JSRuntime *rt, const char *s)-{- if (rt)- rt->rt_info = s;-}--void JS_FreeRuntime(JSRuntime *rt)-{- struct list_head *el, *el1;- int i;-- JS_FreeValueRT(rt, rt->current_exception);-- list_for_each_safe(el, el1, &rt->job_list) {- JSJobEntry *e = list_entry(el, JSJobEntry, link);- for(i = 0; i < e->argc; i++)- JS_FreeValueRT(rt, e->argv[i]);- js_free_rt(rt, e);- }- init_list_head(&rt->job_list);-- JS_RunGC(rt);--#ifdef DUMP_LEAKS- /* leaking objects */- {- BOOL header_done;- JSGCObjectHeader *p;- int count;-- /* remove the internal refcounts to display only the object- referenced externally */- list_for_each(el, &rt->gc_obj_list) {- p = list_entry(el, JSGCObjectHeader, link);- p->mark = 0;- }- gc_decref(rt);-- header_done = FALSE;- list_for_each(el, &rt->gc_obj_list) {- p = list_entry(el, JSGCObjectHeader, link);- if (p->ref_count != 0) {- if (!header_done) {- printf("Object leaks:\n");- JS_DumpObjectHeader(rt);- header_done = TRUE;- }- JS_DumpGCObject(rt, p);- }- }-- count = 0;- list_for_each(el, &rt->gc_obj_list) {- p = list_entry(el, JSGCObjectHeader, link);- if (p->ref_count == 0) {- count++;- }- }- if (count != 0)- printf("Secondary object leaks: %d\n", count);- }-#endif- assert(list_empty(&rt->gc_obj_list));-- /* free the classes */- for(i = 0; i < rt->class_count; i++) {- JSClass *cl = &rt->class_array[i];- if (cl->class_id != 0) {- JS_FreeAtomRT(rt, cl->class_name);- }- }- js_free_rt(rt, rt->class_array);--#ifdef CONFIG_BIGNUM- bf_context_end(&rt->bf_ctx);-#endif--#ifdef DUMP_LEAKS- /* only the atoms defined in JS_InitAtoms() should be left */- {- BOOL header_done = FALSE;-- for(i = 0; i < rt->atom_size; i++) {- JSAtomStruct *p = rt->atom_array[i];- if (!atom_is_free(p) /* && p->str*/) {- if (i >= JS_ATOM_END || p->header.ref_count != 1) {- if (!header_done) {- header_done = TRUE;- if (rt->rt_info) {- printf("%s:1: atom leakage:", rt->rt_info);- } else {- printf("Atom leaks:\n"- " %6s %6s %s\n",- "ID", "REFCNT", "NAME");- }- }- if (rt->rt_info) {- printf(" ");- } else {- printf(" %6u %6u ", i, p->header.ref_count);- }- switch (p->atom_type) {- case JS_ATOM_TYPE_STRING:- JS_DumpString(rt, p);- break;- case JS_ATOM_TYPE_GLOBAL_SYMBOL:- printf("Symbol.for(");- JS_DumpString(rt, p);- printf(")");- break;- case JS_ATOM_TYPE_SYMBOL:- if (p->hash == JS_ATOM_HASH_SYMBOL) {- printf("Symbol(");- JS_DumpString(rt, p);- printf(")");- } else {- printf("Private(");- JS_DumpString(rt, p);- printf(")");- }- break;- }- if (rt->rt_info) {- printf(":%u", p->header.ref_count);- } else {- printf("\n");- }- }- }- }- if (rt->rt_info && header_done)- printf("\n");- }-#endif-- /* free the atoms */- for(i = 0; i < rt->atom_size; i++) {- JSAtomStruct *p = rt->atom_array[i];- if (!atom_is_free(p)) {-#ifdef DUMP_LEAKS- list_del(&p->link);-#endif- js_free_rt(rt, p);- }- }- js_free_rt(rt, rt->atom_array);- js_free_rt(rt, rt->atom_hash);- js_free_rt(rt, rt->shape_hash);-#ifdef DUMP_LEAKS- if (!list_empty(&rt->string_list)) {- if (rt->rt_info) {- printf("%s:1: string leakage:", rt->rt_info);- } else {- printf("String leaks:\n"- " %6s %s\n",- "REFCNT", "VALUE");- }- list_for_each_safe(el, el1, &rt->string_list) {- JSString *str = list_entry(el, JSString, link);- if (rt->rt_info) {- printf(" ");- } else {- printf(" %6u ", str->header.ref_count);- }- JS_DumpString(rt, str);- if (rt->rt_info) {- printf(":%u", str->header.ref_count);- } else {- printf("\n");- }- list_del(&str->link);- js_free_rt(rt, str);- }- if (rt->rt_info)- printf("\n");- }- {- JSMallocState *s = &rt->malloc_state;- if (s->malloc_count > 1) {- if (rt->rt_info)- printf("%s:1: ", rt->rt_info);- printf("Memory leak: %"PRIu64" bytes lost in %"PRIu64" block%s\n",- (uint64_t)(s->malloc_size - sizeof(JSRuntime)),- (uint64_t)(s->malloc_count - 1), &"s"[s->malloc_count == 2]);- }- }-#endif-- {- JSMallocState ms = rt->malloc_state;- rt->mf.js_free(&ms, rt);- }-}--JSContext *JS_NewContextRaw(JSRuntime *rt)-{- JSContext *ctx;- int i;-- ctx = js_mallocz_rt(rt, sizeof(JSContext));- if (!ctx)- return NULL;- ctx->header.ref_count = 1;- add_gc_object(rt, &ctx->header, JS_GC_OBJ_TYPE_JS_CONTEXT);-- ctx->class_proto = js_malloc_rt(rt, sizeof(ctx->class_proto[0]) *- rt->class_count);- if (!ctx->class_proto) {- js_free_rt(rt, ctx);- return NULL;- }- ctx->rt = rt;- list_add_tail(&ctx->link, &rt->context_list);-#ifdef CONFIG_BIGNUM- ctx->bf_ctx = &rt->bf_ctx;- ctx->fp_env.prec = 113;- ctx->fp_env.flags = bf_set_exp_bits(15) | BF_RNDN | BF_FLAG_SUBNORMAL;-#endif- for(i = 0; i < rt->class_count; i++)- ctx->class_proto[i] = JS_NULL;- ctx->array_ctor = JS_NULL;- ctx->regexp_ctor = JS_NULL;- ctx->promise_ctor = JS_NULL;- init_list_head(&ctx->loaded_modules);-- JS_AddIntrinsicBasicObjects(ctx);- return ctx;-}--JSContext *JS_NewContext(JSRuntime *rt)-{- JSContext *ctx;-- ctx = JS_NewContextRaw(rt);- if (!ctx)- return NULL;-- JS_AddIntrinsicBaseObjects(ctx);- JS_AddIntrinsicDate(ctx);- JS_AddIntrinsicEval(ctx);- JS_AddIntrinsicStringNormalize(ctx);- JS_AddIntrinsicRegExp(ctx);- JS_AddIntrinsicJSON(ctx);- JS_AddIntrinsicProxy(ctx);- JS_AddIntrinsicMapSet(ctx);- JS_AddIntrinsicTypedArrays(ctx);- JS_AddIntrinsicPromise(ctx);-#ifdef CONFIG_BIGNUM- JS_AddIntrinsicBigInt(ctx);-#endif- return ctx;-}--void *JS_GetContextOpaque(JSContext *ctx)-{- return ctx->user_opaque;-}--void JS_SetContextOpaque(JSContext *ctx, void *opaque)-{- ctx->user_opaque = opaque;-}--/* set the new value and free the old value after (freeing the value- can reallocate the object data) */-static inline void set_value(JSContext *ctx, JSValue *pval, JSValue new_val)-{- JSValue old_val;- old_val = *pval;- *pval = new_val;- JS_FreeValue(ctx, old_val);-}--void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj)-{- JSRuntime *rt = ctx->rt;- assert(class_id < rt->class_count);- set_value(ctx, &ctx->class_proto[class_id], obj);-}--JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id)-{- JSRuntime *rt = ctx->rt;- assert(class_id < rt->class_count);- return JS_DupValue(ctx, ctx->class_proto[class_id]);-}--typedef enum JSFreeModuleEnum {- JS_FREE_MODULE_ALL,- JS_FREE_MODULE_NOT_RESOLVED,- JS_FREE_MODULE_NOT_EVALUATED,-} JSFreeModuleEnum;--/* XXX: would be more efficient with separate module lists */-static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag)-{- struct list_head *el, *el1;- list_for_each_safe(el, el1, &ctx->loaded_modules) {- JSModuleDef *m = list_entry(el, JSModuleDef, link);- if (flag == JS_FREE_MODULE_ALL ||- (flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved) ||- (flag == JS_FREE_MODULE_NOT_EVALUATED && !m->evaluated)) {- js_free_module_def(ctx, m);- }- }-}--JSContext *JS_DupContext(JSContext *ctx)-{- ctx->header.ref_count++;- return ctx;-}--/* used by the GC */-static void JS_MarkContext(JSRuntime *rt, JSContext *ctx,- JS_MarkFunc *mark_func)-{- int i;- struct list_head *el;-- /* modules are not seen by the GC, so we directly mark the objects- referenced by each module */- list_for_each(el, &ctx->loaded_modules) {- JSModuleDef *m = list_entry(el, JSModuleDef, link);- js_mark_module_def(rt, m, mark_func);- }-- JS_MarkValue(rt, ctx->global_obj, mark_func);- JS_MarkValue(rt, ctx->global_var_obj, mark_func);-- JS_MarkValue(rt, ctx->throw_type_error, mark_func);- JS_MarkValue(rt, ctx->eval_obj, mark_func);-- JS_MarkValue(rt, ctx->array_proto_values, mark_func);- for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {- JS_MarkValue(rt, ctx->native_error_proto[i], mark_func);- }- for(i = 0; i < rt->class_count; i++) {- JS_MarkValue(rt, ctx->class_proto[i], mark_func);- }- JS_MarkValue(rt, ctx->iterator_proto, mark_func);- JS_MarkValue(rt, ctx->async_iterator_proto, mark_func);- JS_MarkValue(rt, ctx->promise_ctor, mark_func);- JS_MarkValue(rt, ctx->array_ctor, mark_func);- JS_MarkValue(rt, ctx->regexp_ctor, mark_func);- JS_MarkValue(rt, ctx->function_ctor, mark_func);- JS_MarkValue(rt, ctx->function_proto, mark_func);-- if (ctx->array_shape)- mark_func(rt, &ctx->array_shape->header);-}--void JS_FreeContext(JSContext *ctx)-{- JSRuntime *rt = ctx->rt;- int i;-- if (--ctx->header.ref_count > 0)- return;- assert(ctx->header.ref_count == 0);- -#ifdef DUMP_ATOMS- JS_DumpAtoms(ctx->rt);-#endif-#ifdef DUMP_SHAPES- JS_DumpShapes(ctx->rt);-#endif-#ifdef DUMP_OBJECTS- {- struct list_head *el;- JSGCObjectHeader *p;- printf("JSObjects: {\n");- JS_DumpObjectHeader(ctx->rt);- list_for_each(el, &rt->gc_obj_list) {- p = list_entry(el, JSGCObjectHeader, link);- JS_DumpGCObject(rt, p);- }- printf("}\n");- }-#endif-#ifdef DUMP_MEM- {- JSMemoryUsage stats;- JS_ComputeMemoryUsage(rt, &stats);- JS_DumpMemoryUsage(stdout, &stats, rt);- }-#endif-- js_free_modules(ctx, JS_FREE_MODULE_ALL);-- JS_FreeValue(ctx, ctx->global_obj);- JS_FreeValue(ctx, ctx->global_var_obj);-- JS_FreeValue(ctx, ctx->throw_type_error);- JS_FreeValue(ctx, ctx->eval_obj);-- JS_FreeValue(ctx, ctx->array_proto_values);- for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {- JS_FreeValue(ctx, ctx->native_error_proto[i]);- }- for(i = 0; i < rt->class_count; i++) {- JS_FreeValue(ctx, ctx->class_proto[i]);- }- js_free_rt(rt, ctx->class_proto);- JS_FreeValue(ctx, ctx->iterator_proto);- JS_FreeValue(ctx, ctx->async_iterator_proto);- JS_FreeValue(ctx, ctx->promise_ctor);- JS_FreeValue(ctx, ctx->array_ctor);- JS_FreeValue(ctx, ctx->regexp_ctor);- JS_FreeValue(ctx, ctx->function_ctor);- JS_FreeValue(ctx, ctx->function_proto);-- js_free_shape_null(ctx->rt, ctx->array_shape);-- list_del(&ctx->link);- remove_gc_object(&ctx->header);- js_free_rt(ctx->rt, ctx);-}--JSRuntime *JS_GetRuntime(JSContext *ctx)-{- return ctx->rt;-}--void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size)-{- rt->stack_size = stack_size;-}--static inline BOOL is_strict_mode(JSContext *ctx)-{- JSStackFrame *sf = ctx->rt->current_stack_frame;- return (sf && (sf->js_mode & JS_MODE_STRICT));-}--#ifdef CONFIG_BIGNUM-static inline BOOL is_math_mode(JSContext *ctx)-{- JSStackFrame *sf = ctx->rt->current_stack_frame;- return (sf && (sf->js_mode & JS_MODE_MATH));-}-#endif--/* JSAtom support */--#define JS_ATOM_TAG_INT (1U << 31)-#define JS_ATOM_MAX_INT (JS_ATOM_TAG_INT - 1)-#define JS_ATOM_MAX ((1U << 30) - 1)--/* return the max count from the hash size */-#define JS_ATOM_COUNT_RESIZE(n) ((n) * 2)--static inline BOOL __JS_AtomIsConst(JSAtom v)-{-#if defined(DUMP_LEAKS) && DUMP_LEAKS > 1- return (int32_t)v <= 0;-#else- return (int32_t)v < JS_ATOM_END;-#endif-}--static inline BOOL __JS_AtomIsTaggedInt(JSAtom v)-{- return (v & JS_ATOM_TAG_INT) != 0;-}--static inline JSAtom __JS_AtomFromUInt32(uint32_t v)-{- return v | JS_ATOM_TAG_INT;-}--static inline uint32_t __JS_AtomToUInt32(JSAtom atom)-{- return atom & ~JS_ATOM_TAG_INT;-}--static inline int is_num(int c)-{- return c >= '0' && c <= '9';-}--/* return TRUE if the string is a number n with 0 <= n <= 2^32-1 */-static inline BOOL is_num_string(uint32_t *pval, const JSString *p)-{- uint32_t n;- uint64_t n64;- int c, i, len;-- len = p->len;- if (len == 0 || len > 10)- return FALSE;- if (p->is_wide_char)- c = p->u.str16[0];- else- c = p->u.str8[0];- if (is_num(c)) {- if (c == '0') {- if (len != 1)- return FALSE;- n = 0;- } else {- n = c - '0';- for(i = 1; i < len; i++) {- if (p->is_wide_char)- c = p->u.str16[i];- else- c = p->u.str8[i];- if (!is_num(c))- return FALSE;- n64 = (uint64_t)n * 10 + (c - '0');- if ((n64 >> 32) != 0)- return FALSE;- n = n64;- }- }- *pval = n;- return TRUE;- } else {- return FALSE;- }-}--/* XXX: could use faster version ? */-static inline uint32_t hash_string8(const uint8_t *str, size_t len, uint32_t h)-{- size_t i;-- for(i = 0; i < len; i++)- h = h * 263 + str[i];- return h;-}--static inline uint32_t hash_string16(const uint16_t *str,- size_t len, uint32_t h)-{- size_t i;-- for(i = 0; i < len; i++)- h = h * 263 + str[i];- return h;-}--static uint32_t hash_string(const JSString *str, uint32_t h)-{- if (str->is_wide_char)- h = hash_string16(str->u.str16, str->len, h);- else- h = hash_string8(str->u.str8, str->len, h);- return h;-}--static __maybe_unused void JS_DumpString(JSRuntime *rt,- const JSString *p)-{- int i, c, sep;-- if (p == NULL) {- printf("<null>");- return;- }- printf("%d", p->header.ref_count);- sep = (p->header.ref_count == 1) ? '\"' : '\'';- putchar(sep);- for(i = 0; i < p->len; i++) {- if (p->is_wide_char)- c = p->u.str16[i];- else- c = p->u.str8[i];- if (c == sep || c == '\\') {- putchar('\\');- putchar(c);- } else if (c >= ' ' && c <= 126) {- putchar(c);- } else if (c == '\n') {- putchar('\\');- putchar('n');- } else {- printf("\\u%04x", c);- }- }- putchar(sep);-}--static __maybe_unused void JS_DumpAtoms(JSRuntime *rt)-{- JSAtomStruct *p;- int h, i;- /* This only dumps hashed atoms, not JS_ATOM_TYPE_SYMBOL atoms */- printf("JSAtom count=%d size=%d hash_size=%d:\n",- rt->atom_count, rt->atom_size, rt->atom_hash_size);- printf("JSAtom hash table: {\n");- for(i = 0; i < rt->atom_hash_size; i++) {- h = rt->atom_hash[i];- if (h) {- printf(" %d:", i);- while (h) {- p = rt->atom_array[h];- printf(" ");- JS_DumpString(rt, p);- h = p->hash_next;- }- printf("\n");- }- }- printf("}\n");- printf("JSAtom table: {\n");- for(i = 0; i < rt->atom_size; i++) {- p = rt->atom_array[i];- if (!atom_is_free(p)) {- printf(" %d: { %d %08x ", i, p->atom_type, p->hash);- if (!(p->len == 0 && p->is_wide_char != 0))- JS_DumpString(rt, p);- printf(" %d }\n", p->hash_next);- }- }- printf("}\n");-}--static int JS_ResizeAtomHash(JSRuntime *rt, int new_hash_size)-{- JSAtomStruct *p;- uint32_t new_hash_mask, h, i, hash_next1, j, *new_hash;-- assert((new_hash_size & (new_hash_size - 1)) == 0); /* power of two */- new_hash_mask = new_hash_size - 1;- new_hash = js_mallocz_rt(rt, sizeof(rt->atom_hash[0]) * new_hash_size);- if (!new_hash)- return -1;- for(i = 0; i < rt->atom_hash_size; i++) {- h = rt->atom_hash[i];- while (h != 0) {- p = rt->atom_array[h];- hash_next1 = p->hash_next;- /* add in new hash table */- j = p->hash & new_hash_mask;- p->hash_next = new_hash[j];- new_hash[j] = h;- h = hash_next1;- }- }- js_free_rt(rt, rt->atom_hash);- rt->atom_hash = new_hash;- rt->atom_hash_size = new_hash_size;- rt->atom_count_resize = JS_ATOM_COUNT_RESIZE(new_hash_size);- // JS_DumpAtoms(rt);- return 0;-}--static int JS_InitAtoms(JSRuntime *rt)-{- int i, len, atom_type;- const char *p;-- rt->atom_hash_size = 0;- rt->atom_hash = NULL;- rt->atom_count = 0;- rt->atom_size = 0;- rt->atom_free_index = 0;- if (JS_ResizeAtomHash(rt, 256)) /* there are at least 195 predefined atoms */- return -1;-- p = js_atom_init;- for(i = 1; i < JS_ATOM_END; i++) {- if (i == JS_ATOM_Private_brand)- atom_type = JS_ATOM_TYPE_PRIVATE;- else if (i >= JS_ATOM_Symbol_toPrimitive)- atom_type = JS_ATOM_TYPE_SYMBOL;- else- atom_type = JS_ATOM_TYPE_STRING;- len = strlen(p);- if (__JS_NewAtomInit(rt, p, len, atom_type) == JS_ATOM_NULL)- return -1;- p = p + len + 1;- }- return 0;-}--static JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v)-{- JSAtomStruct *p;-- if (!__JS_AtomIsConst(v)) {- p = rt->atom_array[v];- p->header.ref_count++;- }- return v;-}--JSAtom JS_DupAtom(JSContext *ctx, JSAtom v)-{- JSRuntime *rt;- JSAtomStruct *p;-- if (!__JS_AtomIsConst(v)) {- rt = ctx->rt;- p = rt->atom_array[v];- p->header.ref_count++;- }- return v;-}--static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v)-{- JSRuntime *rt;- JSAtomStruct *p;-- rt = ctx->rt;- if (__JS_AtomIsTaggedInt(v))- return JS_ATOM_KIND_STRING;- p = rt->atom_array[v];- switch(p->atom_type) {- case JS_ATOM_TYPE_STRING:- return JS_ATOM_KIND_STRING;- case JS_ATOM_TYPE_GLOBAL_SYMBOL:- return JS_ATOM_KIND_SYMBOL;- case JS_ATOM_TYPE_SYMBOL:- switch(p->hash) {- case JS_ATOM_HASH_SYMBOL:- return JS_ATOM_KIND_SYMBOL;- case JS_ATOM_HASH_PRIVATE:- return JS_ATOM_KIND_PRIVATE;- default:- abort();- }- default:- abort();- }-}--static BOOL JS_AtomIsString(JSContext *ctx, JSAtom v)-{- return JS_AtomGetKind(ctx, v) == JS_ATOM_KIND_STRING;-}--static JSAtom js_get_atom_index(JSRuntime *rt, JSAtomStruct *p)-{- uint32_t i = p->hash_next; /* atom_index */- if (p->atom_type != JS_ATOM_TYPE_SYMBOL) {- JSAtomStruct *p1;-- i = rt->atom_hash[p->hash & (rt->atom_hash_size - 1)];- p1 = rt->atom_array[i];- while (p1 != p) {- assert(i != 0);- i = p1->hash_next;- p1 = rt->atom_array[i];- }- }- return i;-}--/* string case (internal). Return JS_ATOM_NULL if error. 'str' is- freed. */-static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type)-{- uint32_t h, h1, i;- JSAtomStruct *p;- int len;--#if 0- printf("__JS_NewAtom: "); JS_DumpString(rt, str); printf("\n");-#endif- if (atom_type < JS_ATOM_TYPE_SYMBOL) {- /* str is not NULL */- if (str->atom_type == atom_type) {- /* str is the atom, return its index */- i = js_get_atom_index(rt, str);- /* reduce string refcount and increase atom's unless constant */- if (__JS_AtomIsConst(i))- str->header.ref_count--;- return i;- }- /* try and locate an already registered atom */- len = str->len;- h = hash_string(str, atom_type);- h &= JS_ATOM_HASH_MASK;- h1 = h & (rt->atom_hash_size - 1);- i = rt->atom_hash[h1];- while (i != 0) {- p = rt->atom_array[i];- if (p->hash == h &&- p->atom_type == atom_type &&- p->len == len &&- js_string_memcmp(p, str, len) == 0) {- if (!__JS_AtomIsConst(i))- p->header.ref_count++;- goto done;- }- i = p->hash_next;- }- } else {- h1 = 0; /* avoid warning */- if (atom_type == JS_ATOM_TYPE_SYMBOL) {- h = JS_ATOM_HASH_SYMBOL;- } else {- h = JS_ATOM_HASH_PRIVATE;- atom_type = JS_ATOM_TYPE_SYMBOL;- }- }-- if (rt->atom_free_index == 0) {- /* allow new atom entries */- uint32_t new_size, start;- JSAtomStruct **new_array;-- /* alloc new with size progression 3/2:- 4 6 9 13 19 28 42 63 94 141 211 316 474 711 1066 1599 2398 3597 5395 8092- preallocating space for predefined atoms (at least 195).- */- new_size = max_int(211, rt->atom_size * 3 / 2);- if (new_size > JS_ATOM_MAX)- goto fail;- /* XXX: should use realloc2 to use slack space */- new_array = js_realloc_rt(rt, rt->atom_array, sizeof(*new_array) * new_size);- if (!new_array)- goto fail;- /* Note: the atom 0 is not used */- start = rt->atom_size;- if (start == 0) {- /* JS_ATOM_NULL entry */- p = js_mallocz_rt(rt, sizeof(JSAtomStruct));- if (!p) {- js_free_rt(rt, new_array);- goto fail;- }- p->header.ref_count = 1; /* not refcounted */- p->atom_type = JS_ATOM_TYPE_SYMBOL;-#ifdef DUMP_LEAKS- list_add_tail(&p->link, &rt->string_list);-#endif- new_array[0] = p;- rt->atom_count++;- start = 1;- }- rt->atom_size = new_size;- rt->atom_array = new_array;- rt->atom_free_index = start;- for(i = start; i < new_size; i++) {- uint32_t next;- if (i == (new_size - 1))- next = 0;- else- next = i + 1;- rt->atom_array[i] = atom_set_free(next);- }- }-- if (str) {- if (str->atom_type == 0) {- p = str;- p->atom_type = atom_type;- } else {- p = js_malloc_rt(rt, sizeof(JSString) +- (str->len << str->is_wide_char) +- 1 - str->is_wide_char);- if (unlikely(!p))- goto fail;- p->header.ref_count = 1;- p->is_wide_char = str->is_wide_char;- p->len = str->len;-#ifdef DUMP_LEAKS- list_add_tail(&p->link, &rt->string_list);-#endif- memcpy(p->u.str8, str->u.str8, (str->len << str->is_wide_char) +- 1 - str->is_wide_char);- js_free_string(rt, str);- }- } else {- p = js_malloc_rt(rt, sizeof(JSAtomStruct)); /* empty wide string */- if (!p)- return JS_ATOM_NULL;- p->header.ref_count = 1;- p->is_wide_char = 1; /* Hack to represent NULL as a JSString */- p->len = 0;-#ifdef DUMP_LEAKS- list_add_tail(&p->link, &rt->string_list);-#endif- }-- /* use an already free entry */- i = rt->atom_free_index;- rt->atom_free_index = atom_get_free(rt->atom_array[i]);- rt->atom_array[i] = p;-- p->hash = h;- p->hash_next = i; /* atom_index */- p->atom_type = atom_type;-- rt->atom_count++;-- if (atom_type != JS_ATOM_TYPE_SYMBOL) {- p->hash_next = rt->atom_hash[h1];- rt->atom_hash[h1] = i;- if (unlikely(rt->atom_count >= rt->atom_count_resize))- JS_ResizeAtomHash(rt, rt->atom_hash_size * 2);- }-- // JS_DumpAtoms(rt);- return i;-- fail:- i = JS_ATOM_NULL;- done:- if (str)- js_free_string(rt, str);- return i;-}--/* only works with zero terminated 8 bit strings */-static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len,- int atom_type)-{- JSString *p;- p = js_alloc_string_rt(rt, len, 0);- if (!p)- return JS_ATOM_NULL;- memcpy(p->u.str8, str, len);- p->u.str8[len] = '\0';- return __JS_NewAtom(rt, p, atom_type);-}--static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len,- int atom_type)-{- uint32_t h, h1, i;- JSAtomStruct *p;-- h = hash_string8((const uint8_t *)str, len, JS_ATOM_TYPE_STRING);- h &= JS_ATOM_HASH_MASK;- h1 = h & (rt->atom_hash_size - 1);- i = rt->atom_hash[h1];- while (i != 0) {- p = rt->atom_array[i];- if (p->hash == h &&- p->atom_type == JS_ATOM_TYPE_STRING &&- p->len == len &&- p->is_wide_char == 0 &&- memcmp(p->u.str8, str, len) == 0) {- if (!__JS_AtomIsConst(i))- p->header.ref_count++;- return i;- }- i = p->hash_next;- }- return JS_ATOM_NULL;-}--static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p)-{-#if 0 /* JS_ATOM_NULL is not refcounted: __JS_AtomIsConst() includes 0 */- if (unlikely(i == JS_ATOM_NULL)) {- p->header.ref_count = INT32_MAX / 2;- return;- }-#endif- uint32_t i = p->hash_next; /* atom_index */- if (p->atom_type != JS_ATOM_TYPE_SYMBOL) {- JSAtomStruct *p0, *p1;- uint32_t h0;-- h0 = p->hash & (rt->atom_hash_size - 1);- i = rt->atom_hash[h0];- p1 = rt->atom_array[i];- if (p1 == p) {- rt->atom_hash[h0] = p1->hash_next;- } else {- for(;;) {- assert(i != 0);- p0 = p1;- i = p1->hash_next;- p1 = rt->atom_array[i];- if (p1 == p) {- p0->hash_next = p1->hash_next;- break;- }- }- }- }- /* insert in free atom list */- rt->atom_array[i] = atom_set_free(rt->atom_free_index);- rt->atom_free_index = i;- /* free the string structure */-#ifdef DUMP_LEAKS- list_del(&p->link);-#endif- js_free_rt(rt, p);- rt->atom_count--;- assert(rt->atom_count >= 0);-}--static void __JS_FreeAtom(JSRuntime *rt, uint32_t i)-{- JSAtomStruct *p;-- p = rt->atom_array[i];- if (--p->header.ref_count > 0)- return;- JS_FreeAtomStruct(rt, p);-}--/* Warning: 'p' is freed */-static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p)-{- JSRuntime *rt = ctx->rt;- uint32_t n;- if (is_num_string(&n, p)) {- if (n <= JS_ATOM_MAX_INT) {- js_free_string(rt, p);- return __JS_AtomFromUInt32(n);- }- }- /* XXX: should generate an exception */- return __JS_NewAtom(rt, p, JS_ATOM_TYPE_STRING);-}--JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len)-{- JSValue val;-- if (len == 0 || !is_digit(*str)) {- JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING);- if (atom)- return atom;- }- val = JS_NewStringLen(ctx, str, len);- if (JS_IsException(val))- return JS_ATOM_NULL;- return JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(val));-}--JSAtom JS_NewAtom(JSContext *ctx, const char *str)-{- return JS_NewAtomLen(ctx, str, strlen(str));-}--JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n)-{- if (n <= JS_ATOM_MAX_INT) {- return __JS_AtomFromUInt32(n);- } else {- char buf[11];- JSValue val;- snprintf(buf, sizeof(buf), "%u", n);- val = JS_NewString(ctx, buf);- if (JS_IsException(val))- return JS_ATOM_NULL;- return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val),- JS_ATOM_TYPE_STRING);- }-}--static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n)-{- if ((uint64_t)n <= JS_ATOM_MAX_INT) {- return __JS_AtomFromUInt32((uint32_t)n);- } else {- char buf[24];- JSValue val;- snprintf(buf, sizeof(buf), "%" PRId64 , n);- val = JS_NewString(ctx, buf);- if (JS_IsException(val))- return JS_ATOM_NULL;- return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val),- JS_ATOM_TYPE_STRING);- }-}--/* 'p' is freed */-static JSValue JS_NewSymbol(JSContext *ctx, JSString *p, int atom_type)-{- JSRuntime *rt = ctx->rt;- JSAtom atom;- atom = __JS_NewAtom(rt, p, atom_type);- if (atom == JS_ATOM_NULL)- return JS_ThrowOutOfMemory(ctx);- return JS_MKPTR(JS_TAG_SYMBOL, rt->atom_array[atom]);-}--/* descr must be a non-numeric string atom */-static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr,- int atom_type)-{- JSRuntime *rt = ctx->rt;- JSString *p;-- assert(!__JS_AtomIsTaggedInt(descr));- assert(descr < rt->atom_size);- p = rt->atom_array[descr];- JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));- return JS_NewSymbol(ctx, p, atom_type);-}--#define ATOM_GET_STR_BUF_SIZE 64--/* Should only be used for debug. */-static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size,- JSAtom atom)-{- if (__JS_AtomIsTaggedInt(atom)) {- snprintf(buf, buf_size, "%u", __JS_AtomToUInt32(atom));- } else {- JSAtomStruct *p;- assert(atom < rt->atom_size);- if (atom == JS_ATOM_NULL) {- snprintf(buf, buf_size, "<null>");- } else {- int i, c;- char *q;- JSString *str;-- q = buf;- p = rt->atom_array[atom];- assert(!atom_is_free(p));- str = p;- if (str) {- if (!str->is_wide_char) {- /* special case ASCII strings */- c = 0;- for(i = 0; i < str->len; i++) {- c |= str->u.str8[i];- }- if (c < 0x80)- return (const char *)str->u.str8;- }- for(i = 0; i < str->len; i++) {- if (str->is_wide_char)- c = str->u.str16[i];- else- c = str->u.str8[i];- if ((q - buf) >= buf_size - UTF8_CHAR_LEN_MAX)- break;- if (c < 128) {- *q++ = c;- } else {- q += unicode_to_utf8((uint8_t *)q, c);- }- }- }- *q = '\0';- }- }- return buf;-}--static const char *JS_AtomGetStr(JSContext *ctx, char *buf, int buf_size, JSAtom atom)-{- return JS_AtomGetStrRT(ctx->rt, buf, buf_size, atom);-}--static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, BOOL force_string)-{- char buf[ATOM_GET_STR_BUF_SIZE];-- if (__JS_AtomIsTaggedInt(atom)) {- snprintf(buf, sizeof(buf), "%u", __JS_AtomToUInt32(atom));- return JS_NewString(ctx, buf);- } else {- JSRuntime *rt = ctx->rt;- JSAtomStruct *p;- assert(atom < rt->atom_size);- p = rt->atom_array[atom];- if (p->atom_type == JS_ATOM_TYPE_STRING) {- goto ret_string;- } else if (force_string) {- if (p->len == 0 && p->is_wide_char != 0) {- /* no description string */- p = rt->atom_array[JS_ATOM_empty_string];- }- ret_string:- return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));- } else {- return JS_DupValue(ctx, JS_MKPTR(JS_TAG_SYMBOL, p));- }- }-}--JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom)-{- return __JS_AtomToValue(ctx, atom, FALSE);-}--JSValue JS_AtomToString(JSContext *ctx, JSAtom atom)-{- return __JS_AtomToValue(ctx, atom, TRUE);-}--/* return TRUE if the atom is an array index (i.e. 0 <= index <=- 2^32-2 and return its value */-static BOOL JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom)-{- if (__JS_AtomIsTaggedInt(atom)) {- *pval = __JS_AtomToUInt32(atom);- return TRUE;- } else {- JSRuntime *rt = ctx->rt;- JSAtomStruct *p;- uint32_t val;-- assert(atom < rt->atom_size);- p = rt->atom_array[atom];- if (p->atom_type == JS_ATOM_TYPE_STRING &&- is_num_string(&val, p) && val != -1) {- *pval = val;- return TRUE;- } else {- *pval = 0;- return FALSE;- }- }-}--/* This test must be fast if atom is not a numeric index (e.g. a- method name). Return JS_UNDEFINED if not a numeric- index. JS_EXCEPTION can also be returned. */-static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom)-{- JSRuntime *rt = ctx->rt;- JSAtomStruct *p1;- JSString *p;- int c, len, ret;- JSValue num, str;-- if (__JS_AtomIsTaggedInt(atom))- return JS_NewInt32(ctx, __JS_AtomToUInt32(atom));- assert(atom < rt->atom_size);- p1 = rt->atom_array[atom];- if (p1->atom_type != JS_ATOM_TYPE_STRING)- return JS_UNDEFINED;- p = p1;- len = p->len;- if (p->is_wide_char) {- const uint16_t *r = p->u.str16, *r_end = p->u.str16 + len;- if (r >= r_end)- return JS_UNDEFINED;- c = *r;- if (c == '-') {- if (r >= r_end)- return JS_UNDEFINED;- r++;- c = *r;- /* -0 case is specific */- if (c == '0' && len == 2)- goto minus_zero;- }- /* XXX: should test NaN, but the tests do not check it */- if (!is_num(c)) {- /* XXX: String should be normalized, therefore 8-bit only */- const uint16_t nfinity16[7] = { 'n', 'f', 'i', 'n', 'i', 't', 'y' };- if (!(c =='I' && (r_end - r) == 8 &&- !memcmp(r + 1, nfinity16, sizeof(nfinity16))))- return JS_UNDEFINED;- }- } else {- const uint8_t *r = p->u.str8, *r_end = p->u.str8 + len;- if (r >= r_end)- return JS_UNDEFINED;- c = *r;- if (c == '-') {- if (r >= r_end)- return JS_UNDEFINED;- r++;- c = *r;- /* -0 case is specific */- if (c == '0' && len == 2) {- minus_zero:- return __JS_NewFloat64(ctx, -0.0);- }- }- if (!is_num(c)) {- if (!(c =='I' && (r_end - r) == 8 &&- !memcmp(r + 1, "nfinity", 7)))- return JS_UNDEFINED;- }- }- /* XXX: bignum: would be better to only accept integer to avoid- relying on current floating point precision */- /* this is ECMA CanonicalNumericIndexString primitive */- num = JS_ToNumber(ctx, JS_MKPTR(JS_TAG_STRING, p));- if (JS_IsException(num))- return num;- str = JS_ToString(ctx, num);- if (JS_IsException(str)) {- JS_FreeValue(ctx, num);- return str;- }- ret = js_string_compare(ctx, p, JS_VALUE_GET_STRING(str));- JS_FreeValue(ctx, str);- if (ret == 0) {- return num;- } else {- JS_FreeValue(ctx, num);- return JS_UNDEFINED;- }-}--/* return -1 if exception or TRUE/FALSE */-static int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom)-{- JSValue num;- num = JS_AtomIsNumericIndex1(ctx, atom);- if (likely(JS_IsUndefined(num)))- return FALSE;- if (JS_IsException(num))- return -1;- JS_FreeValue(ctx, num);- return TRUE;-}--void JS_FreeAtom(JSContext *ctx, JSAtom v)-{- if (!__JS_AtomIsConst(v))- __JS_FreeAtom(ctx->rt, v);-}--void JS_FreeAtomRT(JSRuntime *rt, JSAtom v)-{- if (!__JS_AtomIsConst(v))- __JS_FreeAtom(rt, v);-}--/* return TRUE if 'v' is a symbol with a string description */-static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v)-{- JSRuntime *rt;- JSAtomStruct *p;-- rt = ctx->rt;- if (__JS_AtomIsTaggedInt(v))- return FALSE;- p = rt->atom_array[v];- return (((p->atom_type == JS_ATOM_TYPE_SYMBOL &&- p->hash == JS_ATOM_HASH_SYMBOL) ||- p->atom_type == JS_ATOM_TYPE_GLOBAL_SYMBOL) &&- !(p->len == 0 && p->is_wide_char != 0));-}--static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom)-{- char buf[ATOM_GET_STR_BUF_SIZE];- const char *p;- int i;-- /* XXX: should handle embedded null characters */- /* XXX: should move encoding code to JS_AtomGetStr */- p = JS_AtomGetStr(ctx, buf, sizeof(buf), atom);- for (i = 0; p[i]; i++) {- int c = (unsigned char)p[i];- if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||- (c == '_' || c == '$') || (c >= '0' && c <= '9' && i > 0)))- break;- }- if (i > 0 && p[i] == '\0') {- printf("%s", p);- } else {- putchar('"');- printf("%.*s", i, p);- for (; p[i]; i++) {- int c = (unsigned char)p[i];- if (c == '\"' || c == '\\') {- putchar('\\');- putchar(c);- } else if (c >= ' ' && c <= 126) {- putchar(c);- } else if (c == '\n') {- putchar('\\');- putchar('n');- } else {- printf("\\u%04x", c);- }- }- putchar('\"');- }-}--/* free with JS_FreeCString() */-const char *JS_AtomToCString(JSContext *ctx, JSAtom atom)-{- JSValue str;- const char *cstr;-- str = JS_AtomToString(ctx, atom);- if (JS_IsException(str))- return NULL;- cstr = JS_ToCString(ctx, str);- JS_FreeValue(ctx, str);- return cstr;-}--/* return a string atom containing name concatenated with str1 */-static JSAtom js_atom_concat_str(JSContext *ctx, JSAtom name, const char *str1)-{- JSValue str;- JSAtom atom;- const char *cstr;- char *cstr2;- size_t len, len1;- - str = JS_AtomToString(ctx, name);- if (JS_IsException(str))- return JS_ATOM_NULL;- cstr = JS_ToCStringLen(ctx, &len, str);- if (!cstr)- goto fail;- len1 = strlen(str1);- cstr2 = js_malloc(ctx, len + len1 + 1);- if (!cstr2)- goto fail;- memcpy(cstr2, cstr, len);- memcpy(cstr2 + len, str1, len1);- cstr2[len + len1] = '\0';- atom = JS_NewAtomLen(ctx, cstr2, len + len1);- js_free(ctx, cstr2);- JS_FreeCString(ctx, cstr);- JS_FreeValue(ctx, str);- return atom;- fail:- JS_FreeCString(ctx, cstr);- JS_FreeValue(ctx, str);- return JS_ATOM_NULL;-}--static JSAtom js_atom_concat_num(JSContext *ctx, JSAtom name, uint32_t n)-{- char buf[16];- snprintf(buf, sizeof(buf), "%u", n);- return js_atom_concat_str(ctx, name, buf);-}--static inline BOOL JS_IsEmptyString(JSValueConst v)-{- return JS_VALUE_GET_TAG(v) == JS_TAG_STRING && JS_VALUE_GET_STRING(v)->len == 0;-}--/* JSClass support */--/* a new class ID is allocated if *pclass_id != 0 */-JSClassID JS_NewClassID(JSClassID *pclass_id)-{- JSClassID class_id;- /* XXX: make it thread safe */- class_id = *pclass_id;- if (class_id == 0) {- class_id = js_class_id_alloc++;- *pclass_id = class_id;- }- return class_id;-}--BOOL JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id)-{- return (class_id < rt->class_count &&- rt->class_array[class_id].class_id != 0);-}--/* create a new object internal class. Return -1 if error, 0 if- OK. The finalizer can be NULL if none is needed. */-static int JS_NewClass1(JSRuntime *rt, JSClassID class_id,- const JSClassDef *class_def, JSAtom name)-{- int new_size, i;- JSClass *cl, *new_class_array;- struct list_head *el;-- if (class_id < rt->class_count &&- rt->class_array[class_id].class_id != 0)- return -1;-- if (class_id >= rt->class_count) {- new_size = max_int(JS_CLASS_INIT_COUNT,- max_int(class_id + 1, rt->class_count * 3 / 2));-- /* reallocate the context class prototype array, if any */- list_for_each(el, &rt->context_list) {- JSContext *ctx = list_entry(el, JSContext, link);- JSValue *new_tab;- new_tab = js_realloc_rt(rt, ctx->class_proto,- sizeof(ctx->class_proto[0]) * new_size);- if (!new_tab)- return -1;- for(i = rt->class_count; i < new_size; i++)- new_tab[i] = JS_NULL;- ctx->class_proto = new_tab;- }- /* reallocate the class array */- new_class_array = js_realloc_rt(rt, rt->class_array,- sizeof(JSClass) * new_size);- if (!new_class_array)- return -1;- memset(new_class_array + rt->class_count, 0,- (new_size - rt->class_count) * sizeof(JSClass));- rt->class_array = new_class_array;- rt->class_count = new_size;- }- cl = &rt->class_array[class_id];- cl->class_id = class_id;- cl->class_name = JS_DupAtomRT(rt, name);- cl->finalizer = class_def->finalizer;- cl->gc_mark = class_def->gc_mark;- cl->call = class_def->call;- cl->exotic = class_def->exotic;- return 0;-}--int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def)-{- int ret, len;- JSAtom name;-- len = strlen(class_def->class_name);- name = __JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);- if (name == JS_ATOM_NULL) {- name = __JS_NewAtomInit(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);- if (name == JS_ATOM_NULL)- return -1;- }- ret = JS_NewClass1(rt, class_id, class_def, name);- JS_FreeAtomRT(rt, name);- return ret;-}--static JSValue js_new_string8(JSContext *ctx, const uint8_t *buf, int len)-{- JSString *str;-- if (len <= 0) {- return JS_AtomToString(ctx, JS_ATOM_empty_string);- }- str = js_alloc_string(ctx, len, 0);- if (!str)- return JS_EXCEPTION;- memcpy(str->u.str8, buf, len);- str->u.str8[len] = '\0';- return JS_MKPTR(JS_TAG_STRING, str);-}--static JSValue js_new_string16(JSContext *ctx, const uint16_t *buf, int len)-{- JSString *str;- str = js_alloc_string(ctx, len, 1);- if (!str)- return JS_EXCEPTION;- memcpy(str->u.str16, buf, len * 2);- return JS_MKPTR(JS_TAG_STRING, str);-}--static JSValue js_new_string_char(JSContext *ctx, uint16_t c)-{- if (c < 0x100) {- uint8_t ch8 = c;- return js_new_string8(ctx, &ch8, 1);- } else {- uint16_t ch16 = c;- return js_new_string16(ctx, &ch16, 1);- }-}--static JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end)-{- int len = end - start;- if (start == 0 && end == p->len) {- return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));- }- if (p->is_wide_char && len > 0) {- JSString *str;- int i;- uint16_t c = 0;- for (i = start; i < end; i++) {- c |= p->u.str16[i];- }- if (c > 0xFF)- return js_new_string16(ctx, p->u.str16 + start, len);-- str = js_alloc_string(ctx, len, 0);- if (!str)- return JS_EXCEPTION;- for (i = 0; i < len; i++) {- str->u.str8[i] = p->u.str16[start + i];- }- str->u.str8[len] = '\0';- return JS_MKPTR(JS_TAG_STRING, str);- } else {- return js_new_string8(ctx, p->u.str8 + start, len);- }-}--typedef struct StringBuffer {- JSContext *ctx;- JSString *str;- int len;- int size;- int is_wide_char;- int error_status;-} StringBuffer;--/* It is valid to call string_buffer_end() and all string_buffer functions even- if string_buffer_init() or another string_buffer function returns an error.- If the error_status is set, string_buffer_end() returns JS_EXCEPTION.- */-static int string_buffer_init2(JSContext *ctx, StringBuffer *s, int size,- int is_wide)-{- s->ctx = ctx;- s->size = size;- s->len = 0;- s->is_wide_char = is_wide;- s->error_status = 0;- s->str = js_alloc_string(ctx, size, is_wide);- if (unlikely(!s->str)) {- s->size = 0;- return s->error_status = -1;- }-#ifdef DUMP_LEAKS- /* the StringBuffer may reallocate the JSString, only link it at the end */- list_del(&s->str->link);-#endif- return 0;-}--static inline int string_buffer_init(JSContext *ctx, StringBuffer *s, int size)-{- return string_buffer_init2(ctx, s, size, 0);-}--static void string_buffer_free(StringBuffer *s)-{- js_free(s->ctx, s->str);- s->str = NULL;-}--static int string_buffer_set_error(StringBuffer *s)-{- js_free(s->ctx, s->str);- s->str = NULL;- s->size = 0;- s->len = 0;- return s->error_status = -1;-}--static no_inline int string_buffer_widen(StringBuffer *s, int size)-{- JSString *str;- size_t slack;- int i;-- if (s->error_status)- return -1;-- str = js_realloc2(s->ctx, s->str, sizeof(JSString) + (size << 1), &slack);- if (!str)- return string_buffer_set_error(s);- size += slack >> 1;- for(i = s->len; i-- > 0;) {- str->u.str16[i] = str->u.str8[i];- }- s->is_wide_char = 1;- s->size = size;- s->str = str;- return 0;-}--static no_inline int string_buffer_realloc(StringBuffer *s, int new_len, int c)-{- JSString *new_str;- int new_size;- size_t new_size_bytes, slack;-- if (s->error_status)- return -1;-- if (new_len > JS_STRING_LEN_MAX) {- JS_ThrowInternalError(s->ctx, "string too long");- return string_buffer_set_error(s);- }- new_size = min_int(max_int(new_len, s->size * 3 / 2), JS_STRING_LEN_MAX);- if (!s->is_wide_char && c >= 0x100) {- return string_buffer_widen(s, new_size);- }- new_size_bytes = sizeof(JSString) + (new_size << s->is_wide_char) + 1 - s->is_wide_char;- new_str = js_realloc2(s->ctx, s->str, new_size_bytes, &slack);- if (!new_str)- return string_buffer_set_error(s);- new_size = min_int(new_size + (slack >> s->is_wide_char), JS_STRING_LEN_MAX);- s->size = new_size;- s->str = new_str;- return 0;-}--static no_inline int string_buffer_putc_slow(StringBuffer *s, uint32_t c)-{- if (unlikely(s->len >= s->size)) {- if (string_buffer_realloc(s, s->len + 1, c))- return -1;- }- if (s->is_wide_char) {- s->str->u.str16[s->len++] = c;- } else if (c < 0x100) {- s->str->u.str8[s->len++] = c;- } else {- if (string_buffer_widen(s, s->size))- return -1;- s->str->u.str16[s->len++] = c;- }- return 0;-}--/* 0 <= c <= 0xff */-static int string_buffer_putc8(StringBuffer *s, uint32_t c)-{- if (unlikely(s->len >= s->size)) {- if (string_buffer_realloc(s, s->len + 1, c))- return -1;- }- if (s->is_wide_char) {- s->str->u.str16[s->len++] = c;- } else {- s->str->u.str8[s->len++] = c;- }- return 0;-}--/* 0 <= c <= 0xffff */-static int string_buffer_putc16(StringBuffer *s, uint32_t c)-{- if (likely(s->len < s->size)) {- if (s->is_wide_char) {- s->str->u.str16[s->len++] = c;- return 0;- } else if (c < 0x100) {- s->str->u.str8[s->len++] = c;- return 0;- }- }- return string_buffer_putc_slow(s, c);-}--/* 0 <= c <= 0x10ffff */-static int string_buffer_putc(StringBuffer *s, uint32_t c)-{- if (unlikely(c >= 0x10000)) {- /* surrogate pair */- c -= 0x10000;- if (string_buffer_putc16(s, (c >> 10) + 0xd800))- return -1;- c = (c & 0x3ff) + 0xdc00;- }- return string_buffer_putc16(s, c);-}--static int string_get(const JSString *p, int idx) {- return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx];-}--static int string_getc(const JSString *p, int *pidx)-{- int idx, c, c1;- idx = *pidx;- if (p->is_wide_char) {- c = p->u.str16[idx++];- if (c >= 0xd800 && c < 0xdc00 && idx < p->len) {- c1 = p->u.str16[idx];- if (c1 >= 0xdc00 && c1 < 0xe000) {- c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;- idx++;- }- }- } else {- c = p->u.str8[idx++];- }- *pidx = idx;- return c;-}--static int string_buffer_write8(StringBuffer *s, const uint8_t *p, int len)-{- int i;-- if (s->len + len > s->size) {- if (string_buffer_realloc(s, s->len + len, 0))- return -1;- }- if (s->is_wide_char) {- for (i = 0; i < len; i++) {- s->str->u.str16[s->len + i] = p[i];- }- s->len += len;- } else {- memcpy(&s->str->u.str8[s->len], p, len);- s->len += len;- }- return 0;-}--static int string_buffer_write16(StringBuffer *s, const uint16_t *p, int len)-{- int c = 0, i;-- for (i = 0; i < len; i++) {- c |= p[i];- }- if (s->len + len > s->size) {- if (string_buffer_realloc(s, s->len + len, c))- return -1;- } else if (!s->is_wide_char && c >= 0x100) {- if (string_buffer_widen(s, s->size))- return -1;- }- if (s->is_wide_char) {- memcpy(&s->str->u.str16[s->len], p, len << 1);- s->len += len;- } else {- for (i = 0; i < len; i++) {- s->str->u.str8[s->len + i] = p[i];- }- s->len += len;- }- return 0;-}--/* appending an ASCII string */-static int string_buffer_puts8(StringBuffer *s, const char *str)-{- return string_buffer_write8(s, (const uint8_t *)str, strlen(str));-}--static int string_buffer_concat(StringBuffer *s, const JSString *p,- uint32_t from, uint32_t to)-{- if (to <= from)- return 0;- if (p->is_wide_char)- return string_buffer_write16(s, p->u.str16 + from, to - from);- else- return string_buffer_write8(s, p->u.str8 + from, to - from);-}--static int string_buffer_concat_value(StringBuffer *s, JSValueConst v)-{- JSString *p;- JSValue v1;- int res;-- if (s->error_status) {- /* prevent exception overload */- return -1;- }- if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {- v1 = JS_ToString(s->ctx, v);- if (JS_IsException(v1))- return string_buffer_set_error(s);- p = JS_VALUE_GET_STRING(v1);- res = string_buffer_concat(s, p, 0, p->len);- JS_FreeValue(s->ctx, v1);- return res;- }- p = JS_VALUE_GET_STRING(v);- return string_buffer_concat(s, p, 0, p->len);-}--static int string_buffer_concat_value_free(StringBuffer *s, JSValue v)-{- JSString *p;- int res;-- if (s->error_status) {- /* prevent exception overload */- JS_FreeValue(s->ctx, v);- return -1;- }- if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {- v = JS_ToStringFree(s->ctx, v);- if (JS_IsException(v))- return string_buffer_set_error(s);- }- p = JS_VALUE_GET_STRING(v);- res = string_buffer_concat(s, p, 0, p->len);- JS_FreeValue(s->ctx, v);- return res;-}--static int string_buffer_fill(StringBuffer *s, int c, int count)-{- /* XXX: optimize */- if (s->len + count > s->size) {- if (string_buffer_realloc(s, s->len + count, c))- return -1;- }- while (count-- > 0) {- if (string_buffer_putc16(s, c))- return -1;- }- return 0;-}--static JSValue string_buffer_end(StringBuffer *s)-{- JSString *str;- str = s->str;- if (s->error_status)- return JS_EXCEPTION;- if (s->len == 0) {- js_free(s->ctx, str);- s->str = NULL;- return JS_AtomToString(s->ctx, JS_ATOM_empty_string);- }- if (s->len < s->size) {- /* smaller size so js_realloc should not fail, but OK if it does */- /* XXX: should add some slack to avoid unnecessary calls */- /* XXX: might need to use malloc+free to ensure smaller size */- str = js_realloc_rt(s->ctx->rt, str, sizeof(JSString) +- (s->len << s->is_wide_char) + 1 - s->is_wide_char);- if (str == NULL)- str = s->str;- s->str = str;- }- if (!s->is_wide_char)- str->u.str8[s->len] = 0;-#ifdef DUMP_LEAKS- list_add_tail(&str->link, &s->ctx->rt->string_list);-#endif- str->is_wide_char = s->is_wide_char;- str->len = s->len;- s->str = NULL;- return JS_MKPTR(JS_TAG_STRING, str);-}--/* create a string from a UTF-8 buffer */-JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len)-{- const uint8_t *p, *p_end, *p_start, *p_next;- uint32_t c;- StringBuffer b_s, *b = &b_s;- size_t len1;- - p_start = (const uint8_t *)buf;- p_end = p_start + buf_len;- p = p_start;- while (p < p_end && *p < 128)- p++;- len1 = p - p_start;- if (len1 > JS_STRING_LEN_MAX)- return JS_ThrowInternalError(ctx, "string too long");- if (p == p_end) {- /* ASCII string */- return js_new_string8(ctx, (const uint8_t *)buf, buf_len);- } else {- if (string_buffer_init(ctx, b, buf_len))- goto fail;- string_buffer_write8(b, p_start, len1);- while (p < p_end) {- if (*p < 128) {- string_buffer_putc8(b, *p++);- } else {- /* parse utf-8 sequence, return 0xFFFFFFFF for error */- c = unicode_from_utf8(p, p_end - p, &p_next);- if (c < 0x10000) {- p = p_next;- } else if (c <= 0x10FFFF) {- p = p_next;- /* surrogate pair */- c -= 0x10000;- string_buffer_putc16(b, (c >> 10) + 0xd800);- c = (c & 0x3ff) + 0xdc00;- } else {- /* invalid char */- c = 0xfffd;- /* skip the invalid chars */- /* XXX: seems incorrect. Why not just use c = *p++; ? */- while (p < p_end && (*p >= 0x80 && *p < 0xc0))- p++;- if (p < p_end) {- p++;- while (p < p_end && (*p >= 0x80 && *p < 0xc0))- p++;- }- }- string_buffer_putc16(b, c);- }- }- }- return string_buffer_end(b);-- fail:- string_buffer_free(b);- return JS_EXCEPTION;-}--static JSValue JS_ConcatString3(JSContext *ctx, const char *str1,- JSValue str2, const char *str3)-{- StringBuffer b_s, *b = &b_s;- int len1, len3;- JSString *p;-- if (unlikely(JS_VALUE_GET_TAG(str2) != JS_TAG_STRING)) {- str2 = JS_ToStringFree(ctx, str2);- if (JS_IsException(str2))- goto fail;- }- p = JS_VALUE_GET_STRING(str2);- len1 = strlen(str1);- len3 = strlen(str3);-- if (string_buffer_init2(ctx, b, len1 + p->len + len3, p->is_wide_char))- goto fail;-- string_buffer_write8(b, (const uint8_t *)str1, len1);- string_buffer_concat(b, p, 0, p->len);- string_buffer_write8(b, (const uint8_t *)str3, len3);-- JS_FreeValue(ctx, str2);- return string_buffer_end(b);-- fail:- JS_FreeValue(ctx, str2);- return JS_EXCEPTION;-}--JSValue JS_NewString(JSContext *ctx, const char *str)-{- return JS_NewStringLen(ctx, str, strlen(str));-}--JSValue JS_NewAtomString(JSContext *ctx, const char *str)-{- JSAtom atom = JS_NewAtom(ctx, str);- if (atom == JS_ATOM_NULL)- return JS_EXCEPTION;- JSValue val = JS_AtomToString(ctx, atom);- JS_FreeAtom(ctx, atom);- return val;-}--/* return (NULL, 0) if exception. */-/* return pointer into a JSString with a live ref_count */-/* cesu8 determines if non-BMP1 codepoints are encoded as 1 or 2 utf-8 sequences */-const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, BOOL cesu8)-{- JSValue val;- JSString *str, *str_new;- int pos, len, c, c1;- uint8_t *q;-- if (JS_VALUE_GET_TAG(val1) != JS_TAG_STRING) {- val = JS_ToString(ctx, val1);- if (JS_IsException(val))- goto fail;- } else {- val = JS_DupValue(ctx, val1);- }-- str = JS_VALUE_GET_STRING(val);- len = str->len;- if (!str->is_wide_char) {- const uint8_t *src = str->u.str8;- int count;-- /* count the number of non-ASCII characters */- /* Scanning the whole string is required for ASCII strings,- and computing the number of non-ASCII bytes is less expensive- than testing each byte, hence this method is faster for ASCII- strings, which is the most common case.- */- count = 0;- for (pos = 0; pos < len; pos++) {- count += src[pos] >> 7;- }- if (count == 0) {- if (plen)- *plen = len;- return (const char *)src;- }- str_new = js_alloc_string(ctx, len + count, 0);- if (!str_new)- goto fail;- q = str_new->u.str8;- for (pos = 0; pos < len; pos++) {- c = src[pos];- if (c < 0x80) {- *q++ = c;- } else {- *q++ = (c >> 6) | 0xc0;- *q++ = (c & 0x3f) | 0x80;- }- }- } else {- const uint16_t *src = str->u.str16;- /* Allocate 3 bytes per 16 bit code point. Surrogate pairs may- produce 4 bytes but use 2 code points.- */- str_new = js_alloc_string(ctx, len * 3, 0);- if (!str_new)- goto fail;- q = str_new->u.str8;- pos = 0;- while (pos < len) {- c = src[pos++];- if (c < 0x80) {- *q++ = c;- } else {- if (c >= 0xd800 && c < 0xdc00) {- if (pos < len && !cesu8) {- c1 = src[pos];- if (c1 >= 0xdc00 && c1 < 0xe000) {- pos++;- /* surrogate pair */- c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;- } else {- /* Keep unmatched surrogate code points */- /* c = 0xfffd; */ /* error */- }- } else {- /* Keep unmatched surrogate code points */- /* c = 0xfffd; */ /* error */- }- }- q += unicode_to_utf8(q, c);- }- }- }-- *q = '\0';- str_new->len = q - str_new->u.str8;- JS_FreeValue(ctx, val);- if (plen)- *plen = str_new->len;- return (const char *)str_new->u.str8;- fail:- if (plen)- *plen = 0;- return NULL;-}--void JS_FreeCString(JSContext *ctx, const char *ptr)-{- JSString *p;- if (!ptr)- return;- /* purposely removing constness */- p = (JSString *)(void *)(ptr - offsetof(JSString, u));- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, p));-}--static int memcmp16_8(const uint16_t *src1, const uint8_t *src2, int len)-{- int c, i;- for(i = 0; i < len; i++) {- c = src1[i] - src2[i];- if (c != 0)- return c;- }- return 0;-}--static int memcmp16(const uint16_t *src1, const uint16_t *src2, int len)-{- int c, i;- for(i = 0; i < len; i++) {- c = src1[i] - src2[i];- if (c != 0)- return c;- }- return 0;-}--static int js_string_memcmp(const JSString *p1, const JSString *p2, int len)-{- int res;-- if (likely(!p1->is_wide_char)) {- if (likely(!p2->is_wide_char))- res = memcmp(p1->u.str8, p2->u.str8, len);- else- res = -memcmp16_8(p2->u.str16, p1->u.str8, len);- } else {- if (!p2->is_wide_char)- res = memcmp16_8(p1->u.str16, p2->u.str8, len);- else- res = memcmp16(p1->u.str16, p2->u.str16, len);- }- return res;-}--/* return < 0, 0 or > 0 */-static int js_string_compare(JSContext *ctx,- const JSString *p1, const JSString *p2)-{- int res, len;- len = min_int(p1->len, p2->len);- res = js_string_memcmp(p1, p2, len);- if (res == 0) {- if (p1->len == p2->len)- res = 0;- else if (p1->len < p2->len)- res = -1;- else- res = 1;- }- return res;-}--static void copy_str16(uint16_t *dst, const JSString *p, int offset, int len)-{- if (p->is_wide_char) {- memcpy(dst, p->u.str16 + offset, len * 2);- } else {- const uint8_t *src1 = p->u.str8 + offset;- int i;-- for(i = 0; i < len; i++)- dst[i] = src1[i];- }-}--static JSValue JS_ConcatString1(JSContext *ctx,- const JSString *p1, const JSString *p2)-{- JSString *p;- uint32_t len;- int is_wide_char;-- len = p1->len + p2->len;- if (len > JS_STRING_LEN_MAX)- return JS_ThrowInternalError(ctx, "string too long");- is_wide_char = p1->is_wide_char | p2->is_wide_char;- p = js_alloc_string(ctx, len, is_wide_char);- if (!p)- return JS_EXCEPTION;- if (!is_wide_char) {- memcpy(p->u.str8, p1->u.str8, p1->len);- memcpy(p->u.str8 + p1->len, p2->u.str8, p2->len);- p->u.str8[len] = '\0';- } else {- copy_str16(p->u.str16, p1, 0, p1->len);- copy_str16(p->u.str16 + p1->len, p2, 0, p2->len);- }- return JS_MKPTR(JS_TAG_STRING, p);-}--/* op1 and op2 are converted to strings. For convience, op1 or op2 =- JS_EXCEPTION are accepted and return JS_EXCEPTION. */-static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2)-{- JSValue ret;- JSString *p1, *p2;-- if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_STRING)) {- op1 = JS_ToStringFree(ctx, op1);- if (JS_IsException(op1)) {- JS_FreeValue(ctx, op2);- return JS_EXCEPTION;- }- }- if (unlikely(JS_VALUE_GET_TAG(op2) != JS_TAG_STRING)) {- op2 = JS_ToStringFree(ctx, op2);- if (JS_IsException(op2)) {- JS_FreeValue(ctx, op1);- return JS_EXCEPTION;- }- }- p1 = JS_VALUE_GET_STRING(op1);- p2 = JS_VALUE_GET_STRING(op2);-- /* XXX: could also check if p1 is empty */- if (p2->len == 0) {- goto ret_op1;- }- if (p1->header.ref_count == 1 && p1->is_wide_char == p2->is_wide_char- && js_malloc_usable_size(ctx, p1) >= sizeof(*p1) + ((p1->len + p2->len) << p2->is_wide_char) + 1 - p1->is_wide_char) {- /* Concatenate in place in available space at the end of p1 */- if (p1->is_wide_char) {- memcpy(p1->u.str16 + p1->len, p2->u.str16, p2->len << 1);- p1->len += p2->len;- } else {- memcpy(p1->u.str8 + p1->len, p2->u.str8, p2->len);- p1->len += p2->len;- p1->u.str8[p1->len] = '\0';- }- ret_op1:- JS_FreeValue(ctx, op2);- return op1;- }- ret = JS_ConcatString1(ctx, p1, p2);- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- return ret;-}--/* Shape support */--static inline size_t get_shape_size(size_t hash_size, size_t prop_size)-{- return hash_size * sizeof(uint32_t) + sizeof(JSShape) +- prop_size * sizeof(JSShapeProperty);-}--static inline JSShape *get_shape_from_alloc(void *sh_alloc, size_t hash_size)-{- return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size);-}--static inline uint32_t *prop_hash_end(JSShape *sh)-{- return (uint32_t *)sh;-}--static inline void *get_alloc_from_shape(JSShape *sh)-{- return prop_hash_end(sh) - ((intptr_t)sh->prop_hash_mask + 1);-}--static inline JSShapeProperty *get_shape_prop(JSShape *sh)-{- return sh->prop;-}--static int init_shape_hash(JSRuntime *rt)-{- rt->shape_hash_bits = 4; /* 16 shapes */- rt->shape_hash_size = 1 << rt->shape_hash_bits;- rt->shape_hash_count = 0;- rt->shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) *- rt->shape_hash_size);- if (!rt->shape_hash)- return -1;- return 0;-}--/* same magic hash multiplier as the Linux kernel */-static uint32_t shape_hash(uint32_t h, uint32_t val)-{- return (h + val) * 0x9e370001;-}--/* truncate the shape hash to 'hash_bits' bits */-static uint32_t get_shape_hash(uint32_t h, int hash_bits)-{- return h >> (32 - hash_bits);-}--static uint32_t shape_initial_hash(JSObject *proto)-{- uint32_t h;- h = shape_hash(1, (uintptr_t)proto);- if (sizeof(proto) > 4)- h = shape_hash(h, (uint64_t)(uintptr_t)proto >> 32);- return h;-}--static int resize_shape_hash(JSRuntime *rt, int new_shape_hash_bits)-{- int new_shape_hash_size, i;- uint32_t h;- JSShape **new_shape_hash, *sh, *sh_next;-- new_shape_hash_size = 1 << new_shape_hash_bits;- new_shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) *- new_shape_hash_size);- if (!new_shape_hash)- return -1;- for(i = 0; i < rt->shape_hash_size; i++) {- for(sh = rt->shape_hash[i]; sh != NULL; sh = sh_next) {- sh_next = sh->shape_hash_next;- h = get_shape_hash(sh->hash, new_shape_hash_bits);- sh->shape_hash_next = new_shape_hash[h];- new_shape_hash[h] = sh;- }- }- js_free_rt(rt, rt->shape_hash);- rt->shape_hash_bits = new_shape_hash_bits;- rt->shape_hash_size = new_shape_hash_size;- rt->shape_hash = new_shape_hash;- return 0;-}--static void js_shape_hash_link(JSRuntime *rt, JSShape *sh)-{- uint32_t h;- h = get_shape_hash(sh->hash, rt->shape_hash_bits);- sh->shape_hash_next = rt->shape_hash[h];- rt->shape_hash[h] = sh;- rt->shape_hash_count++;-}--static void js_shape_hash_unlink(JSRuntime *rt, JSShape *sh)-{- uint32_t h;- JSShape **psh;-- h = get_shape_hash(sh->hash, rt->shape_hash_bits);- psh = &rt->shape_hash[h];- while (*psh != sh)- psh = &(*psh)->shape_hash_next;- *psh = sh->shape_hash_next;- rt->shape_hash_count--;-}--/* create a new empty shape with prototype 'proto' */-static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto,- int hash_size, int prop_size)-{- JSRuntime *rt = ctx->rt;- void *sh_alloc;- JSShape *sh;-- /* resize the shape hash table if necessary */- if (2 * (rt->shape_hash_count + 1) > rt->shape_hash_size) {- resize_shape_hash(rt, rt->shape_hash_bits + 1);- }-- sh_alloc = js_malloc(ctx, get_shape_size(hash_size, prop_size));- if (!sh_alloc)- return NULL;- sh = get_shape_from_alloc(sh_alloc, hash_size);- sh->header.ref_count = 1;- add_gc_object(rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);- if (proto)- JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, proto));- sh->proto = proto;- memset(prop_hash_end(sh) - hash_size, 0, sizeof(prop_hash_end(sh)[0]) *- hash_size);- sh->prop_hash_mask = hash_size - 1;- sh->prop_size = prop_size;- sh->prop_count = 0;- sh->deleted_prop_count = 0;- - /* insert in the hash table */- sh->hash = shape_initial_hash(proto);- sh->is_hashed = TRUE;- sh->has_small_array_index = FALSE;- js_shape_hash_link(ctx->rt, sh);- return sh;-}--static JSShape *js_new_shape(JSContext *ctx, JSObject *proto)-{- return js_new_shape2(ctx, proto, JS_PROP_INITIAL_HASH_SIZE,- JS_PROP_INITIAL_SIZE);-}--/* The shape is cloned. The new shape is not inserted in the shape- hash table */-static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1)-{- JSShape *sh;- void *sh_alloc, *sh_alloc1;- size_t size;- JSShapeProperty *pr;- uint32_t i, hash_size;-- hash_size = sh1->prop_hash_mask + 1;- size = get_shape_size(hash_size, sh1->prop_size);- sh_alloc = js_malloc(ctx, size);- if (!sh_alloc)- return NULL;- sh_alloc1 = get_alloc_from_shape(sh1);- memcpy(sh_alloc, sh_alloc1, size);- sh = get_shape_from_alloc(sh_alloc, hash_size);- sh->header.ref_count = 1;- add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);- sh->is_hashed = FALSE;- if (sh->proto) {- JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto));- }- for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {- JS_DupAtom(ctx, pr->atom);- }- return sh;-}--static JSShape *js_dup_shape(JSShape *sh)-{- sh->header.ref_count++;- return sh;-}--static void js_free_shape0(JSRuntime *rt, JSShape *sh)-{- uint32_t i;- JSShapeProperty *pr;-- assert(sh->header.ref_count == 0);- if (sh->is_hashed)- js_shape_hash_unlink(rt, sh);- if (sh->proto != NULL) {- JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, sh->proto));- }- pr = get_shape_prop(sh);- for(i = 0; i < sh->prop_count; i++) {- JS_FreeAtomRT(rt, pr->atom);- pr++;- }- remove_gc_object(&sh->header);- js_free_rt(rt, get_alloc_from_shape(sh));-}--static void js_free_shape(JSRuntime *rt, JSShape *sh)-{- if (unlikely(--sh->header.ref_count <= 0)) {- js_free_shape0(rt, sh);- }-}--static void js_free_shape_null(JSRuntime *rt, JSShape *sh)-{- if (sh)- js_free_shape(rt, sh);-}--/* make space to hold at least 'count' properties */-static no_inline int resize_properties(JSContext *ctx, JSShape **psh,- JSObject *p, uint32_t count)-{- JSShape *sh;- uint32_t new_size, new_hash_size, new_hash_mask, i;- JSShapeProperty *pr;- void *sh_alloc;- intptr_t h;-- sh = *psh;- new_size = max_int(count, sh->prop_size * 3 / 2);- /* Reallocate prop array first to avoid crash or size inconsistency- in case of memory allocation failure */- if (p) {- JSProperty *new_prop;- new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size);- if (unlikely(!new_prop))- return -1;- p->prop = new_prop;- }- new_hash_size = sh->prop_hash_mask + 1;- while (new_hash_size < new_size)- new_hash_size = 2 * new_hash_size;- if (new_hash_size != (sh->prop_hash_mask + 1)) {- JSShape *old_sh;- /* resize the hash table and the properties */- old_sh = sh;- sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));- if (!sh_alloc)- return -1;- sh = get_shape_from_alloc(sh_alloc, new_hash_size);- list_del(&old_sh->header.link);- /* copy all the fields and the properties */- memcpy(sh, old_sh,- sizeof(JSShape) + sizeof(sh->prop[0]) * old_sh->prop_count);- list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);- new_hash_mask = new_hash_size - 1;- sh->prop_hash_mask = new_hash_mask;- memset(prop_hash_end(sh) - new_hash_size, 0,- sizeof(prop_hash_end(sh)[0]) * new_hash_size);- for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) {- if (pr->atom != JS_ATOM_NULL) {- h = ((uintptr_t)pr->atom & new_hash_mask);- pr->hash_next = prop_hash_end(sh)[-h - 1];- prop_hash_end(sh)[-h - 1] = i + 1;- }- }- js_free(ctx, get_alloc_from_shape(old_sh));- } else {- /* only resize the properties */- list_del(&sh->header.link);- sh_alloc = js_realloc(ctx, get_alloc_from_shape(sh),- get_shape_size(new_hash_size, new_size));- if (unlikely(!sh_alloc)) {- /* insert again in the GC list */- list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);- return -1;- }- sh = get_shape_from_alloc(sh_alloc, new_hash_size);- list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);- }- *psh = sh;- sh->prop_size = new_size;- return 0;-}--/* remove the deleted properties. */-static int compact_properties(JSContext *ctx, JSObject *p)-{- JSShape *sh, *old_sh;- void *sh_alloc;- intptr_t h;- uint32_t new_hash_size, i, j, new_hash_mask, new_size;- JSShapeProperty *old_pr, *pr;- JSProperty *prop, *new_prop;- - sh = p->shape;- assert(!sh->is_hashed);-- new_size = max_int(JS_PROP_INITIAL_SIZE,- sh->prop_count - sh->deleted_prop_count);- assert(new_size <= sh->prop_size);-- new_hash_size = sh->prop_hash_mask + 1;- while ((new_hash_size / 2) >= new_size)- new_hash_size = new_hash_size / 2;- new_hash_mask = new_hash_size - 1;-- /* resize the hash table and the properties */- old_sh = sh;- sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));- if (!sh_alloc)- return -1;- sh = get_shape_from_alloc(sh_alloc, new_hash_size);- list_del(&old_sh->header.link);- memcpy(sh, old_sh, sizeof(JSShape));- list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);- - memset(prop_hash_end(sh) - new_hash_size, 0,- sizeof(prop_hash_end(sh)[0]) * new_hash_size);-- j = 0;- old_pr = old_sh->prop;- pr = sh->prop;- prop = p->prop;- for(i = 0; i < sh->prop_count; i++) {- if (old_pr->atom != JS_ATOM_NULL) {- pr->atom = old_pr->atom;- pr->flags = old_pr->flags;- h = ((uintptr_t)old_pr->atom & new_hash_mask);- pr->hash_next = prop_hash_end(sh)[-h - 1];- prop_hash_end(sh)[-h - 1] = j + 1;- prop[j] = prop[i];- j++;- pr++;- }- old_pr++;- }- assert(j == (sh->prop_count - sh->deleted_prop_count));- sh->prop_hash_mask = new_hash_mask;- sh->prop_size = new_size;- sh->deleted_prop_count = 0;- sh->prop_count = j;-- p->shape = sh;- js_free(ctx, get_alloc_from_shape(old_sh));- - /* reduce the size of the object properties */- new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size);- if (new_prop)- p->prop = new_prop;- return 0;-}--static int add_shape_property(JSContext *ctx, JSShape **psh,- JSObject *p, JSAtom atom, int prop_flags)-{- JSRuntime *rt = ctx->rt;- JSShape *sh = *psh;- JSShapeProperty *pr, *prop;- uint32_t hash_mask, new_shape_hash = 0;- intptr_t h;-- /* update the shape hash */- if (sh->is_hashed) {- js_shape_hash_unlink(rt, sh);- new_shape_hash = shape_hash(shape_hash(sh->hash, atom), prop_flags);- }-- if (unlikely(sh->prop_count >= sh->prop_size)) {- if (resize_properties(ctx, psh, p, sh->prop_count + 1)) {- /* in case of error, reinsert in the hash table.- sh is still valid if resize_properties() failed */- if (sh->is_hashed)- js_shape_hash_link(rt, sh);- return -1;- }- sh = *psh;- }- if (sh->is_hashed) {- sh->hash = new_shape_hash;- js_shape_hash_link(rt, sh);- }- /* Initialize the new shape property.- The object property at p->prop[sh->prop_count] is uninitialized */- prop = get_shape_prop(sh);- pr = &prop[sh->prop_count++];- pr->atom = JS_DupAtom(ctx, atom);- pr->flags = prop_flags;- sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom);- /* add in hash table */- hash_mask = sh->prop_hash_mask;- h = atom & hash_mask;- pr->hash_next = prop_hash_end(sh)[-h - 1];- prop_hash_end(sh)[-h - 1] = sh->prop_count;- return 0;-}--/* find a hashed empty shape matching the prototype. Return NULL if- not found */-static JSShape *find_hashed_shape_proto(JSRuntime *rt, JSObject *proto)-{- JSShape *sh1;- uint32_t h, h1;-- h = shape_initial_hash(proto);- h1 = get_shape_hash(h, rt->shape_hash_bits);- for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) {- if (sh1->hash == h &&- sh1->proto == proto &&- sh1->prop_count == 0) {- return sh1;- }- }- return NULL;-}--/* find a hashed shape matching sh + (prop, prop_flags). Return NULL if- not found */-static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh,- JSAtom atom, int prop_flags)-{- JSShape *sh1;- uint32_t h, h1, i, n;-- h = sh->hash;- h = shape_hash(h, atom);- h = shape_hash(h, prop_flags);- h1 = get_shape_hash(h, rt->shape_hash_bits);- for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) {- /* we test the hash first so that the rest is done only if the- shapes really match */- if (sh1->hash == h &&- sh1->proto == sh->proto &&- sh1->prop_count == ((n = sh->prop_count) + 1)) {- for(i = 0; i < n; i++) {- if (unlikely(sh1->prop[i].atom != sh->prop[i].atom) ||- unlikely(sh1->prop[i].flags != sh->prop[i].flags))- goto next;- }- if (unlikely(sh1->prop[n].atom != atom) ||- unlikely(sh1->prop[n].flags != prop_flags))- goto next;- return sh1;- }- next: ;- }- return NULL;-}--static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh)-{- char atom_buf[ATOM_GET_STR_BUF_SIZE];- int j;-- /* XXX: should output readable class prototype */- printf("%5d %3d%c %14p %5d %5d", i,- sh->header.ref_count, " *"[sh->is_hashed],- (void *)sh->proto, sh->prop_size, sh->prop_count);- for(j = 0; j < sh->prop_count; j++) {- printf(" %s", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf),- sh->prop[j].atom));- }- printf("\n");-}--static __maybe_unused void JS_DumpShapes(JSRuntime *rt)-{- int i;- JSShape *sh;- struct list_head *el;- JSObject *p;- JSGCObjectHeader *gp;- - printf("JSShapes: {\n");- printf("%5s %4s %14s %5s %5s %s\n", "SLOT", "REFS", "PROTO", "SIZE", "COUNT", "PROPS");- for(i = 0; i < rt->shape_hash_size; i++) {- for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) {- JS_DumpShape(rt, i, sh);- assert(sh->is_hashed);- }- }- /* dump non-hashed shapes */- list_for_each(el, &rt->gc_obj_list) {- gp = list_entry(el, JSGCObjectHeader, link);- if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {- p = (JSObject *)gp;- if (!p->shape->is_hashed) {- JS_DumpShape(rt, -1, p->shape);- }- }- }- printf("}\n");-}--static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID class_id)-{- JSObject *p;-- js_trigger_gc(ctx->rt, sizeof(JSObject));- p = js_malloc(ctx, sizeof(JSObject));- if (unlikely(!p))- goto fail;- p->class_id = class_id;- p->extensible = TRUE;- p->free_mark = 0;- p->is_exotic = 0;- p->fast_array = 0;- p->is_constructor = 0;- p->is_uncatchable_error = 0;- p->tmp_mark = 0;- p->is_HTMLDDA = 0;- p->first_weak_ref = NULL;- p->u.opaque = NULL;- p->shape = sh;- p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size);- if (unlikely(!p->prop)) {- js_free(ctx, p);- fail:- js_free_shape(ctx->rt, sh);- return JS_EXCEPTION;- }-- switch(class_id) {- case JS_CLASS_OBJECT:- break;- case JS_CLASS_ARRAY:- {- JSProperty *pr;- p->is_exotic = 1;- p->fast_array = 1;- p->u.array.u.values = NULL;- p->u.array.count = 0;- p->u.array.u1.size = 0;- /* the length property is always the first one */- if (likely(sh == ctx->array_shape)) {- pr = &p->prop[0];- } else {- /* only used for the first array */- /* cannot fail */- pr = add_property(ctx, p, JS_ATOM_length,- JS_PROP_WRITABLE | JS_PROP_LENGTH);- }- pr->u.value = JS_NewInt32(ctx, 0);- }- break;- case JS_CLASS_C_FUNCTION:- p->prop[0].u.value = JS_UNDEFINED;- break;- case JS_CLASS_ARGUMENTS:- case JS_CLASS_UINT8C_ARRAY:- case JS_CLASS_INT8_ARRAY:- case JS_CLASS_UINT8_ARRAY:- case JS_CLASS_INT16_ARRAY:- case JS_CLASS_UINT16_ARRAY:- case JS_CLASS_INT32_ARRAY:- case JS_CLASS_UINT32_ARRAY:-#ifdef CONFIG_BIGNUM- case JS_CLASS_BIG_INT64_ARRAY:- case JS_CLASS_BIG_UINT64_ARRAY:-#endif- case JS_CLASS_FLOAT32_ARRAY:- case JS_CLASS_FLOAT64_ARRAY:- p->is_exotic = 1;- p->fast_array = 1;- p->u.array.u.ptr = NULL;- p->u.array.count = 0;- break;- case JS_CLASS_DATAVIEW:- p->u.array.u.ptr = NULL;- p->u.array.count = 0;- break;- case JS_CLASS_NUMBER:- case JS_CLASS_STRING:- case JS_CLASS_BOOLEAN:- case JS_CLASS_SYMBOL:- case JS_CLASS_DATE:-#ifdef CONFIG_BIGNUM- case JS_CLASS_BIG_INT:- case JS_CLASS_BIG_FLOAT:- case JS_CLASS_BIG_DECIMAL:-#endif- p->u.object_data = JS_UNDEFINED;- goto set_exotic;- case JS_CLASS_REGEXP:- p->u.regexp.pattern = NULL;- p->u.regexp.bytecode = NULL;- goto set_exotic;- default:- set_exotic:- if (ctx->rt->class_array[class_id].exotic) {- p->is_exotic = 1;- }- break;- }- p->header.ref_count = 1;- add_gc_object(ctx->rt, &p->header, JS_GC_OBJ_TYPE_JS_OBJECT);- return JS_MKPTR(JS_TAG_OBJECT, p);-}--static JSObject *get_proto_obj(JSValueConst proto_val)-{- if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT)- return NULL;- else- return JS_VALUE_GET_OBJ(proto_val);-}--/* WARNING: proto must be an object or JS_NULL */-JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto_val,- JSClassID class_id)-{- JSShape *sh;- JSObject *proto;-- proto = get_proto_obj(proto_val);- sh = find_hashed_shape_proto(ctx->rt, proto);- if (likely(sh)) {- sh = js_dup_shape(sh);- } else {- sh = js_new_shape(ctx, proto);- if (!sh)- return JS_EXCEPTION;- }- return JS_NewObjectFromShape(ctx, sh, class_id);-}--#if 0-static JSValue JS_GetObjectData(JSContext *ctx, JSValueConst obj)-{- JSObject *p;-- if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {- p = JS_VALUE_GET_OBJ(obj);- switch(p->class_id) {- case JS_CLASS_NUMBER:- case JS_CLASS_STRING:- case JS_CLASS_BOOLEAN:- case JS_CLASS_SYMBOL:- case JS_CLASS_DATE:-#ifdef CONFIG_BIGNUM- case JS_CLASS_BIG_INT:- case JS_CLASS_BIG_FLOAT:- case JS_CLASS_BIG_DECIMAL:-#endif- return JS_DupValue(ctx, p->u.object_data);- }- }- return JS_UNDEFINED;-}-#endif--static int JS_SetObjectData(JSContext *ctx, JSValueConst obj, JSValue val)-{- JSObject *p;-- if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {- p = JS_VALUE_GET_OBJ(obj);- switch(p->class_id) {- case JS_CLASS_NUMBER:- case JS_CLASS_STRING:- case JS_CLASS_BOOLEAN:- case JS_CLASS_SYMBOL:- case JS_CLASS_DATE:-#ifdef CONFIG_BIGNUM- case JS_CLASS_BIG_INT:- case JS_CLASS_BIG_FLOAT:- case JS_CLASS_BIG_DECIMAL:-#endif- JS_FreeValue(ctx, p->u.object_data);- p->u.object_data = val;- return 0;- }- }- JS_FreeValue(ctx, val);- if (!JS_IsException(obj))- JS_ThrowTypeError(ctx, "invalid object type");- return -1;-}--JSValue JS_NewObjectClass(JSContext *ctx, int class_id)-{- return JS_NewObjectProtoClass(ctx, ctx->class_proto[class_id], class_id);-}--JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto)-{- return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT);-}--JSValue JS_NewArray(JSContext *ctx)-{- return JS_NewObjectFromShape(ctx, js_dup_shape(ctx->array_shape),- JS_CLASS_ARRAY);-}--JSValue JS_NewObject(JSContext *ctx)-{- /* inline JS_NewObjectClass(ctx, JS_CLASS_OBJECT); */- return JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_OBJECT);-}--static void js_function_set_properties(JSContext *ctx, JSValueConst func_obj,- JSAtom name, int len)-{- /* ES6 feature non compatible with ES5.1: length is configurable */- JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, JS_NewInt32(ctx, len),- JS_PROP_CONFIGURABLE);- JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name,- JS_AtomToString(ctx, name), JS_PROP_CONFIGURABLE);-}--static BOOL js_class_has_bytecode(JSClassID class_id)-{- return (class_id == JS_CLASS_BYTECODE_FUNCTION ||- class_id == JS_CLASS_GENERATOR_FUNCTION ||- class_id == JS_CLASS_ASYNC_FUNCTION ||- class_id == JS_CLASS_ASYNC_GENERATOR_FUNCTION);-}--/* return NULL without exception if not a function or no bytecode */-static JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val)-{- JSObject *p;- if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)- return NULL;- p = JS_VALUE_GET_OBJ(val);- if (!js_class_has_bytecode(p->class_id))- return NULL;- return p->u.func.function_bytecode;-}--static void js_method_set_home_object(JSContext *ctx, JSValueConst func_obj,- JSValueConst home_obj)-{- JSObject *p, *p1;- JSFunctionBytecode *b;-- if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)- return;- p = JS_VALUE_GET_OBJ(func_obj);- if (!js_class_has_bytecode(p->class_id))- return;- b = p->u.func.function_bytecode;- if (b->need_home_object) {- p1 = p->u.func.home_object;- if (p1) {- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));- }- if (JS_VALUE_GET_TAG(home_obj) == JS_TAG_OBJECT)- p1 = JS_VALUE_GET_OBJ(JS_DupValue(ctx, home_obj));- else- p1 = NULL;- p->u.func.home_object = p1;- }-}--static JSValue js_get_function_name(JSContext *ctx, JSAtom name)-{- JSValue name_str;-- name_str = JS_AtomToString(ctx, name);- if (JS_AtomSymbolHasDescription(ctx, name)) {- name_str = JS_ConcatString3(ctx, "[", name_str, "]");- }- return name_str;-}--/* Modify the name of a method according to the atom and- 'flags'. 'flags' is a bitmask of JS_PROP_HAS_GET and- JS_PROP_HAS_SET. Also set the home object of the method.- Return < 0 if exception. */-static int js_method_set_properties(JSContext *ctx, JSValueConst func_obj,- JSAtom name, int flags, JSValueConst home_obj)-{- JSValue name_str;-- name_str = js_get_function_name(ctx, name);- if (flags & JS_PROP_HAS_GET) {- name_str = JS_ConcatString3(ctx, "get ", name_str, "");- } else if (flags & JS_PROP_HAS_SET) {- name_str = JS_ConcatString3(ctx, "set ", name_str, "");- }- if (JS_IsException(name_str))- return -1;- if (JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name_str,- JS_PROP_CONFIGURABLE) < 0)- return -1;- js_method_set_home_object(ctx, func_obj, home_obj);- return 0;-}--/* Note: at least 'length' arguments will be readable in 'argv' */-static JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func,- const char *name,- int length, JSCFunctionEnum cproto, int magic,- JSValueConst proto_val)-{- JSValue func_obj;- JSObject *p;- JSAtom name_atom;- - func_obj = JS_NewObjectProtoClass(ctx, proto_val, JS_CLASS_C_FUNCTION);- if (JS_IsException(func_obj))- return func_obj;- p = JS_VALUE_GET_OBJ(func_obj);- p->u.cfunc.realm = JS_DupContext(ctx);- p->u.cfunc.c_function.generic = func;- p->u.cfunc.length = length;- p->u.cfunc.cproto = cproto;- p->u.cfunc.magic = magic;- p->is_constructor = (cproto == JS_CFUNC_constructor ||- cproto == JS_CFUNC_constructor_magic ||- cproto == JS_CFUNC_constructor_or_func ||- cproto == JS_CFUNC_constructor_or_func_magic);- if (!name)- name = "";- name_atom = JS_NewAtom(ctx, name);- js_function_set_properties(ctx, func_obj, name_atom, length);- JS_FreeAtom(ctx, name_atom);- return func_obj;-}--/* Note: at least 'length' arguments will be readable in 'argv' */-JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func,- const char *name,- int length, JSCFunctionEnum cproto, int magic)-{- return JS_NewCFunction3(ctx, func, name, length, cproto, magic,- ctx->function_proto);-}--typedef struct JSCFunctionDataRecord {- JSCFunctionData *func;- uint8_t length;- uint8_t data_len;- uint16_t magic;- JSValue data[0];-} JSCFunctionDataRecord;--static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val)-{- JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA);- int i;-- if (s) {- for(i = 0; i < s->data_len; i++) {- JS_FreeValueRT(rt, s->data[i]);- }- js_free_rt(rt, s);- }-}--static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func)-{- JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA);- int i;-- if (s) {- for(i = 0; i < s->data_len; i++) {- JS_MarkValue(rt, s->data[i], mark_func);- }- }-}--static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj,- JSValueConst this_val,- int argc, JSValueConst *argv, int flags)-{- JSCFunctionDataRecord *s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA);- JSValueConst *arg_buf;- int i;-- /* XXX: could add the function on the stack for debug */- if (unlikely(argc < s->length)) {- arg_buf = alloca(sizeof(arg_buf[0]) * s->length);- for(i = 0; i < argc; i++)- arg_buf[i] = argv[i];- for(i = argc; i < s->length; i++)- arg_buf[i] = JS_UNDEFINED;- } else {- arg_buf = argv;- }-- return s->func(ctx, this_val, argc, arg_buf, s->magic, s->data);-}--JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func,- int length, int magic, int data_len,- JSValueConst *data)-{- JSCFunctionDataRecord *s;- JSValue func_obj;- int i;-- func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,- JS_CLASS_C_FUNCTION_DATA);- if (JS_IsException(func_obj))- return func_obj;- s = js_malloc(ctx, sizeof(*s) + data_len * sizeof(JSValue));- if (!s) {- JS_FreeValue(ctx, func_obj);- return JS_EXCEPTION;- }- s->func = func;- s->length = length;- s->data_len = data_len;- s->magic = magic;- for(i = 0; i < data_len; i++)- s->data[i] = JS_DupValue(ctx, data[i]);- JS_SetOpaque(func_obj, s);- js_function_set_properties(ctx, func_obj,- JS_ATOM_empty_string, length);- return func_obj;-}--static JSContext *js_autoinit_get_realm(JSProperty *pr)-{- return (JSContext *)(pr->u.init.realm_and_id & ~3);-}--static JSAutoInitIDEnum js_autoinit_get_id(JSProperty *pr)-{- return pr->u.init.realm_and_id & 3;-}--static void js_autoinit_free(JSRuntime *rt, JSProperty *pr)-{- JS_FreeContext(js_autoinit_get_realm(pr));-}--static void js_autoinit_mark(JSRuntime *rt, JSProperty *pr,- JS_MarkFunc *mark_func)-{- mark_func(rt, &js_autoinit_get_realm(pr)->header);-}--static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags)-{- if (unlikely(prop_flags & JS_PROP_TMASK)) {- if ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET) {- if (pr->u.getset.getter)- JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));- if (pr->u.getset.setter)- JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));- } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_VARREF) {- free_var_ref(rt, pr->u.var_ref);- } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {- js_autoinit_free(rt, pr);- }- } else {- JS_FreeValueRT(rt, pr->u.value);- }-}--static force_inline JSShapeProperty *find_own_property1(JSObject *p,- JSAtom atom)-{- JSShape *sh;- JSShapeProperty *pr, *prop;- intptr_t h;- sh = p->shape;- h = (uintptr_t)atom & sh->prop_hash_mask;- h = prop_hash_end(sh)[-h - 1];- prop = get_shape_prop(sh);- while (h) {- pr = &prop[h - 1];- if (likely(pr->atom == atom)) {- return pr;- }- h = pr->hash_next;- }- return NULL;-}--static force_inline JSShapeProperty *find_own_property(JSProperty **ppr,- JSObject *p,- JSAtom atom)-{- JSShape *sh;- JSShapeProperty *pr, *prop;- intptr_t h;- sh = p->shape;- h = (uintptr_t)atom & sh->prop_hash_mask;- h = prop_hash_end(sh)[-h - 1];- prop = get_shape_prop(sh);- while (h) {- pr = &prop[h - 1];- if (likely(pr->atom == atom)) {- *ppr = &p->prop[h - 1];- /* the compiler should be able to assume that pr != NULL here */- return pr;- }- h = pr->hash_next;- }- *ppr = NULL;- return NULL;-}--/* indicate that the object may be part of a function prototype cycle */-static void set_cycle_flag(JSContext *ctx, JSValueConst obj)-{-}--static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref)-{- if (var_ref) {- assert(var_ref->header.ref_count > 0);- if (--var_ref->header.ref_count == 0) {- if (var_ref->is_detached) {- JS_FreeValueRT(rt, var_ref->value);- remove_gc_object(&var_ref->header);- } else {- list_del(&var_ref->header.link); /* still on the stack */- }- js_free_rt(rt, var_ref);- }- }-}--static void js_array_finalizer(JSRuntime *rt, JSValue val)-{- JSObject *p = JS_VALUE_GET_OBJ(val);- int i;-- for(i = 0; i < p->u.array.count; i++) {- JS_FreeValueRT(rt, p->u.array.u.values[i]);- }- js_free_rt(rt, p->u.array.u.values);-}--static void js_array_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func)-{- JSObject *p = JS_VALUE_GET_OBJ(val);- int i;-- for(i = 0; i < p->u.array.count; i++) {- JS_MarkValue(rt, p->u.array.u.values[i], mark_func);- }-}--static void js_object_data_finalizer(JSRuntime *rt, JSValue val)-{- JSObject *p = JS_VALUE_GET_OBJ(val);- JS_FreeValueRT(rt, p->u.object_data);- p->u.object_data = JS_UNDEFINED;-}--static void js_object_data_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func)-{- JSObject *p = JS_VALUE_GET_OBJ(val);- JS_MarkValue(rt, p->u.object_data, mark_func);-}--static void js_c_function_finalizer(JSRuntime *rt, JSValue val)-{- JSObject *p = JS_VALUE_GET_OBJ(val);-- if (p->u.cfunc.realm)- JS_FreeContext(p->u.cfunc.realm);-}--static void js_c_function_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func)-{- JSObject *p = JS_VALUE_GET_OBJ(val);-- if (p->u.cfunc.realm)- mark_func(rt, &p->u.cfunc.realm->header);-}--static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val)-{- JSObject *p1, *p = JS_VALUE_GET_OBJ(val);- JSFunctionBytecode *b;- JSVarRef **var_refs;- int i;-- p1 = p->u.func.home_object;- if (p1) {- JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, p1));- }- b = p->u.func.function_bytecode;- if (b) {- var_refs = p->u.func.var_refs;- if (var_refs) {- for(i = 0; i < b->closure_var_count; i++)- free_var_ref(rt, var_refs[i]);- js_free_rt(rt, var_refs);- }- JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b));- }-}--static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func)-{- JSObject *p = JS_VALUE_GET_OBJ(val);- JSVarRef **var_refs = p->u.func.var_refs;- JSFunctionBytecode *b = p->u.func.function_bytecode;- int i;-- if (p->u.func.home_object) {- JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object),- mark_func);- }- if (b) {- if (var_refs) {- for(i = 0; i < b->closure_var_count; i++) {- JSVarRef *var_ref = var_refs[i];- if (var_ref && var_ref->is_detached) {- mark_func(rt, &var_ref->header);- }- }- }- /* must mark the function bytecode because template objects may be- part of a cycle */- JS_MarkValue(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b), mark_func);- }-}--static void js_bound_function_finalizer(JSRuntime *rt, JSValue val)-{- JSObject *p = JS_VALUE_GET_OBJ(val);- JSBoundFunction *bf = p->u.bound_function;- int i;-- JS_FreeValueRT(rt, bf->func_obj);- JS_FreeValueRT(rt, bf->this_val);- for(i = 0; i < bf->argc; i++) {- JS_FreeValueRT(rt, bf->argv[i]);- }- js_free_rt(rt, bf);-}--static void js_bound_function_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func)-{- JSObject *p = JS_VALUE_GET_OBJ(val);- JSBoundFunction *bf = p->u.bound_function;- int i;-- JS_MarkValue(rt, bf->func_obj, mark_func);- JS_MarkValue(rt, bf->this_val, mark_func);- for(i = 0; i < bf->argc; i++)- JS_MarkValue(rt, bf->argv[i], mark_func);-}--static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val)-{- JSObject *p = JS_VALUE_GET_OBJ(val);- JSForInIterator *it = p->u.for_in_iterator;- JS_FreeValueRT(rt, it->obj);- js_free_rt(rt, it);-}--static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func)-{- JSObject *p = JS_VALUE_GET_OBJ(val);- JSForInIterator *it = p->u.for_in_iterator;- JS_MarkValue(rt, it->obj, mark_func);-}--static void free_object(JSRuntime *rt, JSObject *p)-{- int i;- JSClassFinalizer *finalizer;- JSShape *sh;- JSShapeProperty *pr;-- p->free_mark = 1; /* used to tell the object is invalid when- freeing cycles */- /* free all the fields */- sh = p->shape;- pr = get_shape_prop(sh);- for(i = 0; i < sh->prop_count; i++) {- free_property(rt, &p->prop[i], pr->flags);- pr++;- }- js_free_rt(rt, p->prop);- /* as an optimization we destroy the shape immediately without- putting it in gc_zero_ref_count_list */- js_free_shape(rt, sh);-- /* fail safe */- p->shape = NULL;- p->prop = NULL;-- if (unlikely(p->first_weak_ref)) {- reset_weak_ref(rt, p);- }-- finalizer = rt->class_array[p->class_id].finalizer;- if (finalizer)- (*finalizer)(rt, JS_MKPTR(JS_TAG_OBJECT, p));-- /* fail safe */- p->class_id = 0;- p->u.opaque = NULL;- p->u.func.var_refs = NULL;- p->u.func.home_object = NULL;-- remove_gc_object(&p->header);- if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && p->header.ref_count != 0) {- list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list);- } else {- js_free_rt(rt, p);- }-}--static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp)-{- switch(gp->gc_obj_type) {- case JS_GC_OBJ_TYPE_JS_OBJECT:- free_object(rt, (JSObject *)gp);- break;- case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:- free_function_bytecode(rt, (JSFunctionBytecode *)gp);- break;- default:- abort();- }-}--static void free_zero_refcount(JSRuntime *rt)-{- struct list_head *el;- JSGCObjectHeader *p;- - rt->gc_phase = JS_GC_PHASE_DECREF;- for(;;) {- el = rt->gc_zero_ref_count_list.next;- if (el == &rt->gc_zero_ref_count_list)- break;- p = list_entry(el, JSGCObjectHeader, link);- assert(p->ref_count == 0);- free_gc_object(rt, p);- }- rt->gc_phase = JS_GC_PHASE_NONE;-}--/* called with the ref_count of 'v' reaches zero. */-void __JS_FreeValueRT(JSRuntime *rt, JSValue v)-{- uint32_t tag = JS_VALUE_GET_TAG(v);--#ifdef DUMP_FREE- {- printf("Freeing ");- if (tag == JS_TAG_OBJECT) {- JS_DumpObject(rt, JS_VALUE_GET_OBJ(v));- } else {- JS_DumpValueShort(rt, v);- printf("\n");- }- }-#endif-- switch(tag) {- case JS_TAG_STRING:- {- JSString *p = JS_VALUE_GET_STRING(v);- if (p->atom_type) {- JS_FreeAtomStruct(rt, p);- } else {-#ifdef DUMP_LEAKS- list_del(&p->link);-#endif- js_free_rt(rt, p);- }- }- break;- case JS_TAG_OBJECT:- case JS_TAG_FUNCTION_BYTECODE:- {- JSGCObjectHeader *p = JS_VALUE_GET_PTR(v);- if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) {- list_del(&p->link);- list_add(&p->link, &rt->gc_zero_ref_count_list);- if (rt->gc_phase == JS_GC_PHASE_NONE) {- free_zero_refcount(rt);- }- }- }- break;- case JS_TAG_MODULE:- abort(); /* never freed here */- break;-#ifdef CONFIG_BIGNUM- case JS_TAG_BIG_INT:- case JS_TAG_BIG_FLOAT:- {- JSBigFloat *bf = JS_VALUE_GET_PTR(v);- bf_delete(&bf->num);- js_free_rt(rt, bf);- }- break;- case JS_TAG_BIG_DECIMAL:- {- JSBigDecimal *bf = JS_VALUE_GET_PTR(v);- bfdec_delete(&bf->num);- js_free_rt(rt, bf);- }- break;-#endif- case JS_TAG_SYMBOL:- {- JSAtomStruct *p = JS_VALUE_GET_PTR(v);- JS_FreeAtomStruct(rt, p);- }- break;- default:- printf("__JS_FreeValue: unknown tag=%d\n", tag);- abort();- }-}--void __JS_FreeValue(JSContext *ctx, JSValue v)-{- __JS_FreeValueRT(ctx->rt, v);-}--/* garbage collection */--static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,- JSGCObjectTypeEnum type)-{- h->mark = 0;- h->gc_obj_type = type;- list_add_tail(&h->link, &rt->gc_obj_list);-}--static void remove_gc_object(JSGCObjectHeader *h)-{- list_del(&h->link);-}--void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)-{- if (JS_VALUE_HAS_REF_COUNT(val)) {- switch(JS_VALUE_GET_TAG(val)) {- case JS_TAG_OBJECT:- case JS_TAG_FUNCTION_BYTECODE:- mark_func(rt, JS_VALUE_GET_PTR(val));- break;- default:- break;- }- }-}--static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp,- JS_MarkFunc *mark_func)-{- switch(gp->gc_obj_type) {- case JS_GC_OBJ_TYPE_JS_OBJECT:- {- JSObject *p = (JSObject *)gp;- JSShapeProperty *prs;- JSShape *sh;- int i;- sh = p->shape;- mark_func(rt, &sh->header);- /* mark all the fields */- prs = get_shape_prop(sh);- for(i = 0; i < sh->prop_count; i++) {- JSProperty *pr = &p->prop[i];- if (prs->atom != JS_ATOM_NULL) {- if (prs->flags & JS_PROP_TMASK) {- if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {- if (pr->u.getset.getter)- mark_func(rt, &pr->u.getset.getter->header);- if (pr->u.getset.setter)- mark_func(rt, &pr->u.getset.setter->header);- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {- if (pr->u.var_ref->is_detached) {- /* Note: the tag does not matter- provided it is a GC object */- mark_func(rt, &pr->u.var_ref->header);- }- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {- js_autoinit_mark(rt, pr, mark_func);- }- } else {- JS_MarkValue(rt, pr->u.value, mark_func);- }- }- prs++;- }-- if (p->class_id != JS_CLASS_OBJECT) {- JSClassGCMark *gc_mark;- gc_mark = rt->class_array[p->class_id].gc_mark;- if (gc_mark)- gc_mark(rt, JS_MKPTR(JS_TAG_OBJECT, p), mark_func);- }- }- break;- case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:- /* the template objects can be part of a cycle */- {- JSFunctionBytecode *b = (JSFunctionBytecode *)gp;- int i;- for(i = 0; i < b->cpool_count; i++) {- JS_MarkValue(rt, b->cpool[i], mark_func);- }- if (b->realm)- mark_func(rt, &b->realm->header);- }- break;- case JS_GC_OBJ_TYPE_VAR_REF:- {- JSVarRef *var_ref = (JSVarRef *)gp;- /* only detached variable referenced are taken into account */- assert(var_ref->is_detached);- JS_MarkValue(rt, *var_ref->pvalue, mark_func);- }- break;- case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:- {- JSAsyncFunctionData *s = (JSAsyncFunctionData *)gp;- if (s->is_active)- async_func_mark(rt, &s->func_state, mark_func);- JS_MarkValue(rt, s->resolving_funcs[0], mark_func);- JS_MarkValue(rt, s->resolving_funcs[1], mark_func);- }- break;- case JS_GC_OBJ_TYPE_SHAPE:- {- JSShape *sh = (JSShape *)gp;- if (sh->proto != NULL) {- mark_func(rt, &sh->proto->header);- }- }- break;- case JS_GC_OBJ_TYPE_JS_CONTEXT:- {- JSContext *ctx = (JSContext *)gp;- JS_MarkContext(rt, ctx, mark_func);- }- break;- default:- abort();- }-}--static void gc_decref_child(JSRuntime *rt, JSGCObjectHeader *p)-{- assert(p->ref_count > 0);- p->ref_count--;- if (p->ref_count == 0 && p->mark == 1) {- list_del(&p->link);- list_add_tail(&p->link, &rt->tmp_obj_list);- }-}--static void gc_decref(JSRuntime *rt)-{- struct list_head *el, *el1;- JSGCObjectHeader *p;- - init_list_head(&rt->tmp_obj_list);-- /* decrement the refcount of all the children of all the GC- objects and move the GC objects with zero refcount to- tmp_obj_list */- list_for_each_safe(el, el1, &rt->gc_obj_list) {- p = list_entry(el, JSGCObjectHeader, link);- assert(p->mark == 0);- mark_children(rt, p, gc_decref_child);- p->mark = 1;- if (p->ref_count == 0) {- list_del(&p->link);- list_add_tail(&p->link, &rt->tmp_obj_list);- }- }-}--static void gc_scan_incref_child(JSRuntime *rt, JSGCObjectHeader *p)-{- p->ref_count++;- if (p->ref_count == 1) {- /* ref_count was 0: remove from tmp_obj_list and add at the- end of gc_obj_list */- list_del(&p->link);- list_add_tail(&p->link, &rt->gc_obj_list);- p->mark = 0; /* reset the mark for the next GC call */- }-}--static void gc_scan_incref_child2(JSRuntime *rt, JSGCObjectHeader *p)-{- p->ref_count++;-}--static void gc_scan(JSRuntime *rt)-{- struct list_head *el;- JSGCObjectHeader *p;-- /* keep the objects with a refcount > 0 and their children. */- list_for_each(el, &rt->gc_obj_list) {- p = list_entry(el, JSGCObjectHeader, link);- assert(p->ref_count > 0);- p->mark = 0; /* reset the mark for the next GC call */- mark_children(rt, p, gc_scan_incref_child);- }- - /* restore the refcount of the objects to be deleted. */- list_for_each(el, &rt->tmp_obj_list) {- p = list_entry(el, JSGCObjectHeader, link);- mark_children(rt, p, gc_scan_incref_child2);- }-}--static void gc_free_cycles(JSRuntime *rt)-{- struct list_head *el, *el1;- JSGCObjectHeader *p;-#ifdef DUMP_GC_FREE- BOOL header_done = FALSE;-#endif-- rt->gc_phase = JS_GC_PHASE_REMOVE_CYCLES;-- for(;;) {- el = rt->tmp_obj_list.next;- if (el == &rt->tmp_obj_list)- break;- p = list_entry(el, JSGCObjectHeader, link);- /* Only need to free the GC object associated with JS- values. The rest will be automatically removed because they- must be referenced by them. */- switch(p->gc_obj_type) {- case JS_GC_OBJ_TYPE_JS_OBJECT:- case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:-#ifdef DUMP_GC_FREE- if (!header_done) {- printf("Freeing cycles:\n");- JS_DumpObjectHeader(rt);- header_done = TRUE;- }- JS_DumpGCObject(rt, p);-#endif- free_gc_object(rt, p);- break;- default:- list_del(&p->link);- list_add_tail(&p->link, &rt->gc_zero_ref_count_list);- break;- }- }- rt->gc_phase = JS_GC_PHASE_NONE;- - list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) {- p = list_entry(el, JSGCObjectHeader, link);- assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT ||- p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);- js_free_rt(rt, p);- }-- init_list_head(&rt->gc_zero_ref_count_list);-}--void JS_RunGC(JSRuntime *rt)-{- /* decrement the reference of the children of each object. mark =- 1 after this pass. */- gc_decref(rt);-- /* keep the GC objects with a non zero refcount and their childs */- gc_scan(rt);-- /* free the GC objects in a cycle */- gc_free_cycles(rt);-}--/* Return false if not an object or if the object has already been- freed (zombie objects are visible in finalizers when freeing- cycles). */-BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj)-{- JSObject *p;- if (!JS_IsObject(obj))- return FALSE;- p = JS_VALUE_GET_OBJ(obj);- return !p->free_mark;-}--/* Compute memory used by various object types */-/* XXX: poor man's approach to handling multiply referenced objects */-typedef struct JSMemoryUsage_helper {- double memory_used_count;- double str_count;- double str_size;- int64_t js_func_count;- double js_func_size;- int64_t js_func_code_size;- int64_t js_func_pc2line_count;- int64_t js_func_pc2line_size;-} JSMemoryUsage_helper;--static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp);--static void compute_jsstring_size(JSString *str, JSMemoryUsage_helper *hp)-{- if (!str->atom_type) { /* atoms are handled separately */- double s_ref_count = str->header.ref_count;- hp->str_count += 1 / s_ref_count;- hp->str_size += ((sizeof(*str) + (str->len << str->is_wide_char) +- 1 - str->is_wide_char) / s_ref_count);- }-}--static void compute_bytecode_size(JSFunctionBytecode *b, JSMemoryUsage_helper *hp)-{- int memory_used_count, js_func_size, i;-- memory_used_count = 0;- js_func_size = offsetof(JSFunctionBytecode, debug);- if (b->vardefs) {- js_func_size += (b->arg_count + b->var_count) * sizeof(*b->vardefs);- }- if (b->cpool) {- js_func_size += b->cpool_count * sizeof(*b->cpool);- for (i = 0; i < b->cpool_count; i++) {- JSValueConst val = b->cpool[i];- compute_value_size(val, hp);- }- }- if (b->closure_var) {- js_func_size += b->closure_var_count * sizeof(*b->closure_var);- }- if (!b->read_only_bytecode && b->byte_code_buf) {- hp->js_func_code_size += b->byte_code_len;- }- if (b->has_debug) {- js_func_size += sizeof(*b) - offsetof(JSFunctionBytecode, debug);- if (b->debug.source) {- memory_used_count++;- js_func_size += b->debug.source_len + 1;- }- if (b->debug.pc2line_len) {- memory_used_count++;- hp->js_func_pc2line_count += 1;- hp->js_func_pc2line_size += b->debug.pc2line_len;- }- }- hp->js_func_size += js_func_size;- hp->js_func_count += 1;- hp->memory_used_count += memory_used_count;-}--static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp)-{- switch(JS_VALUE_GET_TAG(val)) {- case JS_TAG_STRING:- compute_jsstring_size(JS_VALUE_GET_STRING(val), hp);- break;-#ifdef CONFIG_BIGNUM- case JS_TAG_BIG_INT:- case JS_TAG_BIG_FLOAT:- case JS_TAG_BIG_DECIMAL:- /* should track JSBigFloat usage */- break;-#endif- }-}--void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s)-{- struct list_head *el, *el1;- int i;- JSMemoryUsage_helper mem = { 0 }, *hp = &mem;-- memset(s, 0, sizeof(*s));- s->malloc_count = rt->malloc_state.malloc_count;- s->malloc_size = rt->malloc_state.malloc_size;- s->malloc_limit = rt->malloc_state.malloc_limit;-- s->memory_used_count = 2; /* rt + rt->class_array */- s->memory_used_size = sizeof(JSRuntime) + sizeof(JSValue) * rt->class_count;-- list_for_each(el, &rt->context_list) {- JSContext *ctx = list_entry(el, JSContext, link);- JSShape *sh = ctx->array_shape;- s->memory_used_count += 2; /* ctx + ctx->class_proto */- s->memory_used_size += sizeof(JSContext) +- sizeof(JSValue) * rt->class_count;- s->binary_object_count += ctx->binary_object_count;- s->binary_object_size += ctx->binary_object_size;-- /* the hashed shapes are counted separately */- if (sh && !sh->is_hashed) {- int hash_size = sh->prop_hash_mask + 1;- s->shape_count++;- s->shape_size += get_shape_size(hash_size, sh->prop_size);- }- list_for_each(el1, &ctx->loaded_modules) {- JSModuleDef *m = list_entry(el1, JSModuleDef, link);- s->memory_used_count += 1;- s->memory_used_size += sizeof(*m);- if (m->req_module_entries) {- s->memory_used_count += 1;- s->memory_used_size += m->req_module_entries_count * sizeof(*m->req_module_entries);- }- if (m->export_entries) {- s->memory_used_count += 1;- s->memory_used_size += m->export_entries_count * sizeof(*m->export_entries);- for (i = 0; i < m->export_entries_count; i++) {- JSExportEntry *me = &m->export_entries[i];- if (me->export_type == JS_EXPORT_TYPE_LOCAL && me->u.local.var_ref) {- /* potential multiple count */- s->memory_used_count += 1;- compute_value_size(me->u.local.var_ref->value, hp);- }- }- }- if (m->star_export_entries) {- s->memory_used_count += 1;- s->memory_used_size += m->star_export_entries_count * sizeof(*m->star_export_entries);- }- if (m->import_entries) {- s->memory_used_count += 1;- s->memory_used_size += m->import_entries_count * sizeof(*m->import_entries);- }- compute_value_size(m->module_ns, hp);- compute_value_size(m->func_obj, hp);- }- }-- list_for_each(el, &rt->gc_obj_list) {- JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link);- JSObject *p;- JSShape *sh;- JSShapeProperty *prs;-- /* XXX: could count the other GC object types too */- if (gp->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) {- compute_bytecode_size((JSFunctionBytecode *)gp, hp);- continue;- } else if (gp->gc_obj_type != JS_GC_OBJ_TYPE_JS_OBJECT) {- continue;- }- p = (JSObject *)gp;- sh = p->shape;- s->obj_count++;- if (p->prop) {- s->memory_used_count++;- s->prop_size += sh->prop_size * sizeof(*p->prop);- s->prop_count += sh->prop_count;- prs = get_shape_prop(sh);- for(i = 0; i < sh->prop_count; i++) {- JSProperty *pr = &p->prop[i];- if (prs->atom != JS_ATOM_NULL && !(prs->flags & JS_PROP_TMASK)) {- compute_value_size(pr->u.value, hp);- }- prs++;- }- }- /* the hashed shapes are counted separately */- if (!sh->is_hashed) {- int hash_size = sh->prop_hash_mask + 1;- s->shape_count++;- s->shape_size += get_shape_size(hash_size, sh->prop_size);- }-- switch(p->class_id) {- case JS_CLASS_ARRAY: /* u.array | length */- case JS_CLASS_ARGUMENTS: /* u.array | length */- s->array_count++;- if (p->fast_array) {- s->fast_array_count++;- if (p->u.array.u.values) {- s->memory_used_count++;- s->memory_used_size += p->u.array.count *- sizeof(*p->u.array.u.values);- s->fast_array_elements += p->u.array.count;- for (i = 0; i < p->u.array.count; i++) {- compute_value_size(p->u.array.u.values[i], hp);- }- }- }- break;- case JS_CLASS_NUMBER: /* u.object_data */- case JS_CLASS_STRING: /* u.object_data */- case JS_CLASS_BOOLEAN: /* u.object_data */- case JS_CLASS_SYMBOL: /* u.object_data */- case JS_CLASS_DATE: /* u.object_data */-#ifdef CONFIG_BIGNUM- case JS_CLASS_BIG_INT: /* u.object_data */- case JS_CLASS_BIG_FLOAT: /* u.object_data */- case JS_CLASS_BIG_DECIMAL: /* u.object_data */-#endif- compute_value_size(p->u.object_data, hp);- break;- case JS_CLASS_C_FUNCTION: /* u.cfunc */- s->c_func_count++;- break;- case JS_CLASS_BYTECODE_FUNCTION: /* u.func */- {- JSFunctionBytecode *b = p->u.func.function_bytecode;- JSVarRef **var_refs = p->u.func.var_refs;- /* home_object: object will be accounted for in list scan */- if (var_refs) {- s->memory_used_count++;- s->js_func_size += b->closure_var_count * sizeof(*var_refs);- for (i = 0; i < b->closure_var_count; i++) {- if (var_refs[i]) {- double ref_count = var_refs[i]->header.ref_count;- s->memory_used_count += 1 / ref_count;- s->js_func_size += sizeof(*var_refs[i]) / ref_count;- /* handle non object closed values */- if (var_refs[i]->pvalue == &var_refs[i]->value) {- /* potential multiple count */- compute_value_size(var_refs[i]->value, hp);- }- }- }- }- }- break;- case JS_CLASS_BOUND_FUNCTION: /* u.bound_function */- {- JSBoundFunction *bf = p->u.bound_function;- /* func_obj and this_val are objects */- for (i = 0; i < bf->argc; i++) {- compute_value_size(bf->argv[i], hp);- }- s->memory_used_count += 1;- s->memory_used_size += sizeof(*bf) + bf->argc * sizeof(*bf->argv);- }- break;- case JS_CLASS_C_FUNCTION_DATA: /* u.c_function_data_record */- {- JSCFunctionDataRecord *fd = p->u.c_function_data_record;- if (fd) {- for (i = 0; i < fd->data_len; i++) {- compute_value_size(fd->data[i], hp);- }- s->memory_used_count += 1;- s->memory_used_size += sizeof(*fd) + fd->data_len * sizeof(*fd->data);- }- }- break;- case JS_CLASS_REGEXP: /* u.regexp */- compute_jsstring_size(p->u.regexp.pattern, hp);- compute_jsstring_size(p->u.regexp.bytecode, hp);- break;-- case JS_CLASS_FOR_IN_ITERATOR: /* u.for_in_iterator */- {- JSForInIterator *it = p->u.for_in_iterator;- if (it) {- compute_value_size(it->obj, hp);- s->memory_used_count += 1;- s->memory_used_size += sizeof(*it);- }- }- break;- case JS_CLASS_ARRAY_BUFFER: /* u.array_buffer */- case JS_CLASS_SHARED_ARRAY_BUFFER: /* u.array_buffer */- {- JSArrayBuffer *abuf = p->u.array_buffer;- if (abuf) {- s->memory_used_count += 1;- s->memory_used_size += sizeof(*abuf);- if (abuf->data) {- s->memory_used_count += 1;- s->memory_used_size += abuf->byte_length;- }- }- }- break;- case JS_CLASS_GENERATOR: /* u.generator_data */- case JS_CLASS_UINT8C_ARRAY: /* u.typed_array / u.array */- case JS_CLASS_INT8_ARRAY: /* u.typed_array / u.array */- case JS_CLASS_UINT8_ARRAY: /* u.typed_array / u.array */- case JS_CLASS_INT16_ARRAY: /* u.typed_array / u.array */- case JS_CLASS_UINT16_ARRAY: /* u.typed_array / u.array */- case JS_CLASS_INT32_ARRAY: /* u.typed_array / u.array */- case JS_CLASS_UINT32_ARRAY: /* u.typed_array / u.array */-#ifdef CONFIG_BIGNUM- case JS_CLASS_BIG_INT64_ARRAY: /* u.typed_array / u.array */- case JS_CLASS_BIG_UINT64_ARRAY: /* u.typed_array / u.array */-#endif- case JS_CLASS_FLOAT32_ARRAY: /* u.typed_array / u.array */- case JS_CLASS_FLOAT64_ARRAY: /* u.typed_array / u.array */- case JS_CLASS_DATAVIEW: /* u.typed_array */-#ifdef CONFIG_BIGNUM- case JS_CLASS_FLOAT_ENV: /* u.float_env */-#endif- case JS_CLASS_MAP: /* u.map_state */- case JS_CLASS_SET: /* u.map_state */- case JS_CLASS_WEAKMAP: /* u.map_state */- case JS_CLASS_WEAKSET: /* u.map_state */- case JS_CLASS_MAP_ITERATOR: /* u.map_iterator_data */- case JS_CLASS_SET_ITERATOR: /* u.map_iterator_data */- case JS_CLASS_ARRAY_ITERATOR: /* u.array_iterator_data */- case JS_CLASS_STRING_ITERATOR: /* u.array_iterator_data */- case JS_CLASS_PROXY: /* u.proxy_data */- case JS_CLASS_PROMISE: /* u.promise_data */- case JS_CLASS_PROMISE_RESOLVE_FUNCTION: /* u.promise_function_data */- case JS_CLASS_PROMISE_REJECT_FUNCTION: /* u.promise_function_data */- case JS_CLASS_ASYNC_FUNCTION_RESOLVE: /* u.async_function_data */- case JS_CLASS_ASYNC_FUNCTION_REJECT: /* u.async_function_data */- case JS_CLASS_ASYNC_FROM_SYNC_ITERATOR: /* u.async_from_sync_iterator_data */- case JS_CLASS_ASYNC_GENERATOR: /* u.async_generator_data */- /* TODO */- default:- /* XXX: class definition should have an opaque block size */- if (p->u.opaque) {- s->memory_used_count += 1;- }- break;- }- }- s->obj_size += s->obj_count * sizeof(JSObject);-- /* hashed shapes */- s->memory_used_count++; /* rt->shape_hash */- s->memory_used_size += sizeof(rt->shape_hash[0]) * rt->shape_hash_size;- for(i = 0; i < rt->shape_hash_size; i++) {- JSShape *sh;- for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) {- int hash_size = sh->prop_hash_mask + 1;- s->shape_count++;- s->shape_size += get_shape_size(hash_size, sh->prop_size);- }- }-- /* atoms */- s->memory_used_count += 2; /* rt->atom_array, rt->atom_hash */- s->atom_count = rt->atom_count;- s->atom_size = sizeof(rt->atom_array[0]) * rt->atom_size +- sizeof(rt->atom_hash[0]) * rt->atom_hash_size;- for(i = 0; i < rt->atom_size; i++) {- JSAtomStruct *p = rt->atom_array[i];- if (!atom_is_free(p)) {- s->atom_size += (sizeof(*p) + (p->len << p->is_wide_char) +- 1 - p->is_wide_char);- }- }- s->str_count = round(mem.str_count);- s->str_size = round(mem.str_size);- s->js_func_count = mem.js_func_count;- s->js_func_size = round(mem.js_func_size);- s->js_func_code_size = mem.js_func_code_size;- s->js_func_pc2line_count = mem.js_func_pc2line_count;- s->js_func_pc2line_size = mem.js_func_pc2line_size;- s->memory_used_count += round(mem.memory_used_count) +- s->atom_count + s->str_count +- s->obj_count + s->shape_count +- s->js_func_count + s->js_func_pc2line_count;- s->memory_used_size += s->atom_size + s->str_size +- s->obj_size + s->prop_size + s->shape_size +- s->js_func_size + s->js_func_code_size + s->js_func_pc2line_size;-}--void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt)-{- fprintf(fp, "QuickJS memory usage -- "-#ifdef CONFIG_BIGNUM- "BigNum "-#endif- CONFIG_VERSION " version, %d-bit, malloc limit: %"PRId64"\n\n",- (int)sizeof(void *) * 8, (int64_t)(ssize_t)s->malloc_limit);-#if 1- if (rt) {- static const struct {- const char *name;- size_t size;- } object_types[] = {- { "JSRuntime", sizeof(JSRuntime) },- { "JSContext", sizeof(JSContext) },- { "JSObject", sizeof(JSObject) },- { "JSString", sizeof(JSString) },- { "JSFunctionBytecode", sizeof(JSFunctionBytecode) },- };- int i, usage_size_ok = 0;- for(i = 0; i < countof(object_types); i++) {- unsigned int size = object_types[i].size;- void *p = js_malloc_rt(rt, size);- if (p) {- unsigned int size1 = js_malloc_usable_size_rt(rt, p);- if (size1 >= size) {- usage_size_ok = 1;- fprintf(fp, " %3u + %-2u %s\n",- size, size1 - size, object_types[i].name);- }- js_free_rt(rt, p);- }- }- if (!usage_size_ok) {- fprintf(fp, " malloc_usable_size unavailable\n");- }- {- int obj_classes[JS_CLASS_INIT_COUNT + 1] = { 0 };- int class_id;- struct list_head *el;- list_for_each(el, &rt->gc_obj_list) {- JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link);- JSObject *p;- if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {- p = (JSObject *)gp;- obj_classes[min_uint32(p->class_id, JS_CLASS_INIT_COUNT)]++;- }- }- fprintf(fp, "\n" "JSObject classes\n");- if (obj_classes[0])- fprintf(fp, " %5d %2.0d %s\n", obj_classes[0], 0, "none");- for (class_id = 1; class_id < JS_CLASS_INIT_COUNT; class_id++) {- if (obj_classes[class_id]) {- char buf[ATOM_GET_STR_BUF_SIZE];- fprintf(fp, " %5d %2.0d %s\n", obj_classes[class_id], class_id,- JS_AtomGetStrRT(rt, buf, sizeof(buf), js_std_class_def[class_id - 1].class_name));- }- }- if (obj_classes[JS_CLASS_INIT_COUNT])- fprintf(fp, " %5d %2.0d %s\n", obj_classes[JS_CLASS_INIT_COUNT], 0, "other");- }- fprintf(fp, "\n");- }-#endif- fprintf(fp, "%-20s %8s %8s\n", "NAME", "COUNT", "SIZE");-- if (s->malloc_count) {- fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per block)\n",- "memory allocated", s->malloc_count, s->malloc_size,- (double)s->malloc_size / s->malloc_count);- fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%d overhead, %0.1f average slack)\n",- "memory used", s->memory_used_count, s->memory_used_size,- MALLOC_OVERHEAD, ((double)(s->malloc_size - s->memory_used_size) /- s->memory_used_count));- }- if (s->atom_count) {- fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per atom)\n",- "atoms", s->atom_count, s->atom_size,- (double)s->atom_size / s->atom_count);- }- if (s->str_count) {- fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per string)\n",- "strings", s->str_count, s->str_size,- (double)s->str_size / s->str_count);- }- if (s->obj_count) {- fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n",- "objects", s->obj_count, s->obj_size,- (double)s->obj_size / s->obj_count);- fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n",- " properties", s->prop_count, s->prop_size,- (double)s->prop_count / s->obj_count);- fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per shape)\n",- " shapes", s->shape_count, s->shape_size,- (double)s->shape_size / s->shape_count);- }- if (s->js_func_count) {- fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n",- "bytecode functions", s->js_func_count, s->js_func_size);- fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n",- " bytecode", s->js_func_count, s->js_func_code_size,- (double)s->js_func_code_size / s->js_func_count);- if (s->js_func_pc2line_count) {- fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n",- " pc2line", s->js_func_pc2line_count,- s->js_func_pc2line_size,- (double)s->js_func_pc2line_size / s->js_func_pc2line_count);- }- }- if (s->c_func_count) {- fprintf(fp, "%-20s %8"PRId64"\n", "C functions", s->c_func_count);- }- if (s->array_count) {- fprintf(fp, "%-20s %8"PRId64"\n", "arrays", s->array_count);- if (s->fast_array_count) {- fprintf(fp, "%-20s %8"PRId64"\n", " fast arrays", s->fast_array_count);- fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per fast array)\n",- " elements", s->fast_array_elements,- s->fast_array_elements * (int)sizeof(JSValue),- (double)s->fast_array_elements / s->fast_array_count);- }- }- if (s->binary_object_count) {- fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n",- "binary objects", s->binary_object_count, s->binary_object_size);- }-}--JSValue JS_GetGlobalObject(JSContext *ctx)-{- return JS_DupValue(ctx, ctx->global_obj);-}--/* WARNING: obj is freed */-JSValue JS_Throw(JSContext *ctx, JSValue obj)-{- JSRuntime *rt = ctx->rt;- JS_FreeValue(ctx, rt->current_exception);- rt->current_exception = obj;- return JS_EXCEPTION;-}--/* return the pending exception (cannot be called twice). */-JSValue JS_GetException(JSContext *ctx)-{- JSValue val;- JSRuntime *rt = ctx->rt;- val = rt->current_exception;- rt->current_exception = JS_NULL;- return val;-}--static void dbuf_put_leb128(DynBuf *s, uint32_t v)-{- uint32_t a;- for(;;) {- a = v & 0x7f;- v >>= 7;- if (v != 0) {- dbuf_putc(s, a | 0x80);- } else {- dbuf_putc(s, a);- break;- }- }-}--static void dbuf_put_sleb128(DynBuf *s, int32_t v1)-{- uint32_t v = v1;- dbuf_put_leb128(s, (2 * v) ^ -(v >> 31));-}--static int get_leb128(uint32_t *pval, const uint8_t *buf,- const uint8_t *buf_end)-{- const uint8_t *ptr = buf;- uint32_t v, a, i;- v = 0;- for(i = 0; i < 5; i++) {- if (unlikely(ptr >= buf_end))- break;- a = *ptr++;- v |= (a & 0x7f) << (i * 7);- if (!(a & 0x80)) {- *pval = v;- return ptr - buf;- }- }- *pval = 0;- return -1;-}--static int get_sleb128(int32_t *pval, const uint8_t *buf,- const uint8_t *buf_end)-{- int ret;- uint32_t val;- ret = get_leb128(&val, buf, buf_end);- if (ret < 0) {- *pval = 0;- return -1;- }- *pval = (val >> 1) ^ -(val & 1);- return ret;-}--static int find_line_num(JSContext *ctx, JSFunctionBytecode *b,- uint32_t pc_value)-{- const uint8_t *p_end, *p;- int new_line_num, line_num, pc, v, ret;- unsigned int op;-- if (!b->has_debug || !b->debug.pc2line_buf) {- /* function was stripped */- return -1;- }-- p = b->debug.pc2line_buf;- p_end = p + b->debug.pc2line_len;- pc = 0;- line_num = b->debug.line_num;- while (p < p_end) {- op = *p++;- if (op == 0) {- uint32_t val;- ret = get_leb128(&val, p, p_end);- if (ret < 0)- goto fail;- pc += val;- p += ret;- ret = get_sleb128(&v, p, p_end);- if (ret < 0) {- fail:- /* should never happen */- return b->debug.line_num;- }- p += ret;- new_line_num = line_num + v;- } else {- op -= PC2LINE_OP_FIRST;- pc += (op / PC2LINE_RANGE);- new_line_num = line_num + (op % PC2LINE_RANGE) + PC2LINE_BASE;- }- if (pc_value < pc)- return line_num;- line_num = new_line_num;- }- return line_num;-}--/* in order to avoid executing arbitrary code during the stack trace- generation, we only look at simple 'name' properties containing a- string. */-static const char *get_func_name(JSContext *ctx, JSValueConst func)-{- JSProperty *pr;- JSShapeProperty *prs;- JSValueConst val;- - if (JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)- return NULL;- prs = find_own_property(&pr, JS_VALUE_GET_OBJ(func), JS_ATOM_name);- if (!prs)- return NULL;- if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL)- return NULL;- val = pr->u.value;- if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)- return NULL;- return JS_ToCString(ctx, val);-}--#define JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL (1 << 0)-/* only taken into account if filename is provided */-#define JS_BACKTRACE_FLAG_SINGLE_LEVEL (1 << 1)--/* if filename != NULL, an additional level is added with the filename- and line number information (used for parse error). */-static void build_backtrace(JSContext *ctx, JSValueConst error_obj,- const char *filename, int line_num,- int backtrace_flags)-{- JSStackFrame *sf;- JSValue str;- DynBuf dbuf;- const char *func_name_str;- const char *str1;- JSObject *p;- BOOL backtrace_barrier;- - js_dbuf_init(ctx, &dbuf);- if (filename) {- dbuf_printf(&dbuf, " at %s", filename);- if (line_num != -1)- dbuf_printf(&dbuf, ":%d", line_num);- dbuf_putc(&dbuf, '\n');- str = JS_NewString(ctx, filename);- JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_fileName, str,- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);- JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_lineNumber, JS_NewInt32(ctx, line_num),- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);- if (backtrace_flags & JS_BACKTRACE_FLAG_SINGLE_LEVEL)- goto done;- }- for(sf = ctx->rt->current_stack_frame; sf != NULL; sf = sf->prev_frame) {- if (backtrace_flags & JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL) {- backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL;- continue;- }- func_name_str = get_func_name(ctx, sf->cur_func);- if (!func_name_str || func_name_str[0] == '\0')- str1 = "<anonymous>";- else- str1 = func_name_str;- dbuf_printf(&dbuf, " at %s", str1);- JS_FreeCString(ctx, func_name_str);-- p = JS_VALUE_GET_OBJ(sf->cur_func);- backtrace_barrier = FALSE;- if (js_class_has_bytecode(p->class_id)) {- JSFunctionBytecode *b;- const char *atom_str;- int line_num1;-- b = p->u.func.function_bytecode;- backtrace_barrier = b->backtrace_barrier;- if (b->has_debug) {- line_num1 = find_line_num(ctx, b,- sf->cur_pc - b->byte_code_buf - 1);- atom_str = JS_AtomToCString(ctx, b->debug.filename);- dbuf_printf(&dbuf, " (%s",- atom_str ? atom_str : "<null>");- JS_FreeCString(ctx, atom_str);- if (line_num1 != -1)- dbuf_printf(&dbuf, ":%d", line_num1);- dbuf_putc(&dbuf, ')');- }- } else {- dbuf_printf(&dbuf, " (native)");- }- dbuf_putc(&dbuf, '\n');- /* stop backtrace if JS_EVAL_FLAG_BACKTRACE_BARRIER was used */- if (backtrace_barrier)- break;- }- done:- dbuf_putc(&dbuf, '\0');- if (dbuf_error(&dbuf))- str = JS_NULL;- else- str = JS_NewString(ctx, (char *)dbuf.buf);- dbuf_free(&dbuf);- JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, str,- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);-}--/* Note: it is important that no exception is returned by this function */-static BOOL is_backtrace_needed(JSContext *ctx, JSValueConst obj)-{- JSObject *p;- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)- return FALSE;- p = JS_VALUE_GET_OBJ(obj);- if (p->class_id != JS_CLASS_ERROR)- return FALSE;- if (find_own_property1(p, JS_ATOM_stack))- return FALSE;- return TRUE;-}--JSValue JS_NewError(JSContext *ctx)-{- return JS_NewObjectClass(ctx, JS_CLASS_ERROR);-}--static JSValue JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num,- const char *fmt, va_list ap, BOOL add_backtrace)-{- char buf[256];- JSValue obj, ret;-- vsnprintf(buf, sizeof(buf), fmt, ap);- obj = JS_NewObjectProtoClass(ctx, ctx->native_error_proto[error_num],- JS_CLASS_ERROR);- if (unlikely(JS_IsException(obj))) {- /* out of memory: throw JS_NULL to avoid recursing */- obj = JS_NULL;- } else {- JS_DefinePropertyValue(ctx, obj, JS_ATOM_message,- JS_NewString(ctx, buf),- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);- }- if (add_backtrace) {- build_backtrace(ctx, obj, NULL, 0, 0);- }- ret = JS_Throw(ctx, obj);- return ret;-}--static JSValue JS_ThrowError(JSContext *ctx, JSErrorEnum error_num,- const char *fmt, va_list ap)-{- JSRuntime *rt = ctx->rt;- JSStackFrame *sf;- BOOL add_backtrace;-- /* the backtrace is added later if called from a bytecode function */- sf = rt->current_stack_frame;- add_backtrace = !rt->in_out_of_memory &&- (!sf || (JS_GetFunctionBytecode(sf->cur_func) == NULL));- return JS_ThrowError2(ctx, error_num, fmt, ap, add_backtrace);-}--JSValue __attribute__((format(printf, 2, 3))) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...)-{- JSValue val;- va_list ap;-- va_start(ap, fmt);- val = JS_ThrowError(ctx, JS_SYNTAX_ERROR, fmt, ap);- va_end(ap);- return val;-}--JSValue __attribute__((format(printf, 2, 3))) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...)-{- JSValue val;- va_list ap;-- va_start(ap, fmt);- val = JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap);- va_end(ap);- return val;-}--static int __attribute__((format(printf, 3, 4))) JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, const char *fmt, ...)-{- va_list ap;-- if ((flags & JS_PROP_THROW) ||- ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {- va_start(ap, fmt);- JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap);- va_end(ap);- return -1;- } else {- return FALSE;- }-}--/* never use it directly */-static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowTypeErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...)-{- char buf[ATOM_GET_STR_BUF_SIZE];- return JS_ThrowTypeError(ctx, fmt,- JS_AtomGetStr(ctx, buf, sizeof(buf), atom));-}--/* never use it directly */-static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowSyntaxErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...)-{- char buf[ATOM_GET_STR_BUF_SIZE];- return JS_ThrowSyntaxError(ctx, fmt,- JS_AtomGetStr(ctx, buf, sizeof(buf), atom));-}--/* %s is replaced by 'atom'. The macro is used so that gcc can check- the format string. */-#define JS_ThrowTypeErrorAtom(ctx, fmt, atom) __JS_ThrowTypeErrorAtom(ctx, atom, fmt, "")-#define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) __JS_ThrowSyntaxErrorAtom(ctx, atom, fmt, "")--static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom)-{- if ((flags & JS_PROP_THROW) ||- ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {- JS_ThrowTypeErrorAtom(ctx, "'%s' is read-only", atom);- return -1;- } else {- return FALSE;- }-}--JSValue __attribute__((format(printf, 2, 3))) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...)-{- JSValue val;- va_list ap;-- va_start(ap, fmt);- val = JS_ThrowError(ctx, JS_REFERENCE_ERROR, fmt, ap);- va_end(ap);- return val;-}--JSValue __attribute__((format(printf, 2, 3))) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...)-{- JSValue val;- va_list ap;-- va_start(ap, fmt);- val = JS_ThrowError(ctx, JS_RANGE_ERROR, fmt, ap);- va_end(ap);- return val;-}--JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...)-{- JSValue val;- va_list ap;-- va_start(ap, fmt);- val = JS_ThrowError(ctx, JS_INTERNAL_ERROR, fmt, ap);- va_end(ap);- return val;-}--JSValue JS_ThrowOutOfMemory(JSContext *ctx)-{- JSRuntime *rt = ctx->rt;- if (!rt->in_out_of_memory) {- rt->in_out_of_memory = TRUE;- JS_ThrowInternalError(ctx, "out of memory");- rt->in_out_of_memory = FALSE;- }- return JS_EXCEPTION;-}--static JSValue JS_ThrowStackOverflow(JSContext *ctx)-{- return JS_ThrowInternalError(ctx, "stack overflow");-}--static JSValue JS_ThrowTypeErrorNotAnObject(JSContext *ctx)-{- return JS_ThrowTypeError(ctx, "not an object");-}--static JSValue JS_ThrowTypeErrorNotASymbol(JSContext *ctx)-{- return JS_ThrowTypeError(ctx, "not a symbol");-}--static JSValue JS_ThrowReferenceErrorNotDefined(JSContext *ctx, JSAtom name)-{- char buf[ATOM_GET_STR_BUF_SIZE];- return JS_ThrowReferenceError(ctx, "'%s' is not defined",- JS_AtomGetStr(ctx, buf, sizeof(buf), name));-}--static JSValue JS_ThrowReferenceErrorUninitialized(JSContext *ctx, JSAtom name)-{- char buf[ATOM_GET_STR_BUF_SIZE];- return JS_ThrowReferenceError(ctx, "%s is not initialized",- name == JS_ATOM_NULL ? "lexical variable" :- JS_AtomGetStr(ctx, buf, sizeof(buf), name));-}--static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id)-{- JSRuntime *rt = ctx->rt;- JSAtom name;- name = rt->class_array[class_id].class_name;- return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name);-}--static no_inline __exception int __js_poll_interrupts(JSContext *ctx)-{- JSRuntime *rt = ctx->rt;- ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT;- if (rt->interrupt_handler) {- if (rt->interrupt_handler(rt, rt->interrupt_opaque)) {- /* XXX: should set a specific flag to avoid catching */- JS_ThrowInternalError(ctx, "interrupted");- JS_SetUncatchableError(ctx, ctx->rt->current_exception, TRUE);- return -1;- }- }- return 0;-}--static inline __exception int js_poll_interrupts(JSContext *ctx)-{- if (unlikely(--ctx->interrupt_counter <= 0)) {- return __js_poll_interrupts(ctx);- } else {- return 0;- }-}--/* return -1 (exception) or TRUE/FALSE */-static int JS_SetPrototypeInternal(JSContext *ctx, JSValueConst obj,- JSValueConst proto_val,- BOOL throw_flag)-{- JSObject *proto, *p, *p1;- JSShape *sh;-- if (throw_flag) {- if (JS_VALUE_GET_TAG(obj) == JS_TAG_NULL ||- JS_VALUE_GET_TAG(obj) == JS_TAG_UNDEFINED)- goto not_obj;- } else {- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)- goto not_obj;- }- p = JS_VALUE_GET_OBJ(obj);- if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) {- if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_NULL) {- not_obj:- JS_ThrowTypeErrorNotAnObject(ctx);- return -1;- }- proto = NULL;- } else {- proto = JS_VALUE_GET_OBJ(proto_val);- }-- if (throw_flag && JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)- return TRUE;-- if (unlikely(p->class_id == JS_CLASS_PROXY))- return js_proxy_setPrototypeOf(ctx, obj, proto_val, throw_flag);- sh = p->shape;- if (sh->proto == proto)- return TRUE;- if (!p->extensible) {- if (throw_flag) {- JS_ThrowTypeError(ctx, "object is not extensible");- return -1;- } else {- return FALSE;- }- }- if (proto) {- /* check if there is a cycle */- p1 = proto;- do {- if (p1 == p) {- if (throw_flag) {- JS_ThrowTypeError(ctx, "circular prototype chain");- return -1;- } else {- return FALSE;- }- }- /* Note: for Proxy objects, proto is NULL */- p1 = p1->shape->proto;- } while (p1 != NULL);- JS_DupValue(ctx, proto_val);- }-- if (js_shape_prepare_update(ctx, p, NULL))- return -1;- sh = p->shape;- if (sh->proto)- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto));- sh->proto = proto;- return TRUE;-}--/* return -1 (exception) or TRUE/FALSE */-int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val)-{- return JS_SetPrototypeInternal(ctx, obj, proto_val, TRUE);-}--/* Only works for primitive types, otherwise return JS_NULL. */-static JSValueConst JS_GetPrototypePrimitive(JSContext *ctx, JSValueConst val)-{- switch(JS_VALUE_GET_NORM_TAG(val)) {-#ifdef CONFIG_BIGNUM- case JS_TAG_BIG_INT:- val = ctx->class_proto[JS_CLASS_BIG_INT];- break;- case JS_TAG_BIG_FLOAT:- val = ctx->class_proto[JS_CLASS_BIG_FLOAT];- break;- case JS_TAG_BIG_DECIMAL:- val = ctx->class_proto[JS_CLASS_BIG_DECIMAL];- break;-#endif- case JS_TAG_INT:- case JS_TAG_FLOAT64:- val = ctx->class_proto[JS_CLASS_NUMBER];- break;- case JS_TAG_BOOL:- val = ctx->class_proto[JS_CLASS_BOOLEAN];- break;- case JS_TAG_STRING:- val = ctx->class_proto[JS_CLASS_STRING];- break;- case JS_TAG_SYMBOL:- val = ctx->class_proto[JS_CLASS_SYMBOL];- break;- case JS_TAG_OBJECT:- case JS_TAG_NULL:- case JS_TAG_UNDEFINED:- default:- val = JS_NULL;- break;- }- return val;-}--/* Return an Object, JS_NULL or JS_EXCEPTION in case of Proxy object. */-JSValue JS_GetPrototype(JSContext *ctx, JSValueConst obj)-{- JSValue val;- if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {- JSObject *p;- p = JS_VALUE_GET_OBJ(obj);- if (unlikely(p->class_id == JS_CLASS_PROXY)) {- val = js_proxy_getPrototypeOf(ctx, obj);- } else {- p = p->shape->proto;- if (!p)- val = JS_NULL;- else- val = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));- }- } else {- val = JS_DupValue(ctx, JS_GetPrototypePrimitive(ctx, obj));- }- return val;-}--static JSValue JS_GetPrototypeFree(JSContext *ctx, JSValue obj)-{- JSValue obj1;- obj1 = JS_GetPrototype(ctx, obj);- JS_FreeValue(ctx, obj);- return obj1;-}--/* return TRUE, FALSE or (-1) in case of exception */-static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val,- JSValueConst obj)-{- JSValue obj_proto;- JSObject *proto;- const JSObject *p, *proto1;- BOOL ret;-- if (!JS_IsFunction(ctx, obj))- return FALSE;- p = JS_VALUE_GET_OBJ(obj);- if (p->class_id == JS_CLASS_BOUND_FUNCTION) {- JSBoundFunction *s = p->u.bound_function;- return JS_IsInstanceOf(ctx, val, s->func_obj);- }-- /* Only explicitly boxed values are instances of constructors */- if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)- return FALSE;- obj_proto = JS_GetProperty(ctx, obj, JS_ATOM_prototype);- if (JS_VALUE_GET_TAG(obj_proto) != JS_TAG_OBJECT) {- if (!JS_IsException(obj_proto))- JS_ThrowTypeError(ctx, "operand 'prototype' property is not an object");- ret = -1;- goto done;- }- proto = JS_VALUE_GET_OBJ(obj_proto);- p = JS_VALUE_GET_OBJ(val);- for(;;) {- proto1 = p->shape->proto;- if (!proto1) {- /* slow case if proxy in the prototype chain */- if (unlikely(p->class_id == JS_CLASS_PROXY)) {- JSValue obj1;- obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, (JSObject *)p));- for(;;) {- obj1 = JS_GetPrototypeFree(ctx, obj1);- if (JS_IsException(obj1)) {- ret = -1;- break;- }- if (JS_IsNull(obj1)) {- ret = FALSE;- break;- }- if (proto == JS_VALUE_GET_OBJ(obj1)) {- JS_FreeValue(ctx, obj1);- ret = TRUE;- break;- }- /* must check for timeout to avoid infinite loop */- if (js_poll_interrupts(ctx)) {- JS_FreeValue(ctx, obj1);- ret = -1;- break;- }- }- } else {- ret = FALSE;- }- break;- }- p = proto1;- if (proto == p) {- ret = TRUE;- break;- }- }-done:- JS_FreeValue(ctx, obj_proto);- return ret;-}--/* return TRUE, FALSE or (-1) in case of exception */-int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj)-{- JSValue method;-- if (!JS_IsObject(obj))- goto fail;- method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_hasInstance);- if (JS_IsException(method))- return -1;- if (!JS_IsNull(method) && !JS_IsUndefined(method)) {- JSValue ret;- ret = JS_CallFree(ctx, method, obj, 1, &val);- return JS_ToBoolFree(ctx, ret);- }-- /* legacy case */- if (!JS_IsFunction(ctx, obj)) {- fail:- JS_ThrowTypeError(ctx, "invalid 'instanceof' right operand");- return -1;- }- return JS_OrdinaryIsInstanceOf(ctx, val, obj);-}--typedef int JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque);--static JSAutoInitFunc *js_autoinit_func_table[] = {- js_instantiate_prototype, /* JS_AUTOINIT_ID_PROTOTYPE */- js_module_ns_autoinit, /* JS_AUTOINIT_ID_MODULE_NS */- JS_InstantiateFunctionListItem, /* JS_AUTOINIT_ID_PROP */-};--static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop,- JSProperty *pr)-{- int ret;- JSContext *realm;- JSAutoInitFunc *func;- - realm = js_autoinit_get_realm(pr);- func = js_autoinit_func_table[js_autoinit_get_id(pr)];- ret = func(realm, p, prop, pr->u.init.opaque);- return ret;-}--JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,- JSAtom prop, JSValueConst this_obj,- BOOL throw_ref_error)-{- JSObject *p;- JSProperty *pr;- JSShapeProperty *prs;- uint32_t tag;-- tag = JS_VALUE_GET_TAG(obj);- if (unlikely(tag != JS_TAG_OBJECT)) {- switch(tag) {- case JS_TAG_NULL:- return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", prop);- case JS_TAG_UNDEFINED:- return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of undefined", prop);- case JS_TAG_EXCEPTION:- return JS_EXCEPTION;- case JS_TAG_STRING:- {- JSString *p1 = JS_VALUE_GET_STRING(obj);- if (__JS_AtomIsTaggedInt(prop)) {- uint32_t idx, ch;- idx = __JS_AtomToUInt32(prop);- if (idx < p1->len) {- if (p1->is_wide_char)- ch = p1->u.str16[idx];- else- ch = p1->u.str8[idx];- return js_new_string_char(ctx, ch);- }- } else if (prop == JS_ATOM_length) {- return JS_NewInt32(ctx, p1->len);- }- }- break;- default:- break;- }- /* cannot raise an exception */- p = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj));- if (!p)- return JS_UNDEFINED;- } else {- p = JS_VALUE_GET_OBJ(obj);- }-- for(;;) {- prs = find_own_property(&pr, p, prop);- if (prs) {- /* found */- if (unlikely(prs->flags & JS_PROP_TMASK)) {- if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {- if (unlikely(!pr->u.getset.getter)) {- return JS_UNDEFINED;- } else {- JSValue func = JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter);- /* Note: the field could be removed in the getter */- func = JS_DupValue(ctx, func);- return JS_CallFree(ctx, func, this_obj, 0, NULL);- }- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {- JSValue val = *pr->u.var_ref->pvalue;- if (unlikely(JS_IsUninitialized(val)))- return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);- return JS_DupValue(ctx, val);- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {- /* Instantiate property and retry */- if (JS_AutoInitProperty(ctx, p, prop, pr))- return JS_EXCEPTION;- continue;- }- } else {- return JS_DupValue(ctx, pr->u.value);- }- }- if (unlikely(p->is_exotic)) {- /* exotic behaviors */- if (p->fast_array) {- if (__JS_AtomIsTaggedInt(prop)) {- uint32_t idx = __JS_AtomToUInt32(prop);- if (idx < p->u.array.count) {- /* we avoid duplicating the code */- return JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx);- } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&- p->class_id <= JS_CLASS_FLOAT64_ARRAY) {- goto typed_array_oob;- }- } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&- p->class_id <= JS_CLASS_FLOAT64_ARRAY) {- int ret;- ret = JS_AtomIsNumericIndex(ctx, prop);- if (ret != 0) {- if (ret < 0)- return JS_EXCEPTION;- typed_array_oob:- if (typed_array_is_detached(ctx, p))- return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);- return JS_UNDEFINED;- }- }- } else {- const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;- if (em) {- if (em->get_property) {- JSValue obj1, retval;- /* XXX: should pass throw_ref_error */- /* Note: if 'p' is a prototype, it can be- freed in the called function */- obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));- retval = em->get_property(ctx, obj1, prop, this_obj);- JS_FreeValue(ctx, obj1);- return retval;- }- if (em->get_own_property) {- JSPropertyDescriptor desc;- int ret;- JSValue obj1;-- /* Note: if 'p' is a prototype, it can be- freed in the called function */- obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));- ret = em->get_own_property(ctx, &desc, obj1, prop);- JS_FreeValue(ctx, obj1);- if (ret < 0)- return JS_EXCEPTION;- if (ret) {- if (desc.flags & JS_PROP_GETSET) {- JS_FreeValue(ctx, desc.setter);- return JS_CallFree(ctx, desc.getter, this_obj, 0, NULL);- } else {- return desc.value;- }- }- }- }- }- }- p = p->shape->proto;- if (!p)- break;- }- if (unlikely(throw_ref_error)) {- return JS_ThrowReferenceErrorNotDefined(ctx, prop);- } else {- return JS_UNDEFINED;- }-}--static JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *ctx, JSAtom atom)-{- return JS_ThrowTypeErrorAtom(ctx, "private class field '%s' does not exist",- atom);-}--/* Private fields can be added even on non extensible objects or- Proxies */-static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj,- JSValueConst name, JSValue val)-{- JSObject *p;- JSShapeProperty *prs;- JSProperty *pr;- JSAtom prop;-- if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {- JS_ThrowTypeErrorNotAnObject(ctx);- goto fail;- }- /* safety check */- if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) {- JS_ThrowTypeErrorNotASymbol(ctx);- goto fail;- }- prop = js_symbol_to_atom(ctx, (JSValue)name);- p = JS_VALUE_GET_OBJ(obj);- prs = find_own_property(&pr, p, prop);- if (prs) {- JS_ThrowTypeErrorAtom(ctx, "private class field '%s' already exists",- prop);- goto fail;- }- pr = add_property(ctx, p, prop, JS_PROP_C_W_E);- if (unlikely(!pr)) {- fail:- JS_FreeValue(ctx, val);- return -1;- }- pr->u.value = val;- return 0;-}--static JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj,- JSValueConst name)-{- JSObject *p;- JSShapeProperty *prs;- JSProperty *pr;- JSAtom prop;-- if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))- return JS_ThrowTypeErrorNotAnObject(ctx);- /* safety check */- if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL))- return JS_ThrowTypeErrorNotASymbol(ctx);- prop = js_symbol_to_atom(ctx, (JSValue)name);- p = JS_VALUE_GET_OBJ(obj);- prs = find_own_property(&pr, p, prop);- if (!prs) {- JS_ThrowTypeErrorPrivateNotFound(ctx, prop);- return JS_EXCEPTION;- }- return JS_DupValue(ctx, pr->u.value);-}--static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj,- JSValueConst name, JSValue val)-{- JSObject *p;- JSShapeProperty *prs;- JSProperty *pr;- JSAtom prop;-- if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {- JS_ThrowTypeErrorNotAnObject(ctx);- goto fail;- }- /* safety check */- if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) {- JS_ThrowTypeErrorNotASymbol(ctx);- goto fail;- }- prop = js_symbol_to_atom(ctx, (JSValue)name);- p = JS_VALUE_GET_OBJ(obj);- prs = find_own_property(&pr, p, prop);- if (!prs) {- JS_ThrowTypeErrorPrivateNotFound(ctx, prop);- fail:- JS_FreeValue(ctx, val);- return -1;- }- set_value(ctx, &pr->u.value, val);- return 0;-}--static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj)-{- JSObject *p, *p1;- JSShapeProperty *prs;- JSProperty *pr;- JSValue brand;- JSAtom brand_atom;- - if (unlikely(JS_VALUE_GET_TAG(home_obj) != JS_TAG_OBJECT)) {- JS_ThrowTypeErrorNotAnObject(ctx);- return -1;- }- p = JS_VALUE_GET_OBJ(home_obj);- prs = find_own_property(&pr, p, JS_ATOM_Private_brand);- if (!prs) {- brand = JS_NewSymbolFromAtom(ctx, JS_ATOM_brand, JS_ATOM_TYPE_PRIVATE);- if (JS_IsException(brand))- return -1;- /* if the brand is not present, add it */- pr = add_property(ctx, p, JS_ATOM_Private_brand, JS_PROP_C_W_E);- if (!pr) {- JS_FreeValue(ctx, brand);- return -1;- }- pr->u.value = JS_DupValue(ctx, brand);- } else {- brand = JS_DupValue(ctx, pr->u.value);- }- brand_atom = js_symbol_to_atom(ctx, brand);- - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {- JS_ThrowTypeErrorNotAnObject(ctx);- JS_FreeAtom(ctx, brand_atom);- return -1;- }- p1 = JS_VALUE_GET_OBJ(obj);- pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E);- JS_FreeAtom(ctx, brand_atom);- if (!pr)- return -1;- pr->u.value = JS_UNDEFINED;- return 0;-}--static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func)-{- JSObject *p, *p1, *home_obj;- JSShapeProperty *prs;- JSProperty *pr;- JSValueConst brand;- - /* get the home object of 'func' */- if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)) {- not_obj:- JS_ThrowTypeErrorNotAnObject(ctx);- return -1;- }- p1 = JS_VALUE_GET_OBJ(func);- if (!js_class_has_bytecode(p1->class_id))- goto not_obj;- home_obj = p1->u.func.home_object;- if (!home_obj)- goto not_obj;- prs = find_own_property(&pr, home_obj, JS_ATOM_Private_brand);- if (!prs) {- JS_ThrowTypeError(ctx, "expecting <brand> private field");- return -1;- }- brand = pr->u.value;- /* safety check */- if (unlikely(JS_VALUE_GET_TAG(brand) != JS_TAG_SYMBOL))- goto not_obj;- - /* get the brand array of 'obj' */- if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))- goto not_obj;- p = JS_VALUE_GET_OBJ(obj);- prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, (JSValue)brand));- if (!prs) {- JS_ThrowTypeError(ctx, "invalid brand on object");- return -1;- }- return 0;-}--static int num_keys_cmp(const void *p1, const void *p2, void *opaque)-{- JSContext *ctx = opaque;- JSAtom atom1 = ((const JSPropertyEnum *)p1)->atom;- JSAtom atom2 = ((const JSPropertyEnum *)p2)->atom;- uint32_t v1, v2;- BOOL atom1_is_integer, atom2_is_integer;-- atom1_is_integer = JS_AtomIsArrayIndex(ctx, &v1, atom1);- atom2_is_integer = JS_AtomIsArrayIndex(ctx, &v2, atom2);- assert(atom1_is_integer && atom2_is_integer);- if (v1 < v2)- return -1;- else if (v1 == v2)- return 0;- else- return 1;-}--static void js_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len)-{- uint32_t i;- if (tab) {- for(i = 0; i < len; i++)- JS_FreeAtom(ctx, tab[i].atom);- js_free(ctx, tab);- }-}--/* return < 0 in case if exception, 0 if OK. ptab and its atoms must- be freed by the user. */-static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx,- JSPropertyEnum **ptab,- uint32_t *plen,- JSObject *p, int flags)-{- int i, j;- JSShape *sh;- JSShapeProperty *prs;- JSPropertyEnum *tab_atom, *tab_exotic;- JSAtom atom;- uint32_t num_keys_count, str_keys_count, sym_keys_count, atom_count;- uint32_t num_index, str_index, sym_index, exotic_count;- BOOL is_enumerable, num_sorted;- uint32_t num_key;- JSAtomKindEnum kind;- - /* clear pointer for consistency in case of failure */- *ptab = NULL;- *plen = 0;-- /* compute the number of returned properties */- num_keys_count = 0;- str_keys_count = 0;- sym_keys_count = 0;- exotic_count = 0;- tab_exotic = NULL;- sh = p->shape;- for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {- atom = prs->atom;- if (atom != JS_ATOM_NULL) {- is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0);- kind = JS_AtomGetKind(ctx, atom);- if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&- ((flags >> kind) & 1) != 0) {- /* need to raise an exception in case of the module- name space (implicit GetOwnProperty) */- if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) &&- (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY))) {- JSVarRef *var_ref = p->prop[i].u.var_ref;- if (unlikely(JS_IsUninitialized(*var_ref->pvalue))) {- JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);- return -1;- }- }- if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) {- num_keys_count++;- } else if (kind == JS_ATOM_KIND_STRING) {- str_keys_count++;- } else {- sym_keys_count++;- }- }- }- }-- if (p->is_exotic) {- if (p->fast_array) {- /* the implicit GetOwnProperty raises an exception if the- typed array is detached */- if ((flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY)) &&- (p->class_id >= JS_CLASS_UINT8C_ARRAY &&- p->class_id <= JS_CLASS_FLOAT64_ARRAY) &&- typed_array_is_detached(ctx, p) &&- typed_array_get_length(ctx, p) != 0) {- JS_ThrowTypeErrorDetachedArrayBuffer(ctx);- return -1;- }- if (flags & JS_GPN_STRING_MASK) {- num_keys_count += p->u.array.count;- }- } else {- const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;- if (em && em->get_own_property_names) {- if (em->get_own_property_names(ctx, &tab_exotic, &exotic_count,- JS_MKPTR(JS_TAG_OBJECT, p)))- return -1;- for(i = 0; i < exotic_count; i++) {- atom = tab_exotic[i].atom;- kind = JS_AtomGetKind(ctx, atom);- if (((flags >> kind) & 1) != 0) {- is_enumerable = FALSE;- if (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY)) {- JSPropertyDescriptor desc;- int res;- /* set the "is_enumerable" field if necessary */- res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);- if (res < 0) {- js_free_prop_enum(ctx, tab_exotic, exotic_count);- return -1;- }- if (res) {- is_enumerable =- ((desc.flags & JS_PROP_ENUMERABLE) != 0);- js_free_desc(ctx, &desc);- }- tab_exotic[i].is_enumerable = is_enumerable;- }- if (!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) {- if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) {- num_keys_count++;- } else if (kind == JS_ATOM_KIND_STRING) {- str_keys_count++;- } else {- sym_keys_count++;- }- }- }- }- }- }- }-- /* fill them */-- atom_count = num_keys_count + str_keys_count + sym_keys_count;- /* avoid allocating 0 bytes */- tab_atom = js_malloc(ctx, sizeof(tab_atom[0]) * max_int(atom_count, 1));- if (!tab_atom) {- js_free_prop_enum(ctx, tab_exotic, exotic_count);- return -1;- }-- num_index = 0;- str_index = num_keys_count;- sym_index = str_index + str_keys_count;-- num_sorted = TRUE;- sh = p->shape;- for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {- atom = prs->atom;- if (atom != JS_ATOM_NULL) {- is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0);- kind = JS_AtomGetKind(ctx, atom);- if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&- ((flags >> kind) & 1) != 0) {- if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) {- j = num_index++;- num_sorted = FALSE;- } else if (kind == JS_ATOM_KIND_STRING) {- j = str_index++;- } else {- j = sym_index++;- }- tab_atom[j].atom = JS_DupAtom(ctx, atom);- tab_atom[j].is_enumerable = is_enumerable;- }- }- }-- if (p->is_exotic) {- if (p->fast_array) {- if (flags & JS_GPN_STRING_MASK) {- for(i = 0; i < p->u.array.count; i++) {- tab_atom[num_index].atom = __JS_AtomFromUInt32(i);- if (tab_atom[num_index].atom == JS_ATOM_NULL) {- js_free_prop_enum(ctx, tab_exotic, exotic_count);- js_free_prop_enum(ctx, tab_atom, num_index);- return -1;- }- tab_atom[num_index].is_enumerable = TRUE;- num_index++;- }- }- }- if (exotic_count > 0) {- for(i = 0; i < exotic_count; i++) {- atom = tab_exotic[i].atom;- is_enumerable = tab_exotic[i].is_enumerable;- kind = JS_AtomGetKind(ctx, atom);- if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&- ((flags >> kind) & 1) != 0) {- if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) {- j = num_index++;- num_sorted = FALSE;- } else if (kind == JS_ATOM_KIND_STRING) {- j = str_index++;- } else {- j = sym_index++;- }- tab_atom[j].atom = atom;- tab_atom[j].is_enumerable = is_enumerable;- } else {- JS_FreeAtom(ctx, atom);- }- }- }- js_free(ctx, tab_exotic);- }-- assert(num_index == num_keys_count);- assert(str_index == num_keys_count + str_keys_count);- assert(sym_index == atom_count);-- if (num_keys_count != 0 && !num_sorted) {- rqsort(tab_atom, num_keys_count, sizeof(tab_atom[0]), num_keys_cmp,- ctx);- }- *ptab = tab_atom;- *plen = atom_count;- return 0;-}--int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab,- uint32_t *plen, JSValueConst obj, int flags)-{- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {- JS_ThrowTypeErrorNotAnObject(ctx);- return -1;- }- return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen,- JS_VALUE_GET_OBJ(obj), flags);-}--/* Return -1 if exception,- FALSE if the property does not exist, TRUE if it exists. If TRUE is- returned, the property descriptor 'desc' is filled present. */-static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,- JSObject *p, JSAtom prop)-{- JSShapeProperty *prs;- JSProperty *pr;--retry:- prs = find_own_property(&pr, p, prop);- if (prs) {- if (desc) {- desc->flags = prs->flags & JS_PROP_C_W_E;- desc->getter = JS_UNDEFINED;- desc->setter = JS_UNDEFINED;- desc->value = JS_UNDEFINED;- if (unlikely(prs->flags & JS_PROP_TMASK)) {- if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {- desc->flags |= JS_PROP_GETSET;- if (pr->u.getset.getter)- desc->getter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));- if (pr->u.getset.setter)- desc->setter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {- JSValue val = *pr->u.var_ref->pvalue;- if (unlikely(JS_IsUninitialized(val))) {- JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);- return -1;- }- desc->value = JS_DupValue(ctx, val);- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {- /* Instantiate property and retry */- if (JS_AutoInitProperty(ctx, p, prop, pr))- return -1;- goto retry;- }- } else {- desc->value = JS_DupValue(ctx, pr->u.value);- }- } else {- /* for consistency, send the exception even if desc is NULL */- if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF)) {- if (unlikely(JS_IsUninitialized(*pr->u.var_ref->pvalue))) {- JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);- return -1;- }- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {- /* nothing to do: delay instantiation until actual value and/or attributes are read */- }- }- return TRUE;- }- if (p->is_exotic) {- if (p->fast_array) {- /* specific case for fast arrays */- if (__JS_AtomIsTaggedInt(prop)) {- uint32_t idx;- idx = __JS_AtomToUInt32(prop);- if (idx < p->u.array.count) {- if (desc) {- desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE;- if (p->class_id == JS_CLASS_ARRAY ||- p->class_id == JS_CLASS_ARGUMENTS)- desc->flags |= JS_PROP_CONFIGURABLE;- desc->getter = JS_UNDEFINED;- desc->setter = JS_UNDEFINED;- desc->value = JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx);- }- return TRUE;- }- }- if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&- p->class_id <= JS_CLASS_FLOAT64_ARRAY) {- int ret;- ret = JS_AtomIsNumericIndex(ctx, prop);- if (ret != 0) {- if (ret < 0)- return -1;- if (typed_array_is_detached(ctx, p)) {- JS_ThrowTypeErrorDetachedArrayBuffer(ctx);- return -1;- }- }- }- } else {- const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;- if (em && em->get_own_property) {- return em->get_own_property(ctx, desc,- JS_MKPTR(JS_TAG_OBJECT, p), prop);- }- }- }- return FALSE;-}--int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc,- JSValueConst obj, JSAtom prop)-{- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {- JS_ThrowTypeErrorNotAnObject(ctx);- return -1;- }- return JS_GetOwnPropertyInternal(ctx, desc, JS_VALUE_GET_OBJ(obj), prop);-}--/* return -1 if exception (Proxy object only) or TRUE/FALSE */-int JS_IsExtensible(JSContext *ctx, JSValueConst obj)-{- JSObject *p;-- if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))- return FALSE;- p = JS_VALUE_GET_OBJ(obj);- if (unlikely(p->class_id == JS_CLASS_PROXY))- return js_proxy_isExtensible(ctx, obj);- else- return p->extensible;-}--/* return -1 if exception (Proxy object only) or TRUE/FALSE */-int JS_PreventExtensions(JSContext *ctx, JSValueConst obj)-{- JSObject *p;-- if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))- return FALSE;- p = JS_VALUE_GET_OBJ(obj);- if (unlikely(p->class_id == JS_CLASS_PROXY))- return js_proxy_preventExtensions(ctx, obj);- p->extensible = FALSE;- return TRUE;-}--/* return -1 if exception otherwise TRUE or FALSE */-int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop)-{- JSObject *p;- int ret;- JSValue obj1;-- if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))- return FALSE;- p = JS_VALUE_GET_OBJ(obj);- for(;;) {- if (p->is_exotic) {- const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;- if (em && em->has_property) {- /* has_property can free the prototype */- obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));- ret = em->has_property(ctx, obj1, prop);- JS_FreeValue(ctx, obj1);- return ret;- }- }- /* JS_GetOwnPropertyInternal can free the prototype */- JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));- ret = JS_GetOwnPropertyInternal(ctx, NULL, p, prop);- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));- if (ret != 0)- return ret;- if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&- p->class_id <= JS_CLASS_FLOAT64_ARRAY) {- ret = JS_AtomIsNumericIndex(ctx, prop);- if (ret != 0) {- if (ret < 0)- return -1;- /* the detached array test was done in- JS_GetOwnPropertyInternal() */- return FALSE;- }- }- p = p->shape->proto;- if (!p)- break;- }- return FALSE;-}--/* val must be a symbol */-static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val)-{- JSAtomStruct *p = JS_VALUE_GET_PTR(val);- return js_get_atom_index(ctx->rt, p);-}--/* return JS_ATOM_NULL in case of exception */-JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val)-{- JSAtom atom;- uint32_t tag;- tag = JS_VALUE_GET_TAG(val);- if (tag == JS_TAG_INT &&- (uint32_t)JS_VALUE_GET_INT(val) <= JS_ATOM_MAX_INT) {- /* fast path for integer values */- atom = __JS_AtomFromUInt32(JS_VALUE_GET_INT(val));- } else if (tag == JS_TAG_SYMBOL) {- JSAtomStruct *p = JS_VALUE_GET_PTR(val);- atom = JS_DupAtom(ctx, js_get_atom_index(ctx->rt, p));- } else {- JSValue str;- str = JS_ToPropertyKey(ctx, val);- if (JS_IsException(str))- return JS_ATOM_NULL;- if (JS_VALUE_GET_TAG(str) == JS_TAG_SYMBOL) {- atom = js_symbol_to_atom(ctx, str);- } else {- atom = JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(str));- }- }- return atom;-}--static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj,- JSValue prop)-{- JSAtom atom;- JSValue ret;-- if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT &&- JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) {- JSObject *p;- uint32_t idx, len;- /* fast path for array access */- p = JS_VALUE_GET_OBJ(this_obj);- idx = JS_VALUE_GET_INT(prop);- len = (uint32_t)p->u.array.count;- if (unlikely(idx >= len))- goto slow_path;- switch(p->class_id) {- case JS_CLASS_ARRAY:- case JS_CLASS_ARGUMENTS:- return JS_DupValue(ctx, p->u.array.u.values[idx]);- case JS_CLASS_INT8_ARRAY:- return JS_NewInt32(ctx, p->u.array.u.int8_ptr[idx]);- case JS_CLASS_UINT8C_ARRAY:- case JS_CLASS_UINT8_ARRAY:- return JS_NewInt32(ctx, p->u.array.u.uint8_ptr[idx]);- case JS_CLASS_INT16_ARRAY:- return JS_NewInt32(ctx, p->u.array.u.int16_ptr[idx]);- case JS_CLASS_UINT16_ARRAY:- return JS_NewInt32(ctx, p->u.array.u.uint16_ptr[idx]);- case JS_CLASS_INT32_ARRAY:- return JS_NewInt32(ctx, p->u.array.u.int32_ptr[idx]);- case JS_CLASS_UINT32_ARRAY:- return JS_NewUint32(ctx, p->u.array.u.uint32_ptr[idx]);-#ifdef CONFIG_BIGNUM- case JS_CLASS_BIG_INT64_ARRAY:- return JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]);- case JS_CLASS_BIG_UINT64_ARRAY:- return JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]);-#endif- case JS_CLASS_FLOAT32_ARRAY:- return __JS_NewFloat64(ctx, p->u.array.u.float_ptr[idx]);- case JS_CLASS_FLOAT64_ARRAY:- return __JS_NewFloat64(ctx, p->u.array.u.double_ptr[idx]);- default:- goto slow_path;- }- } else {- slow_path:- atom = JS_ValueToAtom(ctx, prop);- JS_FreeValue(ctx, prop);- if (unlikely(atom == JS_ATOM_NULL))- return JS_EXCEPTION;- ret = JS_GetProperty(ctx, this_obj, atom);- JS_FreeAtom(ctx, atom);- return ret;- }-}--JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj,- uint32_t idx)-{- return JS_GetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx));-}--/* Check if an object has a generalized numeric property. Return value:- -1 for exception,- TRUE if property exists, stored into *pval,- FALSE if proprty does not exist.- */-static int JS_TryGetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, JSValue *pval)-{- JSValue val = JS_UNDEFINED;- JSAtom prop;- int present;-- if (likely((uint64_t)idx <= JS_ATOM_MAX_INT)) {- /* fast path */- present = JS_HasProperty(ctx, obj, __JS_AtomFromUInt32(idx));- if (present > 0) {- val = JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx));- if (unlikely(JS_IsException(val)))- present = -1;- }- } else {- prop = JS_NewAtomInt64(ctx, idx);- present = -1;- if (likely(prop != JS_ATOM_NULL)) {- present = JS_HasProperty(ctx, obj, prop);- if (present > 0) {- val = JS_GetProperty(ctx, obj, prop);- if (unlikely(JS_IsException(val)))- present = -1;- }- JS_FreeAtom(ctx, prop);- }- }- *pval = val;- return present;-}--static JSValue JS_GetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx)-{- JSAtom prop;- JSValue val;-- if ((uint64_t)idx <= INT32_MAX) {- /* fast path for fast arrays */- return JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx));- }- prop = JS_NewAtomInt64(ctx, idx);- if (prop == JS_ATOM_NULL)- return JS_EXCEPTION;-- val = JS_GetProperty(ctx, obj, prop);- JS_FreeAtom(ctx, prop);- return val;-}--JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj,- const char *prop)-{- JSAtom atom;- JSValue ret;- atom = JS_NewAtom(ctx, prop);- ret = JS_GetProperty(ctx, this_obj, atom);- JS_FreeAtom(ctx, atom);- return ret;-}--/* Note: the property value is not initialized. Return NULL if memory- error. */-static JSProperty *add_property(JSContext *ctx,- JSObject *p, JSAtom prop, int prop_flags)-{- JSShape *sh, *new_sh;-- sh = p->shape;- if (sh->is_hashed) {- /* try to find an existing shape */- new_sh = find_hashed_shape_prop(ctx->rt, sh, prop, prop_flags);- if (new_sh) {- /* matching shape found: use it */- /* the property array may need to be resized */- if (new_sh->prop_size != sh->prop_size) {- JSProperty *new_prop;- new_prop = js_realloc(ctx, p->prop, sizeof(p->prop[0]) *- new_sh->prop_size);- if (!new_prop)- return NULL;- p->prop = new_prop;- }- p->shape = js_dup_shape(new_sh);- js_free_shape(ctx->rt, sh);- return &p->prop[new_sh->prop_count - 1];- } else if (sh->header.ref_count != 1) {- /* if the shape is shared, clone it */- new_sh = js_clone_shape(ctx, sh);- if (!new_sh)- return NULL;- /* hash the cloned shape */- new_sh->is_hashed = TRUE;- js_shape_hash_link(ctx->rt, new_sh);- js_free_shape(ctx->rt, p->shape);- p->shape = new_sh;- }- }- assert(p->shape->header.ref_count == 1);- if (add_shape_property(ctx, &p->shape, p, prop, prop_flags))- return NULL;- return &p->prop[p->shape->prop_count - 1];-}--/* can be called on Array or Arguments objects. return < 0 if- memory alloc error. */-static no_inline __exception int convert_fast_array_to_array(JSContext *ctx,- JSObject *p)-{- JSProperty *pr;- JSShape *sh;- JSValue *tab;- uint32_t i, len, new_count;-- if (js_shape_prepare_update(ctx, p, NULL))- return -1;- len = p->u.array.count;- /* resize the properties once to simplify the error handling */- sh = p->shape;- new_count = sh->prop_count + len;- if (new_count > sh->prop_size) {- if (resize_properties(ctx, &p->shape, p, new_count))- return -1;- }-- tab = p->u.array.u.values;- for(i = 0; i < len; i++) {- /* add_property cannot fail here but- __JS_AtomFromUInt32(i) fails for i > INT32_MAX */- pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E);- pr->u.value = *tab++;- }- js_free(ctx, p->u.array.u.values);- p->u.array.count = 0;- p->u.array.u.values = NULL; /* fail safe */- p->u.array.u1.size = 0;- p->fast_array = 0;- return 0;-}--static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom)-{- JSShape *sh;- JSShapeProperty *pr, *lpr, *prop;- JSProperty *pr1;- uint32_t lpr_idx;- intptr_t h, h1;-- redo:- sh = p->shape;- h1 = atom & sh->prop_hash_mask;- h = prop_hash_end(sh)[-h1 - 1];- prop = get_shape_prop(sh);- lpr = NULL;- lpr_idx = 0; /* prevent warning */- while (h != 0) {- pr = &prop[h - 1];- if (likely(pr->atom == atom)) {- /* found ! */- if (!(pr->flags & JS_PROP_CONFIGURABLE))- return FALSE;- /* realloc the shape if needed */- if (lpr)- lpr_idx = lpr - get_shape_prop(sh);- if (js_shape_prepare_update(ctx, p, &pr))- return -1;- sh = p->shape;- /* remove property */- if (lpr) {- lpr = get_shape_prop(sh) + lpr_idx;- lpr->hash_next = pr->hash_next;- } else {- prop_hash_end(sh)[-h1 - 1] = pr->hash_next;- }- sh->deleted_prop_count++;- /* free the entry */- pr1 = &p->prop[h - 1];- free_property(ctx->rt, pr1, pr->flags);- JS_FreeAtom(ctx, pr->atom);- /* put default values */- pr->flags = 0;- pr->atom = JS_ATOM_NULL;- pr1->u.value = JS_UNDEFINED;-- /* compact the properties if too many deleted properties */- if (sh->deleted_prop_count >= 8 &&- sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) {- compact_properties(ctx, p);- }- return TRUE;- }- lpr = pr;- h = pr->hash_next;- }-- if (p->is_exotic) {- if (p->fast_array) {- uint32_t idx;- if (JS_AtomIsArrayIndex(ctx, &idx, atom) &&- idx < p->u.array.count) {- if (p->class_id == JS_CLASS_ARRAY ||- p->class_id == JS_CLASS_ARGUMENTS) {- /* Special case deleting the last element of a fast Array */- if (idx == p->u.array.count - 1) {- JS_FreeValue(ctx, p->u.array.u.values[idx]);- p->u.array.count = idx;- return TRUE;- }- if (convert_fast_array_to_array(ctx, p))- return -1;- goto redo;- } else {- return FALSE; /* not configurable */- }- }- } else {- const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;- if (em && em->delete_property) {- return em->delete_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), atom);- }- }- }- /* not found */- return TRUE;-}--static int call_setter(JSContext *ctx, JSObject *setter,- JSValueConst this_obj, JSValue val, int flags)-{- JSValue ret, func;- if (likely(setter)) {- func = JS_MKPTR(JS_TAG_OBJECT, setter);- /* Note: the field could be removed in the setter */- func = JS_DupValue(ctx, func);- ret = JS_CallFree(ctx, func, this_obj, 1, (JSValueConst *)&val);- JS_FreeValue(ctx, val);- if (JS_IsException(ret))- return -1;- JS_FreeValue(ctx, ret);- return TRUE;- } else {- JS_FreeValue(ctx, val);- if ((flags & JS_PROP_THROW) ||- ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {- JS_ThrowTypeError(ctx, "no setter for property");- return -1;- }- return FALSE;- }-}--/* set the array length and remove the array elements if necessary. */-static int set_array_length(JSContext *ctx, JSObject *p, JSValue val, int flags)-{- uint32_t len, idx, cur_len;- int i, ret;-- /* Note: this call can reallocate the properties of 'p' */- ret = JS_ToArrayLengthFree(ctx, &len, val);- if (ret)- return -1;-- if (likely(p->fast_array)) {- uint32_t old_len = p->u.array.count;- if (len < old_len) {- for(i = len; i < old_len; i++) {- JS_FreeValue(ctx, p->u.array.u.values[i]);- }- p->u.array.count = len;- }- p->prop[0].u.value = JS_NewUint32(ctx, len);- } else {- /* Note: length is always a uint32 because the object is an- array */- JS_ToUint32(ctx, &cur_len, p->prop[0].u.value);- if (len < cur_len) {- uint32_t d;- JSShape *sh;- JSShapeProperty *pr;-- d = cur_len - len;- sh = p->shape;- if (d <= sh->prop_count) {- JSAtom atom;-- /* faster to iterate */- while (cur_len > len) {- atom = JS_NewAtomUInt32(ctx, cur_len - 1);- ret = delete_property(ctx, p, atom);- JS_FreeAtom(ctx, atom);- if (unlikely(!ret)) {- /* unlikely case: property is not- configurable */- break;- }- cur_len--;- }- } else {- /* faster to iterate thru all the properties. Need two- passes in case one of the property is not- configurable */- cur_len = len;- for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count;- i++, pr++) {- if (pr->atom != JS_ATOM_NULL &&- JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) {- if (idx >= cur_len &&- !(pr->flags & JS_PROP_CONFIGURABLE)) {- cur_len = idx + 1;- }- }- }-- for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count;- i++, pr++) {- if (pr->atom != JS_ATOM_NULL &&- JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) {- if (idx >= cur_len) {- /* remove the property */- delete_property(ctx, p, pr->atom);- /* WARNING: the shape may have been modified */- sh = p->shape;- pr = get_shape_prop(sh) + i;- }- }- }- }- } else {- cur_len = len;- }- set_value(ctx, &p->prop[0].u.value, JS_NewUint32(ctx, cur_len));- if (unlikely(cur_len > len)) {- return JS_ThrowTypeErrorOrFalse(ctx, flags, "not configurable");- }- }- return TRUE;-}--/* Preconditions: 'p' must be of class JS_CLASS_ARRAY, p->fast_array =- TRUE and p->extensible = TRUE */-static int add_fast_array_element(JSContext *ctx, JSObject *p,- JSValue val, int flags)-{- uint32_t new_len, array_len;- /* extend the array by one */- /* XXX: convert to slow array if new_len > 2^31-1 elements */- new_len = p->u.array.count + 1;- /* update the length if necessary. We assume that if the length is- not an integer, then if it >= 2^31. */- if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT)) {- array_len = JS_VALUE_GET_INT(p->prop[0].u.value);- if (new_len > array_len) {- if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) {- JS_FreeValue(ctx, val);- return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);- }- p->prop[0].u.value = JS_NewInt32(ctx, new_len);- }- }- if (unlikely(new_len > p->u.array.u1.size)) {- uint32_t new_size;- size_t slack;- JSValue *new_array_prop;- /* XXX: potential arithmetic overflow */- new_size = max_int(new_len, p->u.array.u1.size * 3 / 2);- new_array_prop = js_realloc2(ctx, p->u.array.u.values, sizeof(JSValue) * new_size, &slack);- if (!new_array_prop) {- JS_FreeValue(ctx, val);- return -1;- }- new_size += slack / sizeof(*new_array_prop);- p->u.array.u.values = new_array_prop;- p->u.array.u1.size = new_size;- }- p->u.array.u.values[new_len - 1] = val;- p->u.array.count = new_len;- return TRUE;-}--static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc)-{- JS_FreeValue(ctx, desc->getter);- JS_FreeValue(ctx, desc->setter);- JS_FreeValue(ctx, desc->value);-}--/* generic (and slower) version of JS_SetProperty() for Reflect.set() */-static int JS_SetPropertyGeneric(JSContext *ctx,- JSObject *p, JSAtom prop,- JSValue val, JSValueConst this_obj,- int flags)-{- int ret;- JSPropertyDescriptor desc;-- while (p != NULL) {- if (p->is_exotic) {- const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;- if (em && em->set_property) {- JSValue obj1;- /* set_property can free the prototype */- obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));- ret = em->set_property(ctx, obj1, prop,- val, this_obj, flags);- JS_FreeValue(ctx, obj1);- JS_FreeValue(ctx, val);- return ret;- }- }-- ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);- if (ret < 0)- return ret;- if (ret) {- if (desc.flags & JS_PROP_GETSET) {- JSObject *setter;- if (JS_IsUndefined(desc.setter))- setter = NULL;- else- setter = JS_VALUE_GET_OBJ(desc.setter);- ret = call_setter(ctx, setter, this_obj, val, flags);- JS_FreeValue(ctx, desc.getter);- JS_FreeValue(ctx, desc.setter);- return ret;- } else {- JS_FreeValue(ctx, desc.value);- if (!(desc.flags & JS_PROP_WRITABLE)) {- goto read_only_error;- }- }- break;- }- p = p->shape->proto;- }-- if (!JS_IsObject(this_obj))- return JS_ThrowTypeErrorOrFalse(ctx, flags, "receiver is not an object");-- p = JS_VALUE_GET_OBJ(this_obj);-- /* modify the property in this_obj if it already exists */- ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);- if (ret < 0)- return ret;- if (ret) {- if (desc.flags & JS_PROP_GETSET) {- JS_FreeValue(ctx, desc.getter);- JS_FreeValue(ctx, desc.setter);- JS_FreeValue(ctx, val);- return JS_ThrowTypeErrorOrFalse(ctx, flags, "setter is forbidden");- } else {- JS_FreeValue(ctx, desc.value);- if (!(desc.flags & JS_PROP_WRITABLE) ||- p->class_id == JS_CLASS_MODULE_NS) {- read_only_error:- JS_FreeValue(ctx, val);- return JS_ThrowTypeErrorReadOnly(ctx, flags, prop);- }- }- ret = JS_DefineProperty(ctx, this_obj, prop, val,- JS_UNDEFINED, JS_UNDEFINED,- JS_PROP_HAS_VALUE);- JS_FreeValue(ctx, val);- return ret;- }-- ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED,- flags |- JS_PROP_HAS_VALUE |- JS_PROP_HAS_ENUMERABLE |- JS_PROP_HAS_WRITABLE |- JS_PROP_HAS_CONFIGURABLE |- JS_PROP_C_W_E);- JS_FreeValue(ctx, val);- return ret;-}--/* return -1 in case of exception or TRUE or FALSE. Warning: 'val' is- freed by the function. 'flags' is a bitmask of JS_PROP_NO_ADD,- JS_PROP_THROW or JS_PROP_THROW_STRICT. If JS_PROP_NO_ADD is set,- the new property is not added and an error is raised. */-int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj,- JSAtom prop, JSValue val, int flags)-{- JSObject *p, *p1;- JSShapeProperty *prs;- JSProperty *pr;- uint32_t tag;- JSPropertyDescriptor desc;- int ret;-#if 0- printf("JS_SetPropertyInternal: "); print_atom(ctx, prop); printf("\n");-#endif- tag = JS_VALUE_GET_TAG(this_obj);- if (unlikely(tag != JS_TAG_OBJECT)) {- switch(tag) {- case JS_TAG_NULL:- JS_FreeValue(ctx, val);- JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of null", prop);- return -1;- case JS_TAG_UNDEFINED:- JS_FreeValue(ctx, val);- JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of undefined", prop);- return -1;- default:- /* even on a primitive type we can have setters on the prototype */- p = NULL;- p1 = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, this_obj));- goto prototype_lookup;- }- }- p = JS_VALUE_GET_OBJ(this_obj);-retry:- prs = find_own_property(&pr, p, prop);- if (prs) {- if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE |- JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) {- /* fast case */- set_value(ctx, &pr->u.value, val);- return TRUE;- } else if ((prs->flags & (JS_PROP_LENGTH | JS_PROP_WRITABLE)) ==- (JS_PROP_LENGTH | JS_PROP_WRITABLE)) {- assert(p->class_id == JS_CLASS_ARRAY);- assert(prop == JS_ATOM_length);- return set_array_length(ctx, p, val, flags);- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {- return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags);- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {- /* JS_PROP_WRITABLE is always true for variable- references, but they are write protected in module name- spaces. */- if (p->class_id == JS_CLASS_MODULE_NS)- goto read_only_prop;- set_value(ctx, pr->u.var_ref->pvalue, val);- return TRUE;- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {- /* Instantiate property and retry (potentially useless) */- if (JS_AutoInitProperty(ctx, p, prop, pr)) {- JS_FreeValue(ctx, val);- return -1;- }- goto retry;- } else {- goto read_only_prop;- }- }-- p1 = p;- for(;;) {- if (p1->is_exotic) {- if (p1->fast_array) {- if (__JS_AtomIsTaggedInt(prop)) {- uint32_t idx = __JS_AtomToUInt32(prop);- if (idx < p1->u.array.count) {- if (unlikely(p == p1))- return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, flags);- else- break;- } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY &&- p1->class_id <= JS_CLASS_FLOAT64_ARRAY) {- goto typed_array_oob;- }- } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY &&- p1->class_id <= JS_CLASS_FLOAT64_ARRAY) {- ret = JS_AtomIsNumericIndex(ctx, prop);- if (ret != 0) {- if (ret < 0) {- JS_FreeValue(ctx, val);- return -1;- }- typed_array_oob:- val = JS_ToNumberFree(ctx, val);- JS_FreeValue(ctx, val);- if (JS_IsException(val))- return -1;- if (typed_array_is_detached(ctx, p1)) {- JS_ThrowTypeErrorDetachedArrayBuffer(ctx);- return -1;- }- return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index");- }- }- } else {- const JSClassExoticMethods *em = ctx->rt->class_array[p1->class_id].exotic;- if (em) {- JSValue obj1;- if (em->set_property) {- /* set_property can free the prototype */- obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));- ret = em->set_property(ctx, obj1, prop,- val, this_obj, flags);- JS_FreeValue(ctx, obj1);- JS_FreeValue(ctx, val);- return ret;- }- if (em->get_own_property) {- /* get_own_property can free the prototype */- obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));- ret = em->get_own_property(ctx, &desc,- obj1, prop);- JS_FreeValue(ctx, obj1);- if (ret < 0) {- JS_FreeValue(ctx, val);- return ret;- }- if (ret) {- if (desc.flags & JS_PROP_GETSET) {- JSObject *setter;- if (JS_IsUndefined(desc.setter))- setter = NULL;- else- setter = JS_VALUE_GET_OBJ(desc.setter);- ret = call_setter(ctx, setter, this_obj, val, flags);- JS_FreeValue(ctx, desc.getter);- JS_FreeValue(ctx, desc.setter);- return ret;- } else {- JS_FreeValue(ctx, desc.value);- if (!(desc.flags & JS_PROP_WRITABLE))- goto read_only_prop;- if (likely(p == p1)) {- ret = JS_DefineProperty(ctx, this_obj, prop, val,- JS_UNDEFINED, JS_UNDEFINED,- JS_PROP_HAS_VALUE);- JS_FreeValue(ctx, val);- return ret;- } else {- break;- }- }- }- }- }- }- }- p1 = p1->shape->proto;- prototype_lookup:- if (!p1)- break;-- retry2:- prs = find_own_property(&pr, p1, prop);- if (prs) {- if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {- return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags);- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {- /* Instantiate property and retry (potentially useless) */- if (JS_AutoInitProperty(ctx, p1, prop, pr))- return -1;- goto retry2;- } else if (!(prs->flags & JS_PROP_WRITABLE)) {- read_only_prop:- JS_FreeValue(ctx, val);- return JS_ThrowTypeErrorReadOnly(ctx, flags, prop);- }- }- }-- if (unlikely(flags & JS_PROP_NO_ADD)) {- JS_FreeValue(ctx, val);- JS_ThrowReferenceErrorNotDefined(ctx, prop);- return -1;- }-- if (unlikely(!p)) {- JS_FreeValue(ctx, val);- return JS_ThrowTypeErrorOrFalse(ctx, flags, "not an object");- }-- if (unlikely(!p->extensible)) {- JS_FreeValue(ctx, val);- return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible");- }-- if (p->is_exotic) {- if (p->class_id == JS_CLASS_ARRAY && p->fast_array &&- __JS_AtomIsTaggedInt(prop)) {- uint32_t idx = __JS_AtomToUInt32(prop);- if (idx == p->u.array.count) {- /* fast case */- return add_fast_array_element(ctx, p, val, flags);- } else {- goto generic_create_prop;- }- } else {- generic_create_prop:- ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED,- flags |- JS_PROP_HAS_VALUE |- JS_PROP_HAS_ENUMERABLE |- JS_PROP_HAS_WRITABLE |- JS_PROP_HAS_CONFIGURABLE |- JS_PROP_C_W_E);- JS_FreeValue(ctx, val);- return ret;- }- }-- pr = add_property(ctx, p, prop, JS_PROP_C_W_E);- if (unlikely(!pr)) {- JS_FreeValue(ctx, val);- return -1;- }- pr->u.value = val;- return TRUE;-}--/* flags can be JS_PROP_THROW or JS_PROP_THROW_STRICT */-static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,- JSValue prop, JSValue val, int flags)-{- if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT &&- JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) {- JSObject *p;- uint32_t idx;- double d;- int32_t v;-- /* fast path for array access */- p = JS_VALUE_GET_OBJ(this_obj);- idx = JS_VALUE_GET_INT(prop);- switch(p->class_id) {- case JS_CLASS_ARRAY:- if (unlikely(idx >= (uint32_t)p->u.array.count)) {- JSObject *p1;- JSShape *sh1;-- /* fast path to add an element to the array */- if (idx != (uint32_t)p->u.array.count ||- !p->fast_array || !p->extensible)- goto slow_path;- /* check if prototype chain has a numeric property */- p1 = p->shape->proto;- while (p1 != NULL) {- sh1 = p1->shape;- if (p1->class_id == JS_CLASS_ARRAY) {- if (unlikely(!p1->fast_array))- goto slow_path;- } else if (p1->class_id == JS_CLASS_OBJECT) {- if (unlikely(sh1->has_small_array_index))- goto slow_path;- } else {- goto slow_path;- }- p1 = sh1->proto;- }- /* add element */- return add_fast_array_element(ctx, p, val, flags);- }- set_value(ctx, &p->u.array.u.values[idx], val);- break;- case JS_CLASS_ARGUMENTS:- if (unlikely(idx >= (uint32_t)p->u.array.count))- goto slow_path;- set_value(ctx, &p->u.array.u.values[idx], val);- break;- case JS_CLASS_UINT8C_ARRAY:- if (JS_ToUint8ClampFree(ctx, &v, val))- return -1;- /* Note: the conversion can detach the typed array, so the- array bound check must be done after */- if (unlikely(idx >= (uint32_t)p->u.array.count))- goto ta_out_of_bound;- p->u.array.u.uint8_ptr[idx] = v;- break;- case JS_CLASS_INT8_ARRAY:- case JS_CLASS_UINT8_ARRAY:- if (JS_ToInt32Free(ctx, &v, val))- return -1;- if (unlikely(idx >= (uint32_t)p->u.array.count))- goto ta_out_of_bound;- p->u.array.u.uint8_ptr[idx] = v;- break;- case JS_CLASS_INT16_ARRAY:- case JS_CLASS_UINT16_ARRAY:- if (JS_ToInt32Free(ctx, &v, val))- return -1;- if (unlikely(idx >= (uint32_t)p->u.array.count))- goto ta_out_of_bound;- p->u.array.u.uint16_ptr[idx] = v;- break;- case JS_CLASS_INT32_ARRAY:- case JS_CLASS_UINT32_ARRAY:- if (JS_ToInt32Free(ctx, &v, val))- return -1;- if (unlikely(idx >= (uint32_t)p->u.array.count))- goto ta_out_of_bound;- p->u.array.u.uint32_ptr[idx] = v;- break;-#ifdef CONFIG_BIGNUM- case JS_CLASS_BIG_INT64_ARRAY:- case JS_CLASS_BIG_UINT64_ARRAY:- /* XXX: need specific conversion function */- {- int64_t v;- if (JS_ToBigInt64Free(ctx, &v, val))- return -1;- if (unlikely(idx >= (uint32_t)p->u.array.count))- goto ta_out_of_bound;- p->u.array.u.uint64_ptr[idx] = v;- }- break;-#endif- case JS_CLASS_FLOAT32_ARRAY:- if (JS_ToFloat64Free(ctx, &d, val))- return -1;- if (unlikely(idx >= (uint32_t)p->u.array.count))- goto ta_out_of_bound;- p->u.array.u.float_ptr[idx] = d;- break;- case JS_CLASS_FLOAT64_ARRAY:- if (JS_ToFloat64Free(ctx, &d, val))- return -1;- if (unlikely(idx >= (uint32_t)p->u.array.count)) {- ta_out_of_bound:- if (typed_array_is_detached(ctx, p)) {- JS_ThrowTypeErrorDetachedArrayBuffer(ctx);- return -1;- } else {- return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index");- }- }- p->u.array.u.double_ptr[idx] = d;- break;- default:- goto slow_path;- }- return TRUE;- } else {- JSAtom atom;- int ret;- slow_path:- atom = JS_ValueToAtom(ctx, prop);- JS_FreeValue(ctx, prop);- if (unlikely(atom == JS_ATOM_NULL)) {- JS_FreeValue(ctx, val);- return -1;- }- ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, flags);- JS_FreeAtom(ctx, atom);- return ret;- }-}--int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj,- uint32_t idx, JSValue val)-{- return JS_SetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx), val,- JS_PROP_THROW);-}--int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj,- int64_t idx, JSValue val)-{- JSAtom prop;- int res;-- if ((uint64_t)idx <= INT32_MAX) {- /* fast path for fast arrays */- return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val,- JS_PROP_THROW);- }- prop = JS_NewAtomInt64(ctx, idx);- if (prop == JS_ATOM_NULL) {- JS_FreeValue(ctx, val);- return -1;- }- res = JS_SetProperty(ctx, this_obj, prop, val);- JS_FreeAtom(ctx, prop);- return res;-}--int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj,- const char *prop, JSValue val)-{- JSAtom atom;- int ret;- atom = JS_NewAtom(ctx, prop);- ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, JS_PROP_THROW);- JS_FreeAtom(ctx, atom);- return ret;-}--/* compute the property flags. For each flag: (JS_PROP_HAS_x forces- it, otherwise def_flags is used)- Note: makes assumption about the bit pattern of the flags-*/-static int get_prop_flags(int flags, int def_flags)-{- int mask;- mask = (flags >> JS_PROP_HAS_SHIFT) & JS_PROP_C_W_E;- return (flags & mask) | (def_flags & ~mask);-}--static int JS_CreateProperty(JSContext *ctx, JSObject *p,- JSAtom prop, JSValueConst val,- JSValueConst getter, JSValueConst setter,- int flags)-{- JSProperty *pr;- int ret, prop_flags;-- /* add a new property or modify an existing exotic one */- if (p->is_exotic) {- if (p->class_id == JS_CLASS_ARRAY) {- uint32_t idx, len;-- if (p->fast_array) {- if (__JS_AtomIsTaggedInt(prop)) {- idx = __JS_AtomToUInt32(prop);- if (idx == p->u.array.count) {- if (!p->extensible)- goto not_extensible;- if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET))- goto convert_to_array;- prop_flags = get_prop_flags(flags, 0);- if (prop_flags != JS_PROP_C_W_E)- goto convert_to_array;- return add_fast_array_element(ctx, p,- JS_DupValue(ctx, val), flags);- } else {- goto convert_to_array;- }- } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) {- /* convert the fast array to normal array */- convert_to_array:- if (convert_fast_array_to_array(ctx, p))- return -1;- goto generic_array;- }- } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) {- JSProperty *plen;- JSShapeProperty *pslen;- generic_array:- /* update the length field */- plen = &p->prop[0];- JS_ToUint32(ctx, &len, plen->u.value);- if ((idx + 1) > len) {- pslen = get_shape_prop(p->shape);- if (unlikely(!(pslen->flags & JS_PROP_WRITABLE)))- return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);- /* XXX: should update the length after defining- the property */- len = idx + 1;- set_value(ctx, &plen->u.value, JS_NewUint32(ctx, len));- }- }- } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&- p->class_id <= JS_CLASS_FLOAT64_ARRAY) {- ret = JS_AtomIsNumericIndex(ctx, prop);- if (ret != 0) {- if (ret < 0)- return -1;- return JS_ThrowTypeErrorOrFalse(ctx, flags, "cannot create numeric index in typed array");- }- } else if (!(flags & JS_PROP_NO_EXOTIC)) {- const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;- if (em) {- if (em->define_own_property) {- return em->define_own_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p),- prop, val, getter, setter, flags);- }- ret = JS_IsExtensible(ctx, JS_MKPTR(JS_TAG_OBJECT, p));- if (ret < 0)- return -1;- if (!ret)- goto not_extensible;- }- }- }-- if (!p->extensible) {- not_extensible:- return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible");- }-- if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {- prop_flags = (flags & (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) |- JS_PROP_GETSET;- } else {- prop_flags = flags & JS_PROP_C_W_E;- }- pr = add_property(ctx, p, prop, prop_flags);- if (unlikely(!pr))- return -1;- if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {- pr->u.getset.getter = NULL;- if ((flags & JS_PROP_HAS_GET) && JS_IsFunction(ctx, getter)) {- pr->u.getset.getter =- JS_VALUE_GET_OBJ(JS_DupValue(ctx, getter));- }- pr->u.getset.setter = NULL;- if ((flags & JS_PROP_HAS_SET) && JS_IsFunction(ctx, setter)) {- pr->u.getset.setter =- JS_VALUE_GET_OBJ(JS_DupValue(ctx, setter));- }- } else {- if (flags & JS_PROP_HAS_VALUE) {- pr->u.value = JS_DupValue(ctx, val);- } else {- pr->u.value = JS_UNDEFINED;- }- }- return TRUE;-}--/* return FALSE if not OK */-static BOOL check_define_prop_flags(int prop_flags, int flags)-{- BOOL has_accessor, is_getset;-- if (!(prop_flags & JS_PROP_CONFIGURABLE)) {- if ((flags & (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) ==- (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) {- return FALSE;- }- if ((flags & JS_PROP_HAS_ENUMERABLE) &&- (flags & JS_PROP_ENUMERABLE) != (prop_flags & JS_PROP_ENUMERABLE))- return FALSE;- }- if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE |- JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {- if (!(prop_flags & JS_PROP_CONFIGURABLE)) {- has_accessor = ((flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) != 0);- is_getset = ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET);- if (has_accessor != is_getset)- return FALSE;- if (!has_accessor && !is_getset && !(prop_flags & JS_PROP_WRITABLE)) {- /* not writable: cannot set the writable bit */- if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) ==- (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE))- return FALSE;- }- }- }- return TRUE;-}--/* ensure that the shape can be safely modified */-static int js_shape_prepare_update(JSContext *ctx, JSObject *p,- JSShapeProperty **pprs)-{- JSShape *sh;- uint32_t idx = 0; /* prevent warning */-- sh = p->shape;- if (sh->is_hashed) {- if (sh->header.ref_count != 1) {- if (pprs)- idx = *pprs - get_shape_prop(sh);- /* clone the shape (the resulting one is no longer hashed) */- sh = js_clone_shape(ctx, sh);- if (!sh)- return -1;- js_free_shape(ctx->rt, p->shape);- p->shape = sh;- if (pprs)- *pprs = get_shape_prop(sh) + idx;- } else {- js_shape_hash_unlink(ctx->rt, sh);- sh->is_hashed = FALSE;- }- }- return 0;-}--static int js_update_property_flags(JSContext *ctx, JSObject *p,- JSShapeProperty **pprs, int flags)-{- if (flags != (*pprs)->flags) {- if (js_shape_prepare_update(ctx, p, pprs))- return -1;- (*pprs)->flags = flags;- }- return 0;-}--/* allowed flags:- JS_PROP_CONFIGURABLE, JS_PROP_WRITABLE, JS_PROP_ENUMERABLE- JS_PROP_HAS_GET, JS_PROP_HAS_SET, JS_PROP_HAS_VALUE,- JS_PROP_HAS_CONFIGURABLE, JS_PROP_HAS_WRITABLE, JS_PROP_HAS_ENUMERABLE,- JS_PROP_THROW, JS_PROP_NO_EXOTIC.- If JS_PROP_THROW is set, return an exception instead of FALSE.- if JS_PROP_NO_EXOTIC is set, do not call the exotic- define_own_property callback.- return -1 (exception), FALSE or TRUE.-*/-int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,- JSAtom prop, JSValueConst val,- JSValueConst getter, JSValueConst setter, int flags)-{- JSObject *p;- JSShapeProperty *prs;- JSProperty *pr;- int mask, res;-- if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) {- JS_ThrowTypeErrorNotAnObject(ctx);- return -1;- }- p = JS_VALUE_GET_OBJ(this_obj);-- redo_prop_update:- prs = find_own_property(&pr, p, prop);- if (prs) {- /* property already exists */- if (!check_define_prop_flags(prs->flags, flags)) {- not_configurable:- return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable");- }-- retry:- if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE |- JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {- if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {- JSObject *new_getter, *new_setter;-- if (JS_IsFunction(ctx, getter)) {- new_getter = JS_VALUE_GET_OBJ(getter);- } else {- new_getter = NULL;- }- if (JS_IsFunction(ctx, setter)) {- new_setter = JS_VALUE_GET_OBJ(setter);- } else {- new_setter = NULL;- }-- if ((prs->flags & JS_PROP_TMASK) != JS_PROP_GETSET) {- if (js_shape_prepare_update(ctx, p, &prs))- return -1;- /* convert to getset */- if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {- free_var_ref(ctx->rt, pr->u.var_ref);- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {- /* clear property and update */- if (js_shape_prepare_update(ctx, p, &prs))- return -1;- js_autoinit_free(ctx->rt, pr);- prs->flags &= ~JS_PROP_TMASK;- pr->u.value = JS_UNDEFINED;- goto retry;- } else {- JS_FreeValue(ctx, pr->u.value);- }- prs->flags = (prs->flags &- (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) |- JS_PROP_GETSET;- pr->u.getset.getter = NULL;- pr->u.getset.setter = NULL;- } else {- if (!(prs->flags & JS_PROP_CONFIGURABLE)) {- if ((flags & JS_PROP_HAS_GET) &&- new_getter != pr->u.getset.getter) {- goto not_configurable;- }- if ((flags & JS_PROP_HAS_SET) &&- new_setter != pr->u.getset.setter) {- goto not_configurable;- }- }- }- if (flags & JS_PROP_HAS_GET) {- if (pr->u.getset.getter)- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));- if (new_getter)- JS_DupValue(ctx, getter);- pr->u.getset.getter = new_getter;- }- if (flags & JS_PROP_HAS_SET) {- if (pr->u.getset.setter)- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));- if (new_setter)- JS_DupValue(ctx, setter);- pr->u.getset.setter = new_setter;- }- } else {- if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {- /* convert to data descriptor */- if (js_shape_prepare_update(ctx, p, &prs))- return -1;- if (pr->u.getset.getter)- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));- if (pr->u.getset.setter)- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));- prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE);- pr->u.value = JS_UNDEFINED;- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {- /* Note: JS_PROP_VARREF is always writable */- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {- /* clear property and update */- if (js_shape_prepare_update(ctx, p, &prs))- return -1;- js_autoinit_free(ctx->rt, pr);- prs->flags &= ~JS_PROP_TMASK;- pr->u.value = JS_UNDEFINED;- } else {- if ((prs->flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 &&- (flags & JS_PROP_HAS_VALUE) &&- !js_same_value(ctx, val, pr->u.value)) {- goto not_configurable;- }- }- if (prs->flags & JS_PROP_LENGTH) {- if (flags & JS_PROP_HAS_VALUE) {- res = set_array_length(ctx, p, JS_DupValue(ctx, val),- flags);- } else {- res = TRUE;- }- /* still need to reset the writable flag if needed.- The JS_PROP_LENGTH is reset to have the correct- read-only behavior in JS_SetProperty(). */- if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) ==- JS_PROP_HAS_WRITABLE) {- prs = get_shape_prop(p->shape);- if (js_update_property_flags(ctx, p, &prs,- prs->flags & ~(JS_PROP_WRITABLE | JS_PROP_LENGTH)))- return -1;- }- return res;- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {- if (flags & JS_PROP_HAS_VALUE) {- if (p->class_id == JS_CLASS_MODULE_NS) {- /* JS_PROP_WRITABLE is always true for variable- references, but they are write protected in module name- spaces. */- if (!js_same_value(ctx, val, *pr->u.var_ref->pvalue))- goto not_configurable;- }- /* update the reference */- set_value(ctx, pr->u.var_ref->pvalue,- JS_DupValue(ctx, val));- }- /* if writable is set to false, no longer a- reference (for mapped arguments) */- if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) {- JSValue val1;- if (js_shape_prepare_update(ctx, p, &prs))- return -1;- val1 = JS_DupValue(ctx, *pr->u.var_ref->pvalue);- free_var_ref(ctx->rt, pr->u.var_ref);- pr->u.value = val1;- prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE);- }- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {- /* XXX: should never happen, type was reset above */- abort();- } else {- if (flags & JS_PROP_HAS_VALUE) {- JS_FreeValue(ctx, pr->u.value);- pr->u.value = JS_DupValue(ctx, val);- }- if (flags & JS_PROP_HAS_WRITABLE) {- if (js_update_property_flags(ctx, p, &prs,- (prs->flags & ~JS_PROP_WRITABLE) |- (flags & JS_PROP_WRITABLE)))- return -1;- }- }- }- }- mask = 0;- if (flags & JS_PROP_HAS_CONFIGURABLE)- mask |= JS_PROP_CONFIGURABLE;- if (flags & JS_PROP_HAS_ENUMERABLE)- mask |= JS_PROP_ENUMERABLE;- if (js_update_property_flags(ctx, p, &prs,- (prs->flags & ~mask) | (flags & mask)))- return -1;- return TRUE;- }-- /* handle modification of fast array elements */- if (p->fast_array) {- uint32_t idx;- uint32_t prop_flags;- if (p->class_id == JS_CLASS_ARRAY) {- if (__JS_AtomIsTaggedInt(prop)) {- idx = __JS_AtomToUInt32(prop);- if (idx < p->u.array.count) {- prop_flags = get_prop_flags(flags, JS_PROP_C_W_E);- if (prop_flags != JS_PROP_C_W_E)- goto convert_to_slow_array;- if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {- convert_to_slow_array:- if (convert_fast_array_to_array(ctx, p))- return -1;- else- goto redo_prop_update;- }- if (flags & JS_PROP_HAS_VALUE) {- set_value(ctx, &p->u.array.u.values[idx], JS_DupValue(ctx, val));- }- return TRUE;- }- }- } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&- p->class_id <= JS_CLASS_FLOAT64_ARRAY) {- JSValue num;- int ret;-- if (!__JS_AtomIsTaggedInt(prop)) {- /* slow path with to handle all numeric indexes */- num = JS_AtomIsNumericIndex1(ctx, prop);- if (JS_IsUndefined(num))- goto typed_array_done;- if (JS_IsException(num))- return -1;- ret = JS_NumberIsInteger(ctx, num);- if (ret < 0) {- JS_FreeValue(ctx, num);- return -1;- }- if (!ret) {- JS_FreeValue(ctx, num);- return JS_ThrowTypeErrorOrFalse(ctx, flags, "non integer index in typed array");- }- ret = JS_NumberIsNegativeOrMinusZero(ctx, num);- JS_FreeValue(ctx, num);- if (ret) {- return JS_ThrowTypeErrorOrFalse(ctx, flags, "negative index in typed array");- }- if (!__JS_AtomIsTaggedInt(prop))- goto typed_array_oob;- }- idx = __JS_AtomToUInt32(prop);- /* if the typed array is detached, p->u.array.count = 0 */- if (idx >= typed_array_get_length(ctx, p)) {- typed_array_oob:- return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound index in typed array");- }- prop_flags = get_prop_flags(flags, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE);- if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET) ||- prop_flags != (JS_PROP_ENUMERABLE | JS_PROP_WRITABLE)) {- return JS_ThrowTypeErrorOrFalse(ctx, flags, "invalid descriptor flags");- }- if (flags & JS_PROP_HAS_VALUE) {- return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), JS_DupValue(ctx, val), flags);- }- return TRUE;- typed_array_done: ;- }- }-- return JS_CreateProperty(ctx, p, prop, val, getter, setter, flags);-}--static int JS_DefineAutoInitProperty(JSContext *ctx, JSValueConst this_obj,- JSAtom prop, JSAutoInitIDEnum id,- void *opaque, int flags)-{- JSObject *p;- JSProperty *pr;-- if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT)- return FALSE;-- p = JS_VALUE_GET_OBJ(this_obj);-- if (find_own_property(&pr, p, prop)) {- /* property already exists */- abort();- return FALSE;- }-- /* Specialized CreateProperty */- pr = add_property(ctx, p, prop, (flags & JS_PROP_C_W_E) | JS_PROP_AUTOINIT);- if (unlikely(!pr))- return -1;- pr->u.init.realm_and_id = (uintptr_t)JS_DupContext(ctx);- assert((pr->u.init.realm_and_id & 3) == 0);- assert(id <= 3);- pr->u.init.realm_and_id |= id;- pr->u.init.opaque = opaque;- return TRUE;-}--/* shortcut to add or redefine a new property value */-int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj,- JSAtom prop, JSValue val, int flags)-{- int ret;- ret = JS_DefineProperty(ctx, this_obj, prop, val, JS_UNDEFINED, JS_UNDEFINED,- flags | JS_PROP_HAS_VALUE | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE);- JS_FreeValue(ctx, val);- return ret;-}--int JS_DefinePropertyValueValue(JSContext *ctx, JSValueConst this_obj,- JSValue prop, JSValue val, int flags)-{- JSAtom atom;- int ret;- atom = JS_ValueToAtom(ctx, prop);- JS_FreeValue(ctx, prop);- if (unlikely(atom == JS_ATOM_NULL)) {- JS_FreeValue(ctx, val);- return -1;- }- ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags);- JS_FreeAtom(ctx, atom);- return ret;-}--int JS_DefinePropertyValueUint32(JSContext *ctx, JSValueConst this_obj,- uint32_t idx, JSValue val, int flags)-{- return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewUint32(ctx, idx),- val, flags);-}--int JS_DefinePropertyValueInt64(JSContext *ctx, JSValueConst this_obj,- int64_t idx, JSValue val, int flags)-{- return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx),- val, flags);-}--int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj,- const char *prop, JSValue val, int flags)-{- JSAtom atom;- int ret;- atom = JS_NewAtom(ctx, prop);- ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags);- JS_FreeAtom(ctx, atom);- return ret;-}--/* shortcut to add getter & setter */-int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj,- JSAtom prop, JSValue getter, JSValue setter,- int flags)-{- int ret;- ret = JS_DefineProperty(ctx, this_obj, prop, JS_UNDEFINED, getter, setter,- flags | JS_PROP_HAS_GET | JS_PROP_HAS_SET |- JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE);- JS_FreeValue(ctx, getter);- JS_FreeValue(ctx, setter);- return ret;-}--static int JS_CreateDataPropertyUint32(JSContext *ctx, JSValueConst this_obj,- int64_t idx, JSValue val, int flags)-{- return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx),- val, flags | JS_PROP_CONFIGURABLE |- JS_PROP_ENUMERABLE | JS_PROP_WRITABLE);-}---/* return TRUE if 'obj' has a non empty 'name' string */-static BOOL js_object_has_name(JSContext *ctx, JSValueConst obj)-{- JSProperty *pr;- JSShapeProperty *prs;- JSValueConst val;- JSString *p;- - prs = find_own_property(&pr, JS_VALUE_GET_OBJ(obj), JS_ATOM_name);- if (!prs)- return FALSE;- if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL)- return TRUE;- val = pr->u.value;- if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)- return TRUE;- p = JS_VALUE_GET_STRING(val);- return (p->len != 0);-}--static int JS_DefineObjectName(JSContext *ctx, JSValueConst obj,- JSAtom name, int flags)-{- if (name != JS_ATOM_NULL- && JS_IsObject(obj)- && !js_object_has_name(ctx, obj)- && JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, JS_AtomToString(ctx, name), flags) < 0) {- return -1;- }- return 0;-}--static int JS_DefineObjectNameComputed(JSContext *ctx, JSValueConst obj,- JSValueConst str, int flags)-{- if (JS_IsObject(obj) &&- !js_object_has_name(ctx, obj)) {- JSAtom prop;- JSValue name_str;- prop = JS_ValueToAtom(ctx, str);- if (prop == JS_ATOM_NULL)- return -1;- name_str = js_get_function_name(ctx, prop);- JS_FreeAtom(ctx, prop);- if (JS_IsException(name_str))- return -1;- if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, name_str, flags) < 0)- return -1;- }- return 0;-}--#define DEFINE_GLOBAL_LEX_VAR (1 << 7)-#define DEFINE_GLOBAL_FUNC_VAR (1 << 6)--static JSValue JS_ThrowSyntaxErrorVarRedeclaration(JSContext *ctx, JSAtom prop)-{- return JS_ThrowSyntaxErrorAtom(ctx, "redeclaration of '%s'", prop);-}--/* flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */-/* XXX: could support exotic global object. */-static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags)-{- JSObject *p;- JSShapeProperty *prs;-- p = JS_VALUE_GET_OBJ(ctx->global_obj);- prs = find_own_property1(p, prop);- /* XXX: should handle JS_PROP_AUTOINIT */- if (flags & DEFINE_GLOBAL_LEX_VAR) {- if (prs && !(prs->flags & JS_PROP_CONFIGURABLE))- goto fail_redeclaration;- } else {- if (!prs && !p->extensible)- goto define_error;- if (flags & DEFINE_GLOBAL_FUNC_VAR) {- if (prs) {- if (!(prs->flags & JS_PROP_CONFIGURABLE) &&- ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET ||- ((prs->flags & (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)) !=- (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)))) {- define_error:- JS_ThrowTypeErrorAtom(ctx, "cannot define variable '%s'",- prop);- return -1;- }- }- }- }- /* check if there already is a lexical declaration */- p = JS_VALUE_GET_OBJ(ctx->global_var_obj);- prs = find_own_property1(p, prop);- if (prs) {- fail_redeclaration:- JS_ThrowSyntaxErrorVarRedeclaration(ctx, prop);- return -1;- }- return 0;-}--/* def_flags is (0, DEFINE_GLOBAL_LEX_VAR) |- JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE */-/* XXX: could support exotic global object. */-static int JS_DefineGlobalVar(JSContext *ctx, JSAtom prop, int def_flags)-{- JSObject *p;- JSShapeProperty *prs;- JSProperty *pr;- JSValue val;- int flags;-- if (def_flags & DEFINE_GLOBAL_LEX_VAR) {- p = JS_VALUE_GET_OBJ(ctx->global_var_obj);- flags = JS_PROP_ENUMERABLE | (def_flags & JS_PROP_WRITABLE) |- JS_PROP_CONFIGURABLE;- val = JS_UNINITIALIZED;- } else {- p = JS_VALUE_GET_OBJ(ctx->global_obj);- flags = JS_PROP_ENUMERABLE | JS_PROP_WRITABLE |- (def_flags & JS_PROP_CONFIGURABLE);- val = JS_UNDEFINED;- }- prs = find_own_property1(p, prop);- if (prs)- return 0;- if (!p->extensible)- return 0;- pr = add_property(ctx, p, prop, flags);- if (unlikely(!pr))- return -1;- pr->u.value = val;- return 0;-}--/* 'def_flags' is 0 or JS_PROP_CONFIGURABLE. */-/* XXX: could support exotic global object. */-static int JS_DefineGlobalFunction(JSContext *ctx, JSAtom prop,- JSValueConst func, int def_flags)-{-- JSObject *p;- JSShapeProperty *prs;- int flags;-- p = JS_VALUE_GET_OBJ(ctx->global_obj);- prs = find_own_property1(p, prop);- flags = JS_PROP_HAS_VALUE | JS_PROP_THROW;- if (!prs || (prs->flags & JS_PROP_CONFIGURABLE)) {- flags |= JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | def_flags |- JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE;- }- if (JS_DefineProperty(ctx, ctx->global_obj, prop, func,- JS_UNDEFINED, JS_UNDEFINED, flags) < 0)- return -1;- return 0;-}--static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop,- BOOL throw_ref_error)-{- JSObject *p;- JSShapeProperty *prs;- JSProperty *pr;-- /* no exotic behavior is possible in global_var_obj */- p = JS_VALUE_GET_OBJ(ctx->global_var_obj);- prs = find_own_property(&pr, p, prop);- if (prs) {- /* XXX: should handle JS_PROP_TMASK properties */- if (unlikely(JS_IsUninitialized(pr->u.value)))- return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);- return JS_DupValue(ctx, pr->u.value);- }- return JS_GetPropertyInternal(ctx, ctx->global_obj, prop,- ctx->global_obj, throw_ref_error);-}--/* construct a reference to a global variable */-static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom prop, JSValue *sp)-{- JSObject *p;- JSShapeProperty *prs;- JSProperty *pr;-- /* no exotic behavior is possible in global_var_obj */- p = JS_VALUE_GET_OBJ(ctx->global_var_obj);- prs = find_own_property(&pr, p, prop);- if (prs) {- /* XXX: should handle JS_PROP_AUTOINIT properties? */- /* XXX: conformance: do these tests in- OP_put_var_ref/OP_get_var_ref ? */- if (unlikely(JS_IsUninitialized(pr->u.value))) {- JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);- return -1;- }- if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) {- return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop);- }- sp[0] = JS_DupValue(ctx, ctx->global_var_obj);- } else {- int ret;- ret = JS_HasProperty(ctx, ctx->global_obj, prop);- if (ret < 0)- return -1;- if (ret) {- sp[0] = JS_DupValue(ctx, ctx->global_obj);- } else {- sp[0] = JS_UNDEFINED;- }- }- sp[1] = JS_AtomToValue(ctx, prop);- return 0;-}--/* use for strict variable access: test if the variable exists */-static int JS_CheckGlobalVar(JSContext *ctx, JSAtom prop)-{- JSObject *p;- JSShapeProperty *prs;- int ret;-- /* no exotic behavior is possible in global_var_obj */- p = JS_VALUE_GET_OBJ(ctx->global_var_obj);- prs = find_own_property1(p, prop);- if (prs) {- ret = TRUE;- } else {- ret = JS_HasProperty(ctx, ctx->global_obj, prop);- if (ret < 0)- return -1;- }- return ret;-}--/* flag = 0: normal variable write- flag = 1: initialize lexical variable- flag = 2: normal variable write, strict check was done before-*/-static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val,- int flag)-{- JSObject *p;- JSShapeProperty *prs;- JSProperty *pr;- int flags;-- /* no exotic behavior is possible in global_var_obj */- p = JS_VALUE_GET_OBJ(ctx->global_var_obj);- prs = find_own_property(&pr, p, prop);- if (prs) {- /* XXX: should handle JS_PROP_AUTOINIT properties? */- if (flag != 1) {- if (unlikely(JS_IsUninitialized(pr->u.value))) {- JS_FreeValue(ctx, val);- JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);- return -1;- }- if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) {- JS_FreeValue(ctx, val);- return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop);- }- }- set_value(ctx, &pr->u.value, val);- return 0;- }-- flags = JS_PROP_THROW_STRICT;- if (flag != 2 && is_strict_mode(ctx))- flags |= JS_PROP_NO_ADD;- return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, flags);-}--/* return -1, FALSE or TRUE. return FALSE if not configurable or- invalid object. return -1 in case of exception.- flags can be 0, JS_PROP_THROW or JS_PROP_THROW_STRICT */-int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags)-{- JSValue obj1;- JSObject *p;- int res;- - obj1 = JS_ToObject(ctx, obj);- if (JS_IsException(obj1))- return -1;- p = JS_VALUE_GET_OBJ(obj1);- res = delete_property(ctx, p, prop);- JS_FreeValue(ctx, obj1);- if (res != FALSE)- return res;- if ((flags & JS_PROP_THROW) ||- ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {- JS_ThrowTypeError(ctx, "could not delete property");- return -1;- }- return FALSE;-}--int JS_DeletePropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, int flags)-{- JSAtom prop;- int res;-- if ((uint64_t)idx <= JS_ATOM_MAX_INT) {- /* fast path for fast arrays */- return JS_DeleteProperty(ctx, obj, __JS_AtomFromUInt32(idx), flags);- }- prop = JS_NewAtomInt64(ctx, idx);- if (prop == JS_ATOM_NULL)- return -1;- res = JS_DeleteProperty(ctx, obj, prop, flags);- JS_FreeAtom(ctx, prop);- return res;-}--BOOL JS_IsFunction(JSContext *ctx, JSValueConst val)-{- JSObject *p;- if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)- return FALSE;- p = JS_VALUE_GET_OBJ(val);- switch(p->class_id) {- case JS_CLASS_BYTECODE_FUNCTION:- return TRUE;- case JS_CLASS_PROXY:- return p->u.proxy_data->is_func;- default:- return (ctx->rt->class_array[p->class_id].call != NULL);- }-}--BOOL JS_IsCFunction(JSContext *ctx, JSValueConst val, JSCFunction *func, int magic)-{- JSObject *p;- if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)- return FALSE;- p = JS_VALUE_GET_OBJ(val);- if (p->class_id == JS_CLASS_C_FUNCTION)- return (p->u.cfunc.c_function.generic == func && p->u.cfunc.magic == magic);- else- return FALSE;-}--BOOL JS_IsConstructor(JSContext *ctx, JSValueConst val)-{- JSObject *p;- if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)- return FALSE;- p = JS_VALUE_GET_OBJ(val);- return p->is_constructor;-}--BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, BOOL val)-{- JSObject *p;- if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)- return FALSE;- p = JS_VALUE_GET_OBJ(func_obj);- p->is_constructor = val;- return TRUE;-}--BOOL JS_IsError(JSContext *ctx, JSValueConst val)-{- JSObject *p;- if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)- return FALSE;- p = JS_VALUE_GET_OBJ(val);- return (p->class_id == JS_CLASS_ERROR);-}--/* used to avoid catching interrupt exceptions */-BOOL JS_IsUncatchableError(JSContext *ctx, JSValueConst val)-{- JSObject *p;- if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)- return FALSE;- p = JS_VALUE_GET_OBJ(val);- return p->class_id == JS_CLASS_ERROR && p->is_uncatchable_error;-}--void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag)-{- JSObject *p;- if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)- return;- p = JS_VALUE_GET_OBJ(val);- if (p->class_id == JS_CLASS_ERROR)- p->is_uncatchable_error = flag;-}--void JS_ResetUncatchableError(JSContext *ctx)-{- JS_SetUncatchableError(ctx, ctx->rt->current_exception, FALSE);-}--void JS_SetOpaque(JSValue obj, void *opaque)-{- JSObject *p;- if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {- p = JS_VALUE_GET_OBJ(obj);- p->u.opaque = opaque;- }-}--/* return NULL if not an object of class class_id */-void *JS_GetOpaque(JSValueConst obj, JSClassID class_id)-{- JSObject *p;- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)- return NULL;- p = JS_VALUE_GET_OBJ(obj);- if (p->class_id != class_id)- return NULL;- return p->u.opaque;-}--void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id)-{- void *p = JS_GetOpaque(obj, class_id);- if (unlikely(!p)) {- JS_ThrowTypeErrorInvalidClass(ctx, class_id);- }- return p;-}--#define HINT_STRING 0-#define HINT_NUMBER 1-#define HINT_NONE 2-/* don't try Symbol.toPrimitive */-#define HINT_FORCE_ORDINARY (1 << 4)--static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint)-{- int i;- BOOL force_ordinary;-- JSAtom method_name;- JSValue method, ret;- if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)- return val;- force_ordinary = hint & HINT_FORCE_ORDINARY;- hint &= ~HINT_FORCE_ORDINARY;- if (!force_ordinary) {- method = JS_GetProperty(ctx, val, JS_ATOM_Symbol_toPrimitive);- if (JS_IsException(method))- goto exception;- /* ECMA says *If exoticToPrim is not undefined* but tests in- test262 use null as a non callable converter */- if (!JS_IsUndefined(method) && !JS_IsNull(method)) {- JSAtom atom;- JSValue arg;- switch(hint) {- case HINT_STRING:- atom = JS_ATOM_string;- break;- case HINT_NUMBER:- atom = JS_ATOM_number;- break;- default:- case HINT_NONE:- atom = JS_ATOM_default;- break;- }- arg = JS_AtomToString(ctx, atom);- ret = JS_CallFree(ctx, method, val, 1, (JSValueConst *)&arg);- JS_FreeValue(ctx, arg);- if (JS_IsException(ret))- goto exception;- JS_FreeValue(ctx, val);- if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT)- return ret;- JS_FreeValue(ctx, ret);- return JS_ThrowTypeError(ctx, "toPrimitive");- }- }- if (hint != HINT_STRING)- hint = HINT_NUMBER;- for(i = 0; i < 2; i++) {- if ((i ^ hint) == 0) {- method_name = JS_ATOM_toString;- } else {- method_name = JS_ATOM_valueOf;- }- method = JS_GetProperty(ctx, val, method_name);- if (JS_IsException(method))- goto exception;- if (JS_IsFunction(ctx, method)) {- ret = JS_CallFree(ctx, method, val, 0, NULL);- if (JS_IsException(ret))- goto exception;- if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {- JS_FreeValue(ctx, val);- return ret;- }- JS_FreeValue(ctx, ret);- } else {- JS_FreeValue(ctx, method);- }- }- JS_ThrowTypeError(ctx, "toPrimitive");-exception:- JS_FreeValue(ctx, val);- return JS_EXCEPTION;-}--static JSValue JS_ToPrimitive(JSContext *ctx, JSValueConst val, int hint)-{- return JS_ToPrimitiveFree(ctx, JS_DupValue(ctx, val), hint);-}--void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj)-{- JSObject *p;- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)- return;- p = JS_VALUE_GET_OBJ(obj);- p->is_HTMLDDA = TRUE;-}--static inline BOOL JS_IsHTMLDDA(JSContext *ctx, JSValueConst obj)-{- JSObject *p;- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)- return FALSE;- p = JS_VALUE_GET_OBJ(obj);- return p->is_HTMLDDA;-}- -static int JS_ToBoolFree(JSContext *ctx, JSValue val)-{- uint32_t tag = JS_VALUE_GET_TAG(val);- switch(tag) {- case JS_TAG_INT:- return JS_VALUE_GET_INT(val) != 0;- case JS_TAG_BOOL:- case JS_TAG_NULL:- case JS_TAG_UNDEFINED:- return JS_VALUE_GET_INT(val);- case JS_TAG_EXCEPTION:- return -1;- case JS_TAG_STRING:- {- BOOL ret = JS_VALUE_GET_STRING(val)->len != 0;- JS_FreeValue(ctx, val);- return ret;- }-#ifdef CONFIG_BIGNUM- case JS_TAG_BIG_INT:- case JS_TAG_BIG_FLOAT:- {- JSBigFloat *p = JS_VALUE_GET_PTR(val);- BOOL ret;- ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN;- JS_FreeValue(ctx, val);- return ret;- }- case JS_TAG_BIG_DECIMAL:- {- JSBigDecimal *p = JS_VALUE_GET_PTR(val);- BOOL ret;- ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN;- JS_FreeValue(ctx, val);- return ret;- }-#endif- case JS_TAG_OBJECT:- {- JSObject *p = JS_VALUE_GET_OBJ(val);- BOOL ret;- ret = !p->is_HTMLDDA;- JS_FreeValue(ctx, val);- return ret;- }- break;- default:- if (JS_TAG_IS_FLOAT64(tag)) {- double d = JS_VALUE_GET_FLOAT64(val);- return !isnan(d) && d != 0;- } else {- JS_FreeValue(ctx, val);- return TRUE;- }- }-}--int JS_ToBool(JSContext *ctx, JSValueConst val)-{- return JS_ToBoolFree(ctx, JS_DupValue(ctx, val));-}--static int skip_spaces(const char *pc)-{- const uint8_t *p, *p_next, *p_start;- uint32_t c;-- p = p_start = (const uint8_t *)pc;- for (;;) {- c = *p;- if (c < 128) {- if (!((c >= 0x09 && c <= 0x0d) || (c == 0x20)))- break;- p++;- } else {- c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);- if (!lre_is_space(c))- break;- p = p_next;- }- }- return p - p_start;-}--static inline int to_digit(int c)-{- if (c >= '0' && c <= '9')- return c - '0';- else if (c >= 'A' && c <= 'Z')- return c - 'A' + 10;- else if (c >= 'a' && c <= 'z')- return c - 'a' + 10;- else- return 36;-}--/* XXX: remove */-static double js_strtod(const char *p, int radix, BOOL is_float)-{- double d;- int c;- - if (!is_float || radix != 10) {- uint64_t n_max, n;- int int_exp, is_neg;- - is_neg = 0;- if (*p == '-') {- is_neg = 1;- p++;- }-- /* skip leading zeros */- while (*p == '0')- p++;- n = 0;- if (radix == 10)- n_max = ((uint64_t)-1 - 9) / 10; /* most common case */- else- n_max = ((uint64_t)-1 - (radix - 1)) / radix;- /* XXX: could be more precise */- int_exp = 0;- while (*p != '\0') {- c = to_digit((uint8_t)*p);- if (c >= radix)- break;- if (n <= n_max) {- n = n * radix + c;- } else {- int_exp++;- }- p++;- }- d = n;- if (int_exp != 0) {- d *= pow(radix, int_exp);- }- if (is_neg)- d = -d;- } else {- d = strtod(p, NULL);- }- return d;-}--#define ATOD_INT_ONLY (1 << 0)-/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */-#define ATOD_ACCEPT_BIN_OCT (1 << 2)-/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */-#define ATOD_ACCEPT_LEGACY_OCTAL (1 << 4)-/* accept _ between digits as a digit separator */-#define ATOD_ACCEPT_UNDERSCORES (1 << 5)-/* allow a suffix to override the type */-#define ATOD_ACCEPT_SUFFIX (1 << 6) -/* default type */-#define ATOD_TYPE_MASK (3 << 7)-#define ATOD_TYPE_FLOAT64 (0 << 7)-#define ATOD_TYPE_BIG_INT (1 << 7)-#define ATOD_TYPE_BIG_FLOAT (2 << 7)-#define ATOD_TYPE_BIG_DECIMAL (3 << 7)-/* assume bigint mode: floats are parsed as integers if no decimal- point nor exponent */-#define ATOD_MODE_BIGINT (1 << 9) -/* accept -0x1 */-#define ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 10)--#ifdef CONFIG_BIGNUM-static JSValue js_string_to_bigint(JSContext *ctx, const char *buf,- int radix, int flags, slimb_t *pexponent)-{- bf_t a_s, *a = &a_s;- int ret;- JSValue val;- val = JS_NewBigInt(ctx);- if (JS_IsException(val))- return val;- a = JS_GetBigInt(val);- ret = bf_atof(a, buf, NULL, radix, BF_PREC_INF, BF_RNDZ);- if (ret & BF_ST_MEM_ERROR) {- JS_FreeValue(ctx, val);- return JS_ThrowOutOfMemory(ctx);- }- val = JS_CompactBigInt1(ctx, val, (flags & ATOD_MODE_BIGINT) != 0);- return val;-}--static JSValue js_string_to_bigfloat(JSContext *ctx, const char *buf,- int radix, int flags, slimb_t *pexponent)-{- bf_t *a;- int ret;- JSValue val;- - val = JS_NewBigFloat(ctx);- if (JS_IsException(val))- return val;- a = JS_GetBigFloat(val);- if (flags & ATOD_ACCEPT_SUFFIX) {- /* return the exponent to get infinite precision */- ret = bf_atof2(a, pexponent, buf, NULL, radix, BF_PREC_INF,- BF_RNDZ | BF_ATOF_EXPONENT);- } else {- ret = bf_atof(a, buf, NULL, radix, ctx->fp_env.prec,- ctx->fp_env.flags);- }- if (ret & BF_ST_MEM_ERROR) {- JS_FreeValue(ctx, val);- return JS_ThrowOutOfMemory(ctx);- }- return val;-}--static JSValue js_string_to_bigdecimal(JSContext *ctx, const char *buf,- int radix, int flags, slimb_t *pexponent)-{- bfdec_t *a;- int ret;- JSValue val;- - val = JS_NewBigDecimal(ctx);- if (JS_IsException(val))- return val;- a = JS_GetBigDecimal(val);- ret = bfdec_atof(a, buf, NULL, BF_PREC_INF,- BF_RNDZ | BF_ATOF_NO_NAN_INF);- if (ret & BF_ST_MEM_ERROR) {- JS_FreeValue(ctx, val);- return JS_ThrowOutOfMemory(ctx);- }- return val;-}--#endif--/* return an exception in case of memory error. Return JS_NAN if- invalid syntax */-#ifdef CONFIG_BIGNUM-static JSValue js_atof2(JSContext *ctx, const char *str, const char **pp,- int radix, int flags, slimb_t *pexponent)-#else-static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,- int radix, int flags)-#endif-{- const char *p, *p_start;- int sep, is_neg;- BOOL is_float, has_legacy_octal;- int atod_type = flags & ATOD_TYPE_MASK;- char buf1[64], *buf;- int i, j, len;- BOOL buf_allocated = FALSE;- JSValue val;- - /* optional separator between digits */- sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256;- has_legacy_octal = FALSE;- - p = str;- p_start = p;- is_neg = 0;- if (p[0] == '+') {- p++;- p_start++;- if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))- goto no_radix_prefix;- } else if (p[0] == '-') {- p++;- p_start++;- is_neg = 1;- if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))- goto no_radix_prefix;- }- if (p[0] == '0') {- if ((p[1] == 'x' || p[1] == 'X') &&- (radix == 0 || radix == 16)) {- p += 2;- radix = 16;- } else if ((p[1] == 'o' || p[1] == 'O') &&- radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {- p += 2;- radix = 8;- } else if ((p[1] == 'b' || p[1] == 'B') &&- radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {- p += 2;- radix = 2;- } else if ((p[1] >= '0' && p[1] <= '9') &&- radix == 0 && (flags & ATOD_ACCEPT_LEGACY_OCTAL)) {- int i;- has_legacy_octal = TRUE;- sep = 256;- for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++)- continue;- if (p[i] == '8' || p[i] == '9')- goto no_prefix;- p += 1;- radix = 8;- } else {- goto no_prefix;- }- /* there must be a digit after the prefix */- if (to_digit((uint8_t)*p) >= radix)- goto fail;- no_prefix: ;- } else {- no_radix_prefix:- if (!(flags & ATOD_INT_ONLY) &&- (atod_type == ATOD_TYPE_FLOAT64 ||- atod_type == ATOD_TYPE_BIG_FLOAT) &&- strstart(p, "Infinity", &p)) {-#ifdef CONFIG_BIGNUM- if (atod_type == ATOD_TYPE_BIG_FLOAT) {- bf_t *a;- val = JS_NewBigFloat(ctx);- if (JS_IsException(val))- goto done;- a = JS_GetBigFloat(val);- bf_set_inf(a, is_neg);- } else-#endif- {- double d = 1.0 / 0.0;- if (is_neg)- d = -d;- val = JS_NewFloat64(ctx, d);- }- goto done;- }- }- if (radix == 0)- radix = 10;- is_float = FALSE;- p_start = p;- while (to_digit((uint8_t)*p) < radix- || (*p == sep && (radix != 10 ||- p != p_start + 1 || p[-1] != '0') &&- to_digit((uint8_t)p[1]) < radix)) {- p++;- }- if (!(flags & ATOD_INT_ONLY)) {- if (*p == '.' && (p > p_start || to_digit((uint8_t)p[1]) < radix)) {- is_float = TRUE;- p++;- if (*p == sep)- goto fail;- while (to_digit((uint8_t)*p) < radix ||- (*p == sep && to_digit((uint8_t)p[1]) < radix))- p++;- }- if (p > p_start &&- (((*p == 'e' || *p == 'E') && radix == 10) ||- ((*p == 'p' || *p == 'P') && (radix == 2 || radix == 8 || radix == 16)))) {- const char *p1 = p + 1;- is_float = TRUE;- if (*p1 == '+') {- p1++;- } else if (*p1 == '-') {- p1++;- }- if (is_digit((uint8_t)*p1)) {- p = p1 + 1;- while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1])))- p++;- }- }- }- if (p == p_start)- goto fail;-- buf = buf1;- buf_allocated = FALSE;- len = p - p_start;- if (unlikely((len + 2) > sizeof(buf1))) {- buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */- if (!buf)- goto mem_error;- buf_allocated = TRUE;- }- /* remove the separators and the radix prefixes */- j = 0;- if (is_neg)- buf[j++] = '-';- for (i = 0; i < len; i++) {- if (p_start[i] != '_')- buf[j++] = p_start[i];- }- buf[j] = '\0';--#ifdef CONFIG_BIGNUM- if (flags & ATOD_ACCEPT_SUFFIX) {- if (*p == 'n') {- p++;- atod_type = ATOD_TYPE_BIG_INT;- } else if (*p == 'l') {- p++;- atod_type = ATOD_TYPE_BIG_FLOAT;- } else if (*p == 'm') {- p++;- atod_type = ATOD_TYPE_BIG_DECIMAL;- } else {- if (flags & ATOD_MODE_BIGINT) {- if (!is_float)- atod_type = ATOD_TYPE_BIG_INT;- if (has_legacy_octal)- goto fail;- } else {- if (is_float && radix != 10)- goto fail;- }- }- } else {- if (atod_type == ATOD_TYPE_FLOAT64) {- if (flags & ATOD_MODE_BIGINT) {- if (!is_float)- atod_type = ATOD_TYPE_BIG_INT;- if (has_legacy_octal)- goto fail;- } else {- if (is_float && radix != 10)- goto fail;- }- }- }-- switch(atod_type) {- case ATOD_TYPE_FLOAT64:- {- double d;- d = js_strtod(buf, radix, is_float);- /* return int or float64 */- val = JS_NewFloat64(ctx, d);- }- break;- case ATOD_TYPE_BIG_INT:- if (has_legacy_octal || is_float)- goto fail;- val = ctx->rt->bigint_ops.from_string(ctx, buf, radix, flags, NULL);- break;- case ATOD_TYPE_BIG_FLOAT:- if (has_legacy_octal)- goto fail;- val = ctx->rt->bigfloat_ops.from_string(ctx, buf, radix, flags,- pexponent);- break;- case ATOD_TYPE_BIG_DECIMAL:- if (radix != 10)- goto fail;- val = ctx->rt->bigdecimal_ops.from_string(ctx, buf, radix, flags, NULL);- break;- default:- abort();- }-#else- {- double d;- (void)has_legacy_octal;- if (is_float && radix != 10)- goto fail;- d = js_strtod(buf, radix, is_float);- val = JS_NewFloat64(ctx, d);- }-#endif- -done:- if (buf_allocated)- js_free_rt(ctx->rt, buf);- if (pp)- *pp = p;- return val;- fail:- val = JS_NAN;- goto done;- mem_error:- val = JS_ThrowOutOfMemory(ctx);- goto done;-}--#ifdef CONFIG_BIGNUM-static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,- int radix, int flags)-{- return js_atof2(ctx, str, pp, radix, flags, NULL);-}-#endif--typedef enum JSToNumberHintEnum {- TON_FLAG_NUMBER,- TON_FLAG_NUMERIC,-} JSToNumberHintEnum;--static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val,- JSToNumberHintEnum flag)-{- uint32_t tag;- JSValue ret;-- redo:- tag = JS_VALUE_GET_NORM_TAG(val);- switch(tag) {-#ifdef CONFIG_BIGNUM- case JS_TAG_BIG_DECIMAL:- if (flag != TON_FLAG_NUMERIC) {- JS_FreeValue(ctx, val);- return JS_ThrowTypeError(ctx, "cannot convert bigdecimal to number");- }- ret = val;- break;- case JS_TAG_BIG_INT:- if (flag != TON_FLAG_NUMERIC) {- JS_FreeValue(ctx, val);- return JS_ThrowTypeError(ctx, "cannot convert bigint to number");- }- ret = val;- break;- case JS_TAG_BIG_FLOAT:- if (flag != TON_FLAG_NUMERIC) {- JS_FreeValue(ctx, val);- return JS_ThrowTypeError(ctx, "cannot convert bigfloat to number");- }- ret = val;- break;-#endif- case JS_TAG_FLOAT64:- case JS_TAG_INT:- case JS_TAG_EXCEPTION:- ret = val;- break;- case JS_TAG_BOOL:- case JS_TAG_NULL:- ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));- break;- case JS_TAG_UNDEFINED:- ret = JS_NAN;- break;- case JS_TAG_OBJECT:- val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);- if (JS_IsException(val))- return JS_EXCEPTION;- goto redo;- case JS_TAG_STRING:- {- const char *str;- const char *p;- size_t len;- - str = JS_ToCStringLen(ctx, &len, val);- JS_FreeValue(ctx, val);- if (!str)- return JS_EXCEPTION;- p = str;- p += skip_spaces(p);- if ((p - str) == len) {- ret = JS_NewInt32(ctx, 0);- } else {- int flags = ATOD_ACCEPT_BIN_OCT;- ret = js_atof(ctx, p, &p, 0, flags);- if (!JS_IsException(ret)) {- p += skip_spaces(p);- if ((p - str) != len) {- JS_FreeValue(ctx, ret);- ret = JS_NAN;- }- }- }- JS_FreeCString(ctx, str);- }- break;- case JS_TAG_SYMBOL:- JS_FreeValue(ctx, val);- return JS_ThrowTypeError(ctx, "cannot convert symbol to number");- default:- JS_FreeValue(ctx, val);- ret = JS_NAN;- break;- }- return ret;-}--static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val)-{- return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER);-}--static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val)-{- return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC);-}--static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val)-{- return JS_ToNumericFree(ctx, JS_DupValue(ctx, val));-}--static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres,- JSValue val)-{- double d;- uint32_t tag;-- val = JS_ToNumberFree(ctx, val);- if (JS_IsException(val)) {- *pres = JS_FLOAT64_NAN;- return -1;- }- tag = JS_VALUE_GET_NORM_TAG(val);- switch(tag) {- case JS_TAG_INT:- d = JS_VALUE_GET_INT(val);- break;- case JS_TAG_FLOAT64:- d = JS_VALUE_GET_FLOAT64(val);- break;-#ifdef CONFIG_BIGNUM- case JS_TAG_BIG_INT:- case JS_TAG_BIG_FLOAT:- {- JSBigFloat *p = JS_VALUE_GET_PTR(val);- /* XXX: there can be a double rounding issue with some- primitives (such as JS_ToUint8ClampFree()), but it is- not critical to fix it. */- bf_get_float64(&p->num, &d, BF_RNDN);- JS_FreeValue(ctx, val);- }- break;-#endif- default:- abort();- }- *pres = d;- return 0;-}--static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val)-{- uint32_t tag;-- tag = JS_VALUE_GET_TAG(val);- if (tag <= JS_TAG_NULL) {- *pres = JS_VALUE_GET_INT(val);- return 0;- } else if (JS_TAG_IS_FLOAT64(tag)) {- *pres = JS_VALUE_GET_FLOAT64(val);- return 0;- } else {- return __JS_ToFloat64Free(ctx, pres, val);- }-}--int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val)-{- return JS_ToFloat64Free(ctx, pres, JS_DupValue(ctx, val));-}--static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val)-{- return JS_ToNumberFree(ctx, JS_DupValue(ctx, val));-}--/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */-static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val)-{- uint32_t tag;- JSValue ret;-- redo:- tag = JS_VALUE_GET_NORM_TAG(val);- switch(tag) {- case JS_TAG_INT:- case JS_TAG_BOOL:- case JS_TAG_NULL:- case JS_TAG_UNDEFINED:- ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));- break;- case JS_TAG_FLOAT64:- {- double d = JS_VALUE_GET_FLOAT64(val);- if (isnan(d)) {- ret = JS_NewInt32(ctx, 0);- } else {- /* convert -0 to +0 */- d = trunc(d) + 0.0;- ret = JS_NewFloat64(ctx, d);- }- }- break;-#ifdef CONFIG_BIGNUM- case JS_TAG_BIG_FLOAT:- {- bf_t a_s, *a, r_s, *r = &r_s;- BOOL is_nan;-- a = JS_ToBigFloat(ctx, &a_s, val);- if (!bf_is_finite(a)) {- is_nan = bf_is_nan(a);- if (is_nan)- ret = JS_NewInt32(ctx, 0);- else- ret = JS_DupValue(ctx, val);- } else {- ret = JS_NewBigInt(ctx);- if (!JS_IsException(ret)) {- r = JS_GetBigInt(ret);- bf_set(r, a);- bf_rint(r, BF_RNDZ);- ret = JS_CompactBigInt(ctx, ret);- }- }- if (a == &a_s)- bf_delete(a);- JS_FreeValue(ctx, val);- }- break;-#endif- default:- val = JS_ToNumberFree(ctx, val);- if (JS_IsException(val))- return val;- goto redo;- }- return ret;-}--/* Note: the integer value is satured to 32 bits */-static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val)-{- uint32_t tag;- int ret;-- redo:- tag = JS_VALUE_GET_NORM_TAG(val);- switch(tag) {- case JS_TAG_INT:- case JS_TAG_BOOL:- case JS_TAG_NULL:- case JS_TAG_UNDEFINED:- ret = JS_VALUE_GET_INT(val);- break;- case JS_TAG_EXCEPTION:- *pres = 0;- return -1;- case JS_TAG_FLOAT64:- {- double d = JS_VALUE_GET_FLOAT64(val);- if (isnan(d)) {- ret = 0;- } else {- if (d < INT32_MIN)- ret = INT32_MIN;- else if (d > INT32_MAX)- ret = INT32_MAX;- else- ret = (int)d;- }- }- break;-#ifdef CONFIG_BIGNUM- case JS_TAG_BIG_FLOAT:- {- JSBigFloat *p = JS_VALUE_GET_PTR(val);- bf_get_int32(&ret, &p->num, 0);- JS_FreeValue(ctx, val);- }- break;-#endif- default:- val = JS_ToNumberFree(ctx, val);- if (JS_IsException(val)) {- *pres = 0;- return -1;- }- goto redo;- }- *pres = ret;- return 0;-}--int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValueConst val)-{- return JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));-}--int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValueConst val,- int min, int max, int min_offset)-{- int res = JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));- if (res == 0) {- if (*pres < min) {- *pres += min_offset;- if (*pres < min)- *pres = min;- } else {- if (*pres > max)- *pres = max;- }- }- return res;-}--static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val)-{- uint32_t tag;-- redo:- tag = JS_VALUE_GET_NORM_TAG(val);- switch(tag) {- case JS_TAG_INT:- case JS_TAG_BOOL:- case JS_TAG_NULL:- case JS_TAG_UNDEFINED:- *pres = JS_VALUE_GET_INT(val);- return 0;- case JS_TAG_EXCEPTION:- *pres = 0;- return -1;- case JS_TAG_FLOAT64:- {- double d = JS_VALUE_GET_FLOAT64(val);- if (isnan(d)) {- *pres = 0;- } else {- if (d < INT64_MIN)- *pres = INT64_MIN;- else if (d > INT64_MAX)- *pres = INT64_MAX;- else- *pres = (int64_t)d;- }- }- return 0;-#ifdef CONFIG_BIGNUM- case JS_TAG_BIG_FLOAT:- {- JSBigFloat *p = JS_VALUE_GET_PTR(val);- bf_get_int64(pres, &p->num, 0);- JS_FreeValue(ctx, val);- }- return 0;-#endif- default:- val = JS_ToNumberFree(ctx, val);- if (JS_IsException(val)) {- *pres = 0;- return -1;- }- goto redo;- }-}--int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValueConst val)-{- return JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));-}--int JS_ToInt64Clamp(JSContext *ctx, int64_t *pres, JSValueConst val,- int64_t min, int64_t max, int64_t neg_offset)-{- int res = JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));- if (res == 0) {- if (*pres < 0)- *pres += neg_offset;- if (*pres < min)- *pres = min;- else if (*pres > max)- *pres = max;- }- return res;-}--/* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0)- in case of exception */-static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val)-{- uint32_t tag;- int64_t ret;-- redo:- tag = JS_VALUE_GET_NORM_TAG(val);- switch(tag) {- case JS_TAG_INT:- case JS_TAG_BOOL:- case JS_TAG_NULL:- case JS_TAG_UNDEFINED:- ret = JS_VALUE_GET_INT(val);- break;- case JS_TAG_FLOAT64:- {- JSFloat64Union u;- double d;- int e;- d = JS_VALUE_GET_FLOAT64(val);- u.d = d;- /* we avoid doing fmod(x, 2^64) */- e = (u.u64 >> 52) & 0x7ff;- if (likely(e <= (1023 + 62))) {- /* fast case */- ret = (int64_t)d;- } else if (e <= (1023 + 62 + 53)) {- uint64_t v;- /* remainder modulo 2^64 */- v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);- ret = v << ((e - 1023) - 52);- /* take the sign into account */- if (u.u64 >> 63)- ret = -ret;- } else {- ret = 0; /* also handles NaN and +inf */- }- }- break;-#ifdef CONFIG_BIGNUM- case JS_TAG_BIG_FLOAT:- {- JSBigFloat *p = JS_VALUE_GET_PTR(val);- bf_get_int64(&ret, &p->num, BF_GET_INT_MOD);- JS_FreeValue(ctx, val);- }- break;-#endif- default:- val = JS_ToNumberFree(ctx, val);- if (JS_IsException(val)) {- *pres = 0;- return -1;- }- goto redo;- }- *pres = ret;- return 0;-}--int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val)-{- return JS_ToInt64Free(ctx, pres, JS_DupValue(ctx, val));-}--int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val)-{- if (JS_IsBigInt(ctx, val))- return JS_ToBigInt64(ctx, pres, val);- else- return JS_ToInt64(ctx, pres, val);-}--/* return (<0, 0) in case of exception */-static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val)-{- uint32_t tag;- int32_t ret;-- redo:- tag = JS_VALUE_GET_NORM_TAG(val);- switch(tag) {- case JS_TAG_INT:- case JS_TAG_BOOL:- case JS_TAG_NULL:- case JS_TAG_UNDEFINED:- ret = JS_VALUE_GET_INT(val);- break;- case JS_TAG_FLOAT64:- {- JSFloat64Union u;- double d;- int e;- d = JS_VALUE_GET_FLOAT64(val);- u.d = d;- /* we avoid doing fmod(x, 2^32) */- e = (u.u64 >> 52) & 0x7ff;- if (likely(e <= (1023 + 30))) {- /* fast case */- ret = (int32_t)d;- } else if (e <= (1023 + 30 + 53)) {- uint64_t v;- /* remainder modulo 2^32 */- v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);- v = v << ((e - 1023) - 52 + 32);- ret = v >> 32;- /* take the sign into account */- if (u.u64 >> 63)- ret = -ret;- } else {- ret = 0; /* also handles NaN and +inf */- }- }- break;-#ifdef CONFIG_BIGNUM- case JS_TAG_BIG_FLOAT:- {- JSBigFloat *p = JS_VALUE_GET_PTR(val);- bf_get_int32(&ret, &p->num, BF_GET_INT_MOD);- JS_FreeValue(ctx, val);- }- break;-#endif- default:- val = JS_ToNumberFree(ctx, val);- if (JS_IsException(val)) {- *pres = 0;- return -1;- }- goto redo;- }- *pres = ret;- return 0;-}--int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val)-{- return JS_ToInt32Free(ctx, pres, JS_DupValue(ctx, val));-}--static inline int JS_ToUint32Free(JSContext *ctx, uint32_t *pres, JSValue val)-{- return JS_ToInt32Free(ctx, (int32_t *)pres, val);-}--static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val)-{- uint32_t tag;- int res;-- redo:- tag = JS_VALUE_GET_NORM_TAG(val);- switch(tag) {- case JS_TAG_INT:- case JS_TAG_BOOL:- case JS_TAG_NULL:- case JS_TAG_UNDEFINED:- res = JS_VALUE_GET_INT(val);-#ifdef CONFIG_BIGNUM- int_clamp:-#endif- res = max_int(0, min_int(255, res));- break;- case JS_TAG_FLOAT64:- {- double d = JS_VALUE_GET_FLOAT64(val);- if (isnan(d)) {- res = 0;- } else {- if (d < 0)- res = 0;- else if (d > 255)- res = 255;- else- res = lrint(d);- }- }- break;-#ifdef CONFIG_BIGNUM- case JS_TAG_BIG_FLOAT:- {- JSBigFloat *p = JS_VALUE_GET_PTR(val);- bf_t r_s, *r = &r_s;- bf_init(ctx->bf_ctx, r);- bf_set(r, &p->num);- bf_rint(r, BF_RNDN);- bf_get_int32(&res, r, 0);- bf_delete(r);- JS_FreeValue(ctx, val);- }- goto int_clamp;-#endif- default:- val = JS_ToNumberFree(ctx, val);- if (JS_IsException(val)) {- *pres = 0;- return -1;- }- goto redo;- }- *pres = res;- return 0;-}--static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,- JSValue val)-{- uint32_t tag, len;-- redo:- tag = JS_VALUE_GET_TAG(val);- switch(tag) {- case JS_TAG_INT:- case JS_TAG_BOOL:- case JS_TAG_NULL:- {- int v;- v = JS_VALUE_GET_INT(val);- if (v < 0)- goto fail;- len = v;- }- break;-#ifdef CONFIG_BIGNUM- case JS_TAG_BIG_INT:- case JS_TAG_BIG_FLOAT:- {- JSBigFloat *p = JS_VALUE_GET_PTR(val);- bf_t a;- BOOL res;- bf_get_int32((int32_t *)&len, &p->num, BF_GET_INT_MOD);- bf_init(ctx->bf_ctx, &a);- bf_set_ui(&a, len);- res = bf_cmp_eq(&a, &p->num);- bf_delete(&a);- JS_FreeValue(ctx, val);- if (!res)- goto fail;- }- break;-#endif- default:- if (JS_TAG_IS_FLOAT64(tag)) {- double d;- d = JS_VALUE_GET_FLOAT64(val);- len = (uint32_t)d;- if (len != d) {- fail:- JS_ThrowRangeError(ctx, "invalid array length");- return -1;- }- } else {- val = JS_ToNumberFree(ctx, val);- if (JS_IsException(val))- return -1;- goto redo;- }- break;- }- *plen = len;- return 0;-}--#define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1)--static BOOL is_safe_integer(double d)-{- return isfinite(d) && floor(d) == d &&- fabs(d) <= (double)MAX_SAFE_INTEGER;-}--int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val)-{- int64_t v;- if (JS_ToInt64Sat(ctx, &v, val))- return -1;- if (v < 0 || v > MAX_SAFE_INTEGER) {- JS_ThrowRangeError(ctx, "invalid array index");- *plen = 0;- return -1;- }- *plen = v;- return 0;-}--/* convert a value to a length between 0 and MAX_SAFE_INTEGER.- return -1 for exception */-static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen,- JSValue val)-{- int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0);- JS_FreeValue(ctx, val);- return res;-}--/* Note: can return an exception */-static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val)-{- double d;- if (!JS_IsNumber(val))- return FALSE;- if (unlikely(JS_ToFloat64(ctx, &d, val)))- return -1;- return isfinite(d) && floor(d) == d;-}--static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val)-{- uint32_t tag;-- tag = JS_VALUE_GET_NORM_TAG(val);- switch(tag) {- case JS_TAG_INT:- {- int v;- v = JS_VALUE_GET_INT(val);- return (v < 0);- }- case JS_TAG_FLOAT64:- {- JSFloat64Union u;- u.d = JS_VALUE_GET_FLOAT64(val);- return (u.u64 >> 63);- }-#ifdef CONFIG_BIGNUM- case JS_TAG_BIG_INT:- {- JSBigFloat *p = JS_VALUE_GET_PTR(val);- /* Note: integer zeros are not necessarily positive */- return p->num.sign && !bf_is_zero(&p->num);- }- case JS_TAG_BIG_FLOAT:- {- JSBigFloat *p = JS_VALUE_GET_PTR(val);- return p->num.sign;- }- break;- case JS_TAG_BIG_DECIMAL:- {- JSBigDecimal *p = JS_VALUE_GET_PTR(val);- return p->num.sign;- }- break;-#endif- default:- return FALSE;- }-}--#ifdef CONFIG_BIGNUM--static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix)-{- JSValue ret;- bf_t a_s, *a;- char *str;- int saved_sign;-- a = JS_ToBigInt(ctx, &a_s, val);- if (!a)- return JS_EXCEPTION;- saved_sign = a->sign;- if (a->expn == BF_EXP_ZERO)- a->sign = 0;- str = bf_ftoa(NULL, a, radix, 0, BF_RNDZ | BF_FTOA_FORMAT_FRAC |- BF_FTOA_JS_QUIRKS);- a->sign = saved_sign;- JS_FreeBigInt(ctx, a, &a_s);- if (!str)- return JS_ThrowOutOfMemory(ctx);- ret = JS_NewString(ctx, str);- bf_free(ctx->bf_ctx, str);- return ret;-}--static JSValue js_bigint_to_string(JSContext *ctx, JSValueConst val)-{- return js_bigint_to_string1(ctx, val, 10);-}--static JSValue js_ftoa(JSContext *ctx, JSValueConst val1, int radix,- limb_t prec, bf_flags_t flags)-{- JSValue val, ret;- bf_t a_s, *a;- char *str;- int saved_sign;-- val = JS_ToNumeric(ctx, val1);- if (JS_IsException(val))- return val;- a = JS_ToBigFloat(ctx, &a_s, val);- saved_sign = a->sign;- if (a->expn == BF_EXP_ZERO)- a->sign = 0;- flags |= BF_FTOA_JS_QUIRKS;- if ((flags & BF_FTOA_FORMAT_MASK) == BF_FTOA_FORMAT_FREE_MIN) {- /* Note: for floating point numbers with a radix which is not- a power of two, the current precision is used to compute- the number of digits. */- if ((radix & (radix - 1)) != 0) {- bf_t r_s, *r = &r_s;- int prec, flags1;- /* must round first */- if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {- prec = ctx->fp_env.prec;- flags1 = ctx->fp_env.flags &- (BF_FLAG_SUBNORMAL | (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT));- } else {- prec = 53;- flags1 = bf_set_exp_bits(11) | BF_FLAG_SUBNORMAL;- }- bf_init(ctx->bf_ctx, r);- bf_set(r, a);- bf_round(r, prec, flags1 | BF_RNDN);- str = bf_ftoa(NULL, r, radix, prec, flags1 | flags);- bf_delete(r);- } else {- str = bf_ftoa(NULL, a, radix, BF_PREC_INF, flags);- }- } else {- str = bf_ftoa(NULL, a, radix, prec, flags);- }- a->sign = saved_sign;- if (a == &a_s)- bf_delete(a);- JS_FreeValue(ctx, val);- if (!str)- return JS_ThrowOutOfMemory(ctx);- ret = JS_NewString(ctx, str);- bf_free(ctx->bf_ctx, str);- return ret;-}--static JSValue js_bigfloat_to_string(JSContext *ctx, JSValueConst val)-{- return js_ftoa(ctx, val, 10, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN);-}--static JSValue js_bigdecimal_to_string1(JSContext *ctx, JSValueConst val,- limb_t prec, int flags)-{- JSValue ret;- bfdec_t *a;- char *str;- int saved_sign;-- a = JS_ToBigDecimal(ctx, val);- saved_sign = a->sign;- if (a->expn == BF_EXP_ZERO)- a->sign = 0;- str = bfdec_ftoa(NULL, a, prec, flags | BF_FTOA_JS_QUIRKS);- a->sign = saved_sign;- if (!str)- return JS_ThrowOutOfMemory(ctx);- ret = JS_NewString(ctx, str);- bf_free(ctx->bf_ctx, str);- return ret;-}--static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val)-{- return js_bigdecimal_to_string1(ctx, val, 0,- BF_RNDZ | BF_FTOA_FORMAT_FREE);-}--#endif /* CONFIG_BIGNUM */--/* 2 <= base <= 36 */-static char *i64toa(char *buf_end, int64_t n, unsigned int base)-{- char *q = buf_end;- int digit, is_neg;-- is_neg = 0;- if (n < 0) {- is_neg = 1;- n = -n;- }- *--q = '\0';- do {- digit = (uint64_t)n % base;- n = (uint64_t)n / base;- if (digit < 10)- digit += '0';- else- digit += 'a' - 10;- *--q = digit;- } while (n != 0);- if (is_neg)- *--q = '-';- return q;-}--/* buf1 contains the printf result */-static void js_ecvt1(double d, int n_digits, int *decpt, int *sign, char *buf,- int rounding_mode, char *buf1, int buf1_size)-{- if (rounding_mode != FE_TONEAREST)- fesetround(rounding_mode);- snprintf(buf1, buf1_size, "%+.*e", n_digits - 1, d);- if (rounding_mode != FE_TONEAREST)- fesetround(FE_TONEAREST);- *sign = (buf1[0] == '-');- /* mantissa */- buf[0] = buf1[1];- if (n_digits > 1)- memcpy(buf + 1, buf1 + 3, n_digits - 1);- buf[n_digits] = '\0';- /* exponent */- *decpt = atoi(buf1 + n_digits + 2 + (n_digits > 1)) + 1;-}--/* maximum buffer size for js_dtoa */-#define JS_DTOA_BUF_SIZE 128--/* needed because ecvt usually limits the number of digits to- 17. Return the number of digits. */-static int js_ecvt(double d, int n_digits, int *decpt, int *sign, char *buf,- BOOL is_fixed)-{- int rounding_mode;- char buf_tmp[JS_DTOA_BUF_SIZE];-- if (!is_fixed) {- unsigned int n_digits_min, n_digits_max;- /* find the minimum amount of digits (XXX: inefficient but simple) */- n_digits_min = 1;- n_digits_max = 17;- while (n_digits_min < n_digits_max) {- n_digits = (n_digits_min + n_digits_max) / 2;- js_ecvt1(d, n_digits, decpt, sign, buf, FE_TONEAREST,- buf_tmp, sizeof(buf_tmp));- if (strtod(buf_tmp, NULL) == d) {- /* no need to keep the trailing zeros */- while (n_digits >= 2 && buf[n_digits - 1] == '0')- n_digits--;- n_digits_max = n_digits;- } else {- n_digits_min = n_digits + 1;- }- }- n_digits = n_digits_max;- rounding_mode = FE_TONEAREST;- } else {- rounding_mode = FE_TONEAREST;-#ifdef CONFIG_PRINTF_RNDN- {- char buf1[JS_DTOA_BUF_SIZE], buf2[JS_DTOA_BUF_SIZE];- int decpt1, sign1, decpt2, sign2;- /* The JS rounding is specified as round to nearest ties away- from zero (RNDNA), but in printf the "ties" case is not- specified (for example it is RNDN for glibc, RNDNA for- Windows), so we must round manually. */- js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_TONEAREST,- buf_tmp, sizeof(buf_tmp));- /* XXX: could use 2 digits to reduce the average running time */- if (buf1[n_digits] == '5') {- js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_DOWNWARD,- buf_tmp, sizeof(buf_tmp));- js_ecvt1(d, n_digits + 1, &decpt2, &sign2, buf2, FE_UPWARD,- buf_tmp, sizeof(buf_tmp));- if (memcmp(buf1, buf2, n_digits + 1) == 0 && decpt1 == decpt2) {- /* exact result: round away from zero */- if (sign1)- rounding_mode = FE_DOWNWARD;- else- rounding_mode = FE_UPWARD;- }- }- }-#endif /* CONFIG_PRINTF_RNDN */- }- js_ecvt1(d, n_digits, decpt, sign, buf, rounding_mode,- buf_tmp, sizeof(buf_tmp));- return n_digits;-}--static int js_fcvt1(char *buf, int buf_size, double d, int n_digits,- int rounding_mode)-{- int n;- if (rounding_mode != FE_TONEAREST)- fesetround(rounding_mode);- n = snprintf(buf, buf_size, "%.*f", n_digits, d);- if (rounding_mode != FE_TONEAREST)- fesetround(FE_TONEAREST);- assert(n < buf_size);- return n;-}--static void js_fcvt(char *buf, int buf_size, double d, int n_digits)-{- int rounding_mode;- rounding_mode = FE_TONEAREST;-#ifdef CONFIG_PRINTF_RNDN- {- int n1, n2;- char buf1[JS_DTOA_BUF_SIZE];- char buf2[JS_DTOA_BUF_SIZE];-- /* The JS rounding is specified as round to nearest ties away from- zero (RNDNA), but in printf the "ties" case is not specified- (for example it is RNDN for glibc, RNDNA for Windows), so we- must round manually. */- n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_TONEAREST);- rounding_mode = FE_TONEAREST;- /* XXX: could use 2 digits to reduce the average running time */- if (buf1[n1 - 1] == '5') {- n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_DOWNWARD);- n2 = js_fcvt1(buf2, sizeof(buf2), d, n_digits + 1, FE_UPWARD);- if (n1 == n2 && memcmp(buf1, buf2, n1) == 0) {- /* exact result: round away from zero */- if (buf1[0] == '-')- rounding_mode = FE_DOWNWARD;- else- rounding_mode = FE_UPWARD;- }- }- }-#endif /* CONFIG_PRINTF_RNDN */- js_fcvt1(buf, buf_size, d, n_digits, rounding_mode);-}--/* radix != 10 is only supported with flags = JS_DTOA_VAR_FORMAT */-/* use as many digits as necessary */-#define JS_DTOA_VAR_FORMAT (0 << 0)-/* use n_digits significant digits (1 <= n_digits <= 101) */-#define JS_DTOA_FIXED_FORMAT (1 << 0)-/* force fractional format: [-]dd.dd with n_digits fractional digits */-#define JS_DTOA_FRAC_FORMAT (2 << 0)-/* force exponential notation either in fixed or variable format */-#define JS_DTOA_FORCE_EXP (1 << 2)--/* XXX: slow and maybe not fully correct. Use libbf when it is fast enough.- XXX: radix != 10 is only supported for small integers-*/-static void js_dtoa1(char *buf, double d, int radix, int n_digits, int flags)-{- char *q;-- if (!isfinite(d)) {- if (isnan(d)) {- strcpy(buf, "NaN");- } else {- q = buf;- if (d < 0)- *q++ = '-';- strcpy(q, "Infinity");- }- } else if (flags == JS_DTOA_VAR_FORMAT) {- int64_t i64;- char buf1[70], *ptr;- i64 = (int64_t)d;- if (d != i64 || i64 > MAX_SAFE_INTEGER || i64 < -MAX_SAFE_INTEGER)- goto generic_conv;- /* fast path for integers */- ptr = i64toa(buf1 + sizeof(buf1), i64, radix);- strcpy(buf, ptr);- } else {- if (d == 0.0)- d = 0.0; /* convert -0 to 0 */- if (flags == JS_DTOA_FRAC_FORMAT) {- js_fcvt(buf, JS_DTOA_BUF_SIZE, d, n_digits);- } else {- char buf1[JS_DTOA_BUF_SIZE];- int sign, decpt, k, n, i, p, n_max;- BOOL is_fixed;- generic_conv:- is_fixed = ((flags & 3) == JS_DTOA_FIXED_FORMAT);- if (is_fixed) {- n_max = n_digits;- } else {- n_max = 21;- }- /* the number has k digits (k >= 1) */- k = js_ecvt(d, n_digits, &decpt, &sign, buf1, is_fixed);- n = decpt; /* d=10^(n-k)*(buf1) i.e. d= < x.yyyy 10^(n-1) */- q = buf;- if (sign)- *q++ = '-';- if (flags & JS_DTOA_FORCE_EXP)- goto force_exp;- if (n >= 1 && n <= n_max) {- if (k <= n) {- memcpy(q, buf1, k);- q += k;- for(i = 0; i < (n - k); i++)- *q++ = '0';- *q = '\0';- } else {- /* k > n */- memcpy(q, buf1, n);- q += n;- *q++ = '.';- for(i = 0; i < (k - n); i++)- *q++ = buf1[n + i];- *q = '\0';- }- } else if (n >= -5 && n <= 0) {- *q++ = '0';- *q++ = '.';- for(i = 0; i < -n; i++)- *q++ = '0';- memcpy(q, buf1, k);- q += k;- *q = '\0';- } else {- force_exp:- /* exponential notation */- *q++ = buf1[0];- if (k > 1) {- *q++ = '.';- for(i = 1; i < k; i++)- *q++ = buf1[i];- }- *q++ = 'e';- p = n - 1;- if (p >= 0)- *q++ = '+';- sprintf(q, "%d", p);- }- }- }-}--static JSValue js_dtoa(JSContext *ctx,- double d, int radix, int n_digits, int flags)-{- char buf[JS_DTOA_BUF_SIZE];- js_dtoa1(buf, d, radix, n_digits, flags);- return JS_NewString(ctx, buf);-}--JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToPropertyKey)-{- uint32_t tag;- const char *str;- char buf[32];-- tag = JS_VALUE_GET_NORM_TAG(val);- switch(tag) {- case JS_TAG_STRING:- return JS_DupValue(ctx, val);- case JS_TAG_INT:- snprintf(buf, sizeof(buf), "%d", JS_VALUE_GET_INT(val));- str = buf;- goto new_string;- case JS_TAG_BOOL:- return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ?- JS_ATOM_true : JS_ATOM_false);- case JS_TAG_NULL:- return JS_AtomToString(ctx, JS_ATOM_null);- case JS_TAG_UNDEFINED:- return JS_AtomToString(ctx, JS_ATOM_undefined);- case JS_TAG_EXCEPTION:- return JS_EXCEPTION;- case JS_TAG_OBJECT:- {- JSValue val1, ret;- val1 = JS_ToPrimitive(ctx, val, HINT_STRING);- if (JS_IsException(val1))- return val1;- ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey);- JS_FreeValue(ctx, val1);- return ret;- }- break;- case JS_TAG_FUNCTION_BYTECODE:- str = "[function bytecode]";- goto new_string;- case JS_TAG_SYMBOL:- if (is_ToPropertyKey) {- return JS_DupValue(ctx, val);- } else {- return JS_ThrowTypeError(ctx, "cannot convert symbol to string");- }- case JS_TAG_FLOAT64:- return js_dtoa(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0,- JS_DTOA_VAR_FORMAT);-#ifdef CONFIG_BIGNUM- case JS_TAG_BIG_INT:- return ctx->rt->bigint_ops.to_string(ctx, val);- case JS_TAG_BIG_FLOAT:- return ctx->rt->bigfloat_ops.to_string(ctx, val);- case JS_TAG_BIG_DECIMAL:- return ctx->rt->bigdecimal_ops.to_string(ctx, val);-#endif- default:- str = "[unsupported type]";- new_string:- return JS_NewString(ctx, str);- }-}--JSValue JS_ToString(JSContext *ctx, JSValueConst val)-{- return JS_ToStringInternal(ctx, val, FALSE);-}--static JSValue JS_ToStringFree(JSContext *ctx, JSValue val)-{- JSValue ret;- ret = JS_ToString(ctx, val);- JS_FreeValue(ctx, val);- return ret;-}--static JSValue JS_ToLocaleStringFree(JSContext *ctx, JSValue val)-{- if (JS_IsUndefined(val) || JS_IsNull(val))- return JS_ToStringFree(ctx, val);- return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL);-}--JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val)-{- return JS_ToStringInternal(ctx, val, TRUE);-}--static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValueConst val)-{- uint32_t tag = JS_VALUE_GET_TAG(val);- if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)- return JS_ThrowTypeError(ctx, "null or undefined are forbidden");- return JS_ToString(ctx, val);-}--static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1)-{- JSValue val;- JSString *p;- int i;- uint32_t c;- StringBuffer b_s, *b = &b_s;- char buf[16];-- val = JS_ToStringCheckObject(ctx, val1);- if (JS_IsException(val))- return val;- p = JS_VALUE_GET_STRING(val);-- if (string_buffer_init(ctx, b, p->len + 2))- goto fail;-- if (string_buffer_putc8(b, '\"'))- goto fail;- for(i = 0; i < p->len; ) {- c = string_getc(p, &i);- switch(c) {- case '\t':- c = 't';- goto quote;- case '\r':- c = 'r';- goto quote;- case '\n':- c = 'n';- goto quote;- case '\b':- c = 'b';- goto quote;- case '\f':- c = 'f';- goto quote;- case '\"':- case '\\':- quote:- if (string_buffer_putc8(b, '\\'))- goto fail;- if (string_buffer_putc8(b, c))- goto fail;- break;- default:- if (c < 32 || (c >= 0xd800 && c < 0xe000)) {- snprintf(buf, sizeof(buf), "\\u%04x", c);- if (string_buffer_puts8(b, buf))- goto fail;- } else {- if (string_buffer_putc(b, c))- goto fail;- }- break;- }- }- if (string_buffer_putc8(b, '\"'))- goto fail;- JS_FreeValue(ctx, val);- return string_buffer_end(b);- fail:- JS_FreeValue(ctx, val);- string_buffer_free(b);- return JS_EXCEPTION;-}--static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt)-{- printf("%14s %4s %4s %14s %10s %s\n",- "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS");-}--/* for debug only: dump an object without side effect */-static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)-{- uint32_t i;- char atom_buf[ATOM_GET_STR_BUF_SIZE];- JSShape *sh;- JSShapeProperty *prs;- JSProperty *pr;- BOOL is_first = TRUE;-- /* XXX: should encode atoms with special characters */- sh = p->shape; /* the shape can be NULL while freeing an object */- printf("%14p %4d ",- (void *)p,- p->header.ref_count);- if (sh) {- printf("%3d%c %14p ",- sh->header.ref_count,- " *"[sh->is_hashed],- (void *)sh->proto);- } else {- printf("%3s %14s ", "-", "-");- }- printf("%10s ",- JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), rt->class_array[p->class_id].class_name));- if (p->is_exotic && p->fast_array) {- printf("[ ");- for(i = 0; i < p->u.array.count; i++) {- if (i != 0)- printf(", ");- switch (p->class_id) {- case JS_CLASS_ARRAY:- case JS_CLASS_ARGUMENTS:- JS_DumpValueShort(rt, p->u.array.u.values[i]);- break;- case JS_CLASS_UINT8C_ARRAY:- case JS_CLASS_INT8_ARRAY:- case JS_CLASS_UINT8_ARRAY:- case JS_CLASS_INT16_ARRAY:- case JS_CLASS_UINT16_ARRAY:- case JS_CLASS_INT32_ARRAY:- case JS_CLASS_UINT32_ARRAY:-#ifdef CONFIG_BIGNUM- case JS_CLASS_BIG_INT64_ARRAY:- case JS_CLASS_BIG_UINT64_ARRAY:-#endif- case JS_CLASS_FLOAT32_ARRAY:- case JS_CLASS_FLOAT64_ARRAY:- {- int size = 1 << typed_array_size_log2(p->class_id);- const uint8_t *b = p->u.array.u.uint8_ptr + i * size;- while (size-- > 0)- printf("%02X", *b++);- }- break;- }- }- printf(" ] ");- }-- if (sh) {- printf("{ ");- for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {- if (prs->atom != JS_ATOM_NULL) {- pr = &p->prop[i];- if (!is_first)- printf(", ");- printf("%s: ",- JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), prs->atom));- if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {- printf("[getset %p %p]", (void *)pr->u.getset.getter,- (void *)pr->u.getset.setter);- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {- printf("[varref %p]", (void *)pr->u.var_ref);- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {- printf("[autoinit %p %d %p]",- (void *)js_autoinit_get_realm(pr),- js_autoinit_get_id(pr),- (void *)pr->u.init.opaque);- } else {- JS_DumpValueShort(rt, pr->u.value);- }- is_first = FALSE;- }- }- printf(" }");- }- - if (js_class_has_bytecode(p->class_id)) {- JSFunctionBytecode *b = p->u.func.function_bytecode;- JSVarRef **var_refs;- if (b->closure_var_count) {- var_refs = p->u.func.var_refs;- printf(" Closure:");- for(i = 0; i < b->closure_var_count; i++) {- printf(" ");- JS_DumpValueShort(rt, var_refs[i]->value);- }- if (p->u.func.home_object) {- printf(" HomeObject: ");- JS_DumpValueShort(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object));- }- }- }- printf("\n");-}--static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p)-{- if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {- JS_DumpObject(rt, (JSObject *)p);- } else {- printf("%14p %4d ",- (void *)p,- p->ref_count);- switch(p->gc_obj_type) {- case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:- printf("[function bytecode]");- break;- case JS_GC_OBJ_TYPE_SHAPE:- printf("[shape]");- break;- case JS_GC_OBJ_TYPE_VAR_REF:- printf("[var_ref]");- break;- case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:- printf("[async_function]");- break;- case JS_GC_OBJ_TYPE_JS_CONTEXT:- printf("[js_context]");- break;- default:- printf("[unknown %d]", p->gc_obj_type);- break;- }- printf("\n");- }-}--static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,- JSValueConst val)-{- uint32_t tag = JS_VALUE_GET_NORM_TAG(val);- const char *str;-- switch(tag) {- case JS_TAG_INT:- printf("%d", JS_VALUE_GET_INT(val));- break;- case JS_TAG_BOOL:- if (JS_VALUE_GET_BOOL(val))- str = "true";- else- str = "false";- goto print_str;- case JS_TAG_NULL:- str = "null";- goto print_str;- case JS_TAG_EXCEPTION:- str = "exception";- goto print_str;- case JS_TAG_UNINITIALIZED:- str = "uninitialized";- goto print_str;- case JS_TAG_UNDEFINED:- str = "undefined";- print_str:- printf("%s", str);- break;- case JS_TAG_FLOAT64:- printf("%.14g", JS_VALUE_GET_FLOAT64(val));- break;-#ifdef CONFIG_BIGNUM- case JS_TAG_BIG_INT:- {- JSBigFloat *p = JS_VALUE_GET_PTR(val);- char *str;- str = bf_ftoa(NULL, &p->num, 10, 0,- BF_RNDZ | BF_FTOA_FORMAT_FRAC);- printf("%sn", str);- bf_realloc(&rt->bf_ctx, str, 0);- }- break;- case JS_TAG_BIG_FLOAT:- {- JSBigFloat *p = JS_VALUE_GET_PTR(val);- char *str;- str = bf_ftoa(NULL, &p->num, 16, BF_PREC_INF,- BF_RNDZ | BF_FTOA_FORMAT_FREE | BF_FTOA_ADD_PREFIX);- printf("%sl", str);- bf_free(&rt->bf_ctx, str);- }- break;- case JS_TAG_BIG_DECIMAL:- {- JSBigDecimal *p = JS_VALUE_GET_PTR(val);- char *str;- str = bfdec_ftoa(NULL, &p->num, BF_PREC_INF,- BF_RNDZ | BF_FTOA_FORMAT_FREE);- printf("%sm", str);- bf_free(&rt->bf_ctx, str);- }- break;-#endif- case JS_TAG_STRING:- {- JSString *p;- p = JS_VALUE_GET_STRING(val);- JS_DumpString(rt, p);- }- break;- case JS_TAG_FUNCTION_BYTECODE:- {- JSFunctionBytecode *b = JS_VALUE_GET_PTR(val);- char buf[ATOM_GET_STR_BUF_SIZE];- printf("[bytecode %s]", JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));- }- break;- case JS_TAG_OBJECT:- {- JSObject *p = JS_VALUE_GET_OBJ(val);- JSAtom atom = rt->class_array[p->class_id].class_name;- char atom_buf[ATOM_GET_STR_BUF_SIZE];- printf("[%s %p]",- JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), atom), (void *)p);- }- break;- case JS_TAG_SYMBOL:- {- JSAtomStruct *p = JS_VALUE_GET_PTR(val);- char atom_buf[ATOM_GET_STR_BUF_SIZE];- printf("Symbol(%s)",- JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), js_get_atom_index(rt, p)));- }- break;- case JS_TAG_MODULE:- printf("[module]");- break;- default:- printf("[unknown tag %d]", tag);- break;- }-}--static __maybe_unused void JS_DumpValue(JSContext *ctx,- JSValueConst val)-{- JS_DumpValueShort(ctx->rt, val);-}--static __maybe_unused void JS_PrintValue(JSContext *ctx,- const char *str,- JSValueConst val)-{- printf("%s=", str);- JS_DumpValueShort(ctx->rt, val);- printf("\n");-}--/* return -1 if exception (proxy case) or TRUE/FALSE */-int JS_IsArray(JSContext *ctx, JSValueConst val)-{- JSObject *p;- if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) {- p = JS_VALUE_GET_OBJ(val);- if (unlikely(p->class_id == JS_CLASS_PROXY))- return js_proxy_isArray(ctx, val);- else- return p->class_id == JS_CLASS_ARRAY;- } else {- return FALSE;- }-}--static double js_pow(double a, double b)-{- if (unlikely(!isfinite(b)) && fabs(a) == 1) {- /* not compatible with IEEE 754 */- return JS_FLOAT64_NAN;- } else {- return pow(a, b);- }-}--#ifdef CONFIG_BIGNUM--JSValue JS_NewBigInt64_1(JSContext *ctx, int64_t v)-{- JSValue val;- bf_t *a;- val = JS_NewBigInt(ctx);- if (JS_IsException(val))- return val;- a = JS_GetBigInt(val);- if (bf_set_si(a, v)) {- JS_FreeValue(ctx, val);- return JS_ThrowOutOfMemory(ctx);- }- return val;-}--JSValue JS_NewBigInt64(JSContext *ctx, int64_t v)-{- if (is_math_mode(ctx) &&- v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) {- return JS_NewInt64(ctx, v);- } else {- return JS_NewBigInt64_1(ctx, v);- }-}--JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v)-{- JSValue val;- if (is_math_mode(ctx) && v <= MAX_SAFE_INTEGER) {- val = JS_NewInt64(ctx, v);- } else {- bf_t *a;- val = JS_NewBigInt(ctx);- if (JS_IsException(val))- return val;- a = JS_GetBigInt(val);- if (bf_set_ui(a, v)) {- JS_FreeValue(ctx, val);- return JS_ThrowOutOfMemory(ctx);- }- }- return val;-}--/* if the returned bigfloat is allocated it is equal to- 'buf'. Otherwise it is a pointer to the bigfloat in 'val'. Return- NULL in case of error. */-static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val)-{- uint32_t tag;- bf_t *r;- JSBigFloat *p;-- tag = JS_VALUE_GET_NORM_TAG(val);- switch(tag) {- case JS_TAG_INT:- case JS_TAG_BOOL:- case JS_TAG_NULL:- r = buf;- bf_init(ctx->bf_ctx, r);- if (bf_set_si(r, JS_VALUE_GET_INT(val)))- goto fail;- break;- case JS_TAG_FLOAT64:- r = buf;- bf_init(ctx->bf_ctx, r);- if (bf_set_float64(r, JS_VALUE_GET_FLOAT64(val))) {- fail:- bf_delete(r);- return NULL;- }- break;- case JS_TAG_BIG_INT:- case JS_TAG_BIG_FLOAT:- p = JS_VALUE_GET_PTR(val);- r = &p->num;- break;- case JS_TAG_UNDEFINED:- default:- r = buf;- bf_init(ctx->bf_ctx, r);- bf_set_nan(r);- break;- }- return r;-}--/* return NULL if invalid type */-static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val)-{- uint32_t tag;- JSBigDecimal *p;- bfdec_t *r;- - tag = JS_VALUE_GET_NORM_TAG(val);- switch(tag) {- case JS_TAG_BIG_DECIMAL:- p = JS_VALUE_GET_PTR(val);- r = &p->num;- break;- default:- JS_ThrowTypeError(ctx, "bigdecimal expected");- r = NULL;- break;- }- return r;-}--/* return NaN if bad bigint literal */-static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val)-{- const char *str, *p;- size_t len;- int flags;- - str = JS_ToCStringLen(ctx, &len, val);- JS_FreeValue(ctx, val);- if (!str)- return JS_EXCEPTION;- p = str;- p += skip_spaces(p);- if ((p - str) == len) {- val = JS_NewBigInt64(ctx, 0);- } else {- flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT;- if (is_math_mode(ctx))- flags |= ATOD_MODE_BIGINT;- val = js_atof(ctx, p, &p, 0, flags);- p += skip_spaces(p);- if (!JS_IsException(val)) {- if ((p - str) != len) {- JS_FreeValue(ctx, val);- val = JS_NAN;- }- }- }- JS_FreeCString(ctx, str);- return val;-}--static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val)-{- val = JS_StringToBigInt(ctx, val);- if (JS_VALUE_IS_NAN(val))- return JS_ThrowSyntaxError(ctx, "invalid bigint literal");- return val;-}--/* if the returned bigfloat is allocated it is equal to- 'buf'. Otherwise it is a pointer to the bigfloat in 'val'. */-static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val)-{- uint32_t tag;- bf_t *r;- JSBigFloat *p;-- redo:- tag = JS_VALUE_GET_NORM_TAG(val);- switch(tag) {- case JS_TAG_INT:- case JS_TAG_NULL:- case JS_TAG_UNDEFINED:- if (!is_math_mode(ctx))- goto fail;- /* fall tru */- case JS_TAG_BOOL:- r = buf;- bf_init(ctx->bf_ctx, r);- bf_set_si(r, JS_VALUE_GET_INT(val));- break;- case JS_TAG_FLOAT64:- {- double d = JS_VALUE_GET_FLOAT64(val);- if (!is_math_mode(ctx))- goto fail;- if (!isfinite(d))- goto fail;- r = buf;- bf_init(ctx->bf_ctx, r);- d = trunc(d);- bf_set_float64(r, d);- }- break;- case JS_TAG_BIG_INT:- p = JS_VALUE_GET_PTR(val);- r = &p->num;- break;- case JS_TAG_BIG_FLOAT:- if (!is_math_mode(ctx))- goto fail;- p = JS_VALUE_GET_PTR(val);- if (!bf_is_finite(&p->num))- goto fail;- r = buf;- bf_init(ctx->bf_ctx, r);- bf_set(r, &p->num);- bf_rint(r, BF_RNDZ);- JS_FreeValue(ctx, val);- break;- case JS_TAG_STRING:- val = JS_StringToBigIntErr(ctx, val);- if (JS_IsException(val))- return NULL;- goto redo;- case JS_TAG_OBJECT:- val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);- if (JS_IsException(val))- return NULL;- goto redo;- default:- fail:- JS_FreeValue(ctx, val);- JS_ThrowTypeError(ctx, "cannot convert to bigint");- return NULL;- }- return r;-}--static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val)-{- return JS_ToBigIntFree(ctx, buf, JS_DupValue(ctx, val));-}--static __maybe_unused JSValue JS_ToBigIntValueFree(JSContext *ctx, JSValue val)-{- if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT) {- return val;- } else {- bf_t a_s, *a, *r;- int ret;- JSValue res; -- res = JS_NewBigInt(ctx);- if (JS_IsException(res))- return JS_EXCEPTION;- a = JS_ToBigIntFree(ctx, &a_s, val);- if (!a) {- JS_FreeValue(ctx, res);- return JS_EXCEPTION;- }- r = JS_GetBigInt(res);- ret = bf_set(r, a);- JS_FreeBigInt(ctx, a, &a_s);- if (ret) {- JS_FreeValue(ctx, res);- return JS_ThrowOutOfMemory(ctx);- }- return JS_CompactBigInt(ctx, res);- }-}--/* free the bf_t allocated by JS_ToBigInt */-static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf)-{- if (a == buf) {- bf_delete(a);- } else {- JSBigFloat *p = (JSBigFloat *)((uint8_t *)a -- offsetof(JSBigFloat, num));- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_BIG_FLOAT, p));- }-}--/* XXX: merge with JS_ToInt64Free with a specific flag */-static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val)-{- bf_t a_s, *a;-- a = JS_ToBigIntFree(ctx, &a_s, val);- if (!a) {- *pres = 0;- return -1;- }- bf_get_int64(pres, a, BF_GET_INT_MOD);- JS_FreeBigInt(ctx, a, &a_s);- return 0;-}--int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)-{- return JS_ToBigInt64Free(ctx, pres, JS_DupValue(ctx, val));-}--static JSBigFloat *js_new_bf(JSContext *ctx)-{- JSBigFloat *p;- p = js_malloc(ctx, sizeof(*p));- if (!p)- return NULL;- p->header.ref_count = 1;- bf_init(ctx->bf_ctx, &p->num);- return p;-}--static JSValue JS_NewBigFloat(JSContext *ctx)-{- JSBigFloat *p;- p = js_malloc(ctx, sizeof(*p));- if (!p)- return JS_EXCEPTION;- p->header.ref_count = 1;- bf_init(ctx->bf_ctx, &p->num);- return JS_MKPTR(JS_TAG_BIG_FLOAT, p);-}--static JSValue JS_NewBigDecimal(JSContext *ctx)-{- JSBigDecimal *p;- p = js_malloc(ctx, sizeof(*p));- if (!p)- return JS_EXCEPTION;- p->header.ref_count = 1;- bfdec_init(ctx->bf_ctx, &p->num);- return JS_MKPTR(JS_TAG_BIG_DECIMAL, p);-}--static JSValue JS_NewBigInt(JSContext *ctx)-{- JSBigFloat *p;- p = js_malloc(ctx, sizeof(*p));- if (!p)- return JS_EXCEPTION;- p->header.ref_count = 1;- bf_init(ctx->bf_ctx, &p->num);- return JS_MKPTR(JS_TAG_BIG_INT, p);-}--static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val,- BOOL convert_to_safe_integer)-{- int64_t v;- bf_t *a;- - if (JS_VALUE_GET_TAG(val) != JS_TAG_BIG_INT)- return val; /* fail safe */- a = JS_GetBigInt(val);- if (convert_to_safe_integer && bf_get_int64(&v, a, 0) == 0 &&- v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) {- JS_FreeValue(ctx, val);- return JS_NewInt64(ctx, v);- } else if (a->expn == BF_EXP_ZERO && a->sign) {- JSBigFloat *p = JS_VALUE_GET_PTR(val);- assert(p->header.ref_count == 1);- a->sign = 0;- }- return val;-}--/* Convert the big int to a safe integer if in math mode. normalize- the zero representation. Could also be used to convert the bigint- to a short bigint value. The reference count of the value must be- 1. Cannot fail */-static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val)-{- return JS_CompactBigInt1(ctx, val, is_math_mode(ctx));-}--/* must be kept in sync with JSOverloadableOperatorEnum */-/* XXX: use atoms ? */-static const char js_overloadable_operator_names[JS_OVOP_COUNT][4] = {- "+",- "-",- "*",- "/",- "%",- "**",- "|",- "&",- "^",- "<<",- ">>",- ">>>",- "==",- "<",- "pos",- "neg",- "++",- "--",- "~",-};--static int get_ovop_from_opcode(OPCodeEnum op)-{- switch(op) {- case OP_add:- return JS_OVOP_ADD;- case OP_sub:- return JS_OVOP_SUB;- case OP_mul:- return JS_OVOP_MUL;- case OP_div:- return JS_OVOP_DIV;- case OP_mod:- case OP_math_mod:- return JS_OVOP_MOD;- case OP_pow:- return JS_OVOP_POW;- case OP_or:- return JS_OVOP_OR;- case OP_and:- return JS_OVOP_AND;- case OP_xor:- return JS_OVOP_XOR;- case OP_shl:- return JS_OVOP_SHL;- case OP_sar:- return JS_OVOP_SAR;- case OP_shr:- return JS_OVOP_SHR;- case OP_eq:- case OP_neq:- return JS_OVOP_EQ;- case OP_lt:- case OP_lte:- case OP_gt:- case OP_gte:- return JS_OVOP_LESS;- case OP_plus:- return JS_OVOP_POS;- case OP_neg:- return JS_OVOP_NEG;- case OP_inc:- return JS_OVOP_INC;- case OP_dec:- return JS_OVOP_DEC;- default:- abort();- }-}--/* return NULL if not present */-static JSObject *find_binary_op(JSBinaryOperatorDef *def,- uint32_t operator_index,- JSOverloadableOperatorEnum op)-{- JSBinaryOperatorDefEntry *ent;- int i;- for(i = 0; i < def->count; i++) {- ent = &def->tab[i];- if (ent->operator_index == operator_index)- return ent->ops[op];- }- return NULL;-}--/* return -1 if exception, 0 if no operator overloading, 1 if- overloaded operator called */-static __exception int js_call_binary_op_fallback(JSContext *ctx,- JSValue *pret,- JSValueConst op1,- JSValueConst op2,- OPCodeEnum op,- BOOL is_numeric,- int hint)-{- JSValue opset1_obj, opset2_obj, method, ret, new_op1, new_op2;- JSOperatorSetData *opset1, *opset2;- JSOverloadableOperatorEnum ovop;- JSObject *p;- JSValueConst args[2];- - if (!ctx->allow_operator_overloading)- return 0;- - opset2_obj = JS_UNDEFINED;- opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet);- if (JS_IsException(opset1_obj))- goto exception;- if (JS_IsUndefined(opset1_obj))- return 0;- opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);- if (!opset1)- goto exception;-- opset2_obj = JS_GetProperty(ctx, op2, JS_ATOM_Symbol_operatorSet);- if (JS_IsException(opset2_obj))- goto exception;- if (JS_IsUndefined(opset2_obj)) {- JS_FreeValue(ctx, opset1_obj);- return 0;- }- opset2 = JS_GetOpaque2(ctx, opset2_obj, JS_CLASS_OPERATOR_SET);- if (!opset2)- goto exception;-- if (opset1->is_primitive && opset2->is_primitive) {- JS_FreeValue(ctx, opset1_obj);- JS_FreeValue(ctx, opset2_obj);- return 0;- }-- ovop = get_ovop_from_opcode(op);- - if (opset1->operator_counter == opset2->operator_counter) {- p = opset1->self_ops[ovop];- } else if (opset1->operator_counter > opset2->operator_counter) {- p = find_binary_op(&opset1->left, opset2->operator_counter, ovop);- } else {- p = find_binary_op(&opset2->right, opset1->operator_counter, ovop);- }- if (!p) {- JS_ThrowTypeError(ctx, "operator %s: no function defined",- js_overloadable_operator_names[ovop]);- goto exception;- }-- if (opset1->is_primitive) {- if (is_numeric) {- new_op1 = JS_ToNumeric(ctx, op1);- } else {- new_op1 = JS_ToPrimitive(ctx, op1, hint);- }- if (JS_IsException(new_op1))- goto exception;- } else {- new_op1 = JS_DupValue(ctx, op1);- }- - if (opset2->is_primitive) {- if (is_numeric) {- new_op2 = JS_ToNumeric(ctx, op2);- } else {- new_op2 = JS_ToPrimitive(ctx, op2, hint);- }- if (JS_IsException(new_op2)) {- JS_FreeValue(ctx, new_op1);- goto exception;- }- } else {- new_op2 = JS_DupValue(ctx, op2);- }-- /* XXX: could apply JS_ToPrimitive() if primitive type so that the- operator function does not get a value object */- - method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));- if (ovop == JS_OVOP_LESS && (op == OP_lte || op == OP_gt)) {- args[0] = new_op2;- args[1] = new_op1;- } else {- args[0] = new_op1;- args[1] = new_op2;- }- ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args);- JS_FreeValue(ctx, new_op1);- JS_FreeValue(ctx, new_op2);- if (JS_IsException(ret))- goto exception;- if (ovop == JS_OVOP_EQ) {- BOOL res = JS_ToBoolFree(ctx, ret);- if (op == OP_neq)- res ^= 1;- ret = JS_NewBool(ctx, res);- } else if (ovop == JS_OVOP_LESS) {- if (JS_IsUndefined(ret)) {- ret = JS_FALSE;- } else {- BOOL res = JS_ToBoolFree(ctx, ret);- if (op == OP_lte || op == OP_gte)- res ^= 1;- ret = JS_NewBool(ctx, res);- }- }- JS_FreeValue(ctx, opset1_obj);- JS_FreeValue(ctx, opset2_obj);- *pret = ret;- return 1;- exception:- JS_FreeValue(ctx, opset1_obj);- JS_FreeValue(ctx, opset2_obj);- *pret = JS_UNDEFINED;- return -1;-}--/* try to call the operation on the operatorSet field of 'obj'. Only- used for "/" and "**" on the BigInt prototype in math mode */-static __exception int js_call_binary_op_simple(JSContext *ctx,- JSValue *pret,- JSValueConst obj,- JSValueConst op1,- JSValueConst op2,- OPCodeEnum op)-{- JSValue opset1_obj, method, ret, new_op1, new_op2;- JSOperatorSetData *opset1;- JSOverloadableOperatorEnum ovop;- JSObject *p;- JSValueConst args[2];-- opset1_obj = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet);- if (JS_IsException(opset1_obj))- goto exception;- if (JS_IsUndefined(opset1_obj))- return 0;- opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);- if (!opset1)- goto exception;- ovop = get_ovop_from_opcode(op);-- p = opset1->self_ops[ovop];- if (!p) {- JS_FreeValue(ctx, opset1_obj);- return 0;- }-- new_op1 = JS_ToNumeric(ctx, op1);- if (JS_IsException(new_op1))- goto exception;- new_op2 = JS_ToNumeric(ctx, op2);- if (JS_IsException(new_op2)) {- JS_FreeValue(ctx, new_op1);- goto exception;- }-- method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));- args[0] = new_op1;- args[1] = new_op2;- ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args);- JS_FreeValue(ctx, new_op1);- JS_FreeValue(ctx, new_op2);- if (JS_IsException(ret))- goto exception;- JS_FreeValue(ctx, opset1_obj);- *pret = ret;- return 1;- exception:- JS_FreeValue(ctx, opset1_obj);- *pret = JS_UNDEFINED;- return -1;-}--/* return -1 if exception, 0 if no operator overloading, 1 if- overloaded operator called */-static __exception int js_call_unary_op_fallback(JSContext *ctx,- JSValue *pret,- JSValueConst op1,- OPCodeEnum op)-{- JSValue opset1_obj, method, ret;- JSOperatorSetData *opset1;- JSOverloadableOperatorEnum ovop;- JSObject *p;-- if (!ctx->allow_operator_overloading)- return 0;- - opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet);- if (JS_IsException(opset1_obj))- goto exception;- if (JS_IsUndefined(opset1_obj))- return 0;- opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);- if (!opset1)- goto exception;- if (opset1->is_primitive) {- JS_FreeValue(ctx, opset1_obj);- return 0;- }-- ovop = get_ovop_from_opcode(op);-- p = opset1->self_ops[ovop];- if (!p) {- JS_ThrowTypeError(ctx, "no overloaded operator %s",- js_overloadable_operator_names[ovop]);- goto exception;- }- method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));- ret = JS_CallFree(ctx, method, JS_UNDEFINED, 1, &op1);- if (JS_IsException(ret))- goto exception;- JS_FreeValue(ctx, opset1_obj);- *pret = ret;- return 1;- exception:- JS_FreeValue(ctx, opset1_obj);- *pret = JS_UNDEFINED;- return -1;-}--static JSValue throw_bf_exception(JSContext *ctx, int status)-{- const char *str;- if (status & BF_ST_MEM_ERROR)- return JS_ThrowOutOfMemory(ctx);- if (status & BF_ST_DIVIDE_ZERO) {- str = "division by zero";- } else if (status & BF_ST_INVALID_OP) {- str = "invalid operation";- } else {- str = "integer overflow";- }- return JS_ThrowRangeError(ctx, "%s", str);-}--static int js_unary_arith_bigint(JSContext *ctx,- JSValue *pres, OPCodeEnum op, JSValue op1)-{- bf_t a_s, *r, *a;- int ret, v;- JSValue res;- - if (op == OP_plus && !is_math_mode(ctx)) {- JS_ThrowTypeError(ctx, "bigint argument with unary +");- JS_FreeValue(ctx, op1);- return -1;- }- res = JS_NewBigInt(ctx);- if (JS_IsException(res)) {- JS_FreeValue(ctx, op1);- return -1;- }- r = JS_GetBigInt(res);- a = JS_ToBigInt(ctx, &a_s, op1);- ret = 0;- switch(op) {- case OP_inc:- case OP_dec:- v = 2 * (op - OP_dec) - 1;- ret = bf_add_si(r, a, v, BF_PREC_INF, BF_RNDZ);- break;- case OP_plus:- ret = bf_set(r, a);- break;- case OP_neg:- ret = bf_set(r, a);- bf_neg(r);- break;- case OP_not:- ret = bf_add_si(r, a, 1, BF_PREC_INF, BF_RNDZ);- bf_neg(r);- break;- default:- abort();- }- JS_FreeBigInt(ctx, a, &a_s);- JS_FreeValue(ctx, op1);- if (unlikely(ret)) {- JS_FreeValue(ctx, res);- throw_bf_exception(ctx, ret);- return -1;- }- res = JS_CompactBigInt(ctx, res);- *pres = res;- return 0;-}--static int js_unary_arith_bigfloat(JSContext *ctx,- JSValue *pres, OPCodeEnum op, JSValue op1)-{- bf_t a_s, *r, *a;- int ret, v;- JSValue res;- - if (op == OP_plus && !is_math_mode(ctx)) {- JS_ThrowTypeError(ctx, "bigfloat argument with unary +");- JS_FreeValue(ctx, op1);- return -1;- }-- res = JS_NewBigFloat(ctx);- if (JS_IsException(res)) {- JS_FreeValue(ctx, op1);- return -1;- }- r = JS_GetBigFloat(res);- a = JS_ToBigFloat(ctx, &a_s, op1);- ret = 0;- switch(op) {- case OP_inc:- case OP_dec:- v = 2 * (op - OP_dec) - 1;- ret = bf_add_si(r, a, v, ctx->fp_env.prec, ctx->fp_env.flags);- break;- case OP_plus:- ret = bf_set(r, a);- break;- case OP_neg:- ret = bf_set(r, a);- bf_neg(r);- break;- default:- abort();- }- if (a == &a_s)- bf_delete(a);- JS_FreeValue(ctx, op1);- if (unlikely(ret & BF_ST_MEM_ERROR)) {- JS_FreeValue(ctx, res);- throw_bf_exception(ctx, ret);- return -1;- }- *pres = res;- return 0;-}--static int js_unary_arith_bigdecimal(JSContext *ctx,- JSValue *pres, OPCodeEnum op, JSValue op1)-{- bfdec_t *r, *a;- int ret, v;- JSValue res;- - if (op == OP_plus && !is_math_mode(ctx)) {- JS_ThrowTypeError(ctx, "bigdecimal argument with unary +");- JS_FreeValue(ctx, op1);- return -1;- }-- res = JS_NewBigDecimal(ctx);- if (JS_IsException(res)) {- JS_FreeValue(ctx, op1);- return -1;- }- r = JS_GetBigDecimal(res);- a = JS_ToBigDecimal(ctx, op1);- ret = 0;- switch(op) {- case OP_inc:- case OP_dec:- v = 2 * (op - OP_dec) - 1;- ret = bfdec_add_si(r, a, v, BF_PREC_INF, BF_RNDZ);- break;- case OP_plus:- ret = bfdec_set(r, a);- break;- case OP_neg:- ret = bfdec_set(r, a);- bfdec_neg(r);- break;- default:- abort();- }- JS_FreeValue(ctx, op1);- if (unlikely(ret)) {- JS_FreeValue(ctx, res);- throw_bf_exception(ctx, ret);- return -1;- }- *pres = res;- return 0;-}--static no_inline __exception int js_unary_arith_slow(JSContext *ctx,- JSValue *sp,- OPCodeEnum op)-{- JSValue op1, val;- int v, ret;- uint32_t tag;-- op1 = sp[-1];- /* fast path for float64 */- if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1)))- goto handle_float64;- if (JS_IsObject(op1)) {- ret = js_call_unary_op_fallback(ctx, &val, op1, op);- if (ret < 0)- return -1;- if (ret) {- JS_FreeValue(ctx, op1);- sp[-1] = val;- return 0;- }- }-- op1 = JS_ToNumericFree(ctx, op1);- if (JS_IsException(op1))- goto exception;- tag = JS_VALUE_GET_TAG(op1);- switch(tag) {- case JS_TAG_INT:- {- int64_t v64;- v64 = JS_VALUE_GET_INT(op1);- switch(op) {- case OP_inc:- case OP_dec:- v = 2 * (op - OP_dec) - 1;- v64 += v;- break;- case OP_plus:- break;- case OP_neg:- if (v64 == 0) {- sp[-1] = __JS_NewFloat64(ctx, -0.0);- return 0;- } else {- v64 = -v64;- }- break;- default:- abort();- }- sp[-1] = JS_NewInt64(ctx, v64);- }- break;- case JS_TAG_BIG_INT:- handle_bigint:- if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, op, op1))- goto exception;- break;- case JS_TAG_BIG_FLOAT:- if (ctx->rt->bigfloat_ops.unary_arith(ctx, sp - 1, op, op1))- goto exception;- break;- case JS_TAG_BIG_DECIMAL:- if (ctx->rt->bigdecimal_ops.unary_arith(ctx, sp - 1, op, op1))- goto exception;- break;- default:- handle_float64:- {- double d;- if (is_math_mode(ctx))- goto handle_bigint;- d = JS_VALUE_GET_FLOAT64(op1);- switch(op) {- case OP_inc:- case OP_dec:- v = 2 * (op - OP_dec) - 1;- d += v;- break;- case OP_plus:- break;- case OP_neg:- d = -d;- break;- default:- abort();- }- sp[-1] = __JS_NewFloat64(ctx, d);- }- break;- }- return 0;- exception:- sp[-1] = JS_UNDEFINED;- return -1;-}--static __exception int js_post_inc_slow(JSContext *ctx,- JSValue *sp, OPCodeEnum op)-{- JSValue op1;-- /* XXX: allow custom operators */- op1 = sp[-1];- op1 = JS_ToNumericFree(ctx, op1);- if (JS_IsException(op1)) {- sp[-1] = JS_UNDEFINED;- return -1;- }- sp[-1] = op1;- sp[0] = JS_DupValue(ctx, op1);- return js_unary_arith_slow(ctx, sp + 1, op - OP_post_dec + OP_dec);-}--static no_inline int js_not_slow(JSContext *ctx, JSValue *sp)-{- JSValue op1, val;- int ret;- - op1 = sp[-1];- if (JS_IsObject(op1)) {- ret = js_call_unary_op_fallback(ctx, &val, op1, OP_not);- if (ret < 0)- return -1;- if (ret) {- JS_FreeValue(ctx, op1);- sp[-1] = val;- return 0;- }- }-- op1 = JS_ToNumericFree(ctx, op1);- if (JS_IsException(op1))- goto exception;- if (is_math_mode(ctx) || JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) {- if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, OP_not, op1))- goto exception;- } else {- int32_t v1;- if (unlikely(JS_ToInt32Free(ctx, &v1, op1)))- goto exception;- sp[-1] = JS_NewInt32(ctx, ~v1);- }- return 0;- exception:- sp[-1] = JS_UNDEFINED;- return -1;-}--static int js_binary_arith_bigfloat(JSContext *ctx, OPCodeEnum op,- JSValue *pres, JSValue op1, JSValue op2)-{- bf_t a_s, b_s, *r, *a, *b;- int ret;- JSValue res;- - res = JS_NewBigFloat(ctx);- if (JS_IsException(res)) {- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- return -1;- }- r = JS_GetBigFloat(res);- a = JS_ToBigFloat(ctx, &a_s, op1);- b = JS_ToBigFloat(ctx, &b_s, op2);- bf_init(ctx->bf_ctx, r);- switch(op) {- case OP_add:- ret = bf_add(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);- break;- case OP_sub:- ret = bf_sub(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);- break;- case OP_mul:- ret = bf_mul(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);- break;- case OP_div:- ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);- break;- case OP_math_mod:- /* Euclidian remainder */- ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags,- BF_DIVREM_EUCLIDIAN);- break;- case OP_mod:- ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags,- BF_RNDZ);- break;- case OP_pow:- ret = bf_pow(r, a, b, ctx->fp_env.prec,- ctx->fp_env.flags | BF_POW_JS_QUIRKS);- break;- default:- abort();- }- if (a == &a_s)- bf_delete(a);- if (b == &b_s)- bf_delete(b);- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- if (unlikely(ret & BF_ST_MEM_ERROR)) {- JS_FreeValue(ctx, res);- throw_bf_exception(ctx, ret);- return -1;- }- *pres = res;- return 0;-}--static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op,- JSValue *pres, JSValue op1, JSValue op2)-{- bf_t a_s, b_s, *r, *a, *b;- int ret;- JSValue res;- - res = JS_NewBigInt(ctx);- if (JS_IsException(res))- goto fail;- a = JS_ToBigInt(ctx, &a_s, op1);- if (!a)- goto fail;- b = JS_ToBigInt(ctx, &b_s, op2);- if (!b) {- JS_FreeBigInt(ctx, a, &a_s);- goto fail;- }- r = JS_GetBigInt(res);- ret = 0;- switch(op) {- case OP_add:- ret = bf_add(r, a, b, BF_PREC_INF, BF_RNDZ);- break;- case OP_sub:- ret = bf_sub(r, a, b, BF_PREC_INF, BF_RNDZ);- break;- case OP_mul:- ret = bf_mul(r, a, b, BF_PREC_INF, BF_RNDZ);- break;- case OP_div:- if (!is_math_mode(ctx)) {- bf_t rem_s, *rem = &rem_s;- bf_init(ctx->bf_ctx, rem);- ret = bf_divrem(r, rem, a, b, BF_PREC_INF, BF_RNDZ,- BF_RNDZ);- bf_delete(rem);- } else {- goto math_mode_div_pow;- }- break;- case OP_math_mod:- /* Euclidian remainder */- ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ,- BF_DIVREM_EUCLIDIAN) & BF_ST_INVALID_OP;- break;- case OP_mod:- ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ,- BF_RNDZ) & BF_ST_INVALID_OP;- break;- case OP_pow:- if (b->sign) {- if (!is_math_mode(ctx)) {- ret = BF_ST_INVALID_OP;- } else {- math_mode_div_pow:- JS_FreeValue(ctx, res);- ret = js_call_binary_op_simple(ctx, &res, ctx->class_proto[JS_CLASS_BIG_INT], op1, op2, op);- if (ret != 0) {- JS_FreeBigInt(ctx, a, &a_s);- JS_FreeBigInt(ctx, b, &b_s);- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- if (ret < 0) {- return -1;- } else {- *pres = res;- return 0;- }- }- /* if no BigInt power operator defined, return a- bigfloat */- res = JS_NewBigFloat(ctx);- if (JS_IsException(res)) {- JS_FreeBigInt(ctx, a, &a_s);- JS_FreeBigInt(ctx, b, &b_s);- goto fail;- }- r = JS_GetBigFloat(res);- if (op == OP_div) {- ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags) & BF_ST_MEM_ERROR;- } else {- ret = bf_pow(r, a, b, ctx->fp_env.prec,- ctx->fp_env.flags | BF_POW_JS_QUIRKS) & BF_ST_MEM_ERROR;- }- JS_FreeBigInt(ctx, a, &a_s);- JS_FreeBigInt(ctx, b, &b_s);- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- if (unlikely(ret)) {- JS_FreeValue(ctx, res);- throw_bf_exception(ctx, ret);- return -1;- }- *pres = res;- return 0;- }- } else {- ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUIRKS);- }- break;-- /* logical operations */- case OP_shl:- case OP_sar:- {- slimb_t v2;-#if LIMB_BITS == 32- bf_get_int32(&v2, b, 0);- if (v2 == INT32_MIN)- v2 = INT32_MIN + 1;-#else- bf_get_int64(&v2, b, 0);- if (v2 == INT64_MIN)- v2 = INT64_MIN + 1;-#endif- if (op == OP_sar)- v2 = -v2;- ret = bf_set(r, a);- ret |= bf_mul_2exp(r, v2, BF_PREC_INF, BF_RNDZ);- if (v2 < 0) {- ret |= bf_rint(r, BF_RNDD) & (BF_ST_OVERFLOW | BF_ST_MEM_ERROR);- }- }- break;- case OP_and:- ret = bf_logic_and(r, a, b);- break;- case OP_or:- ret = bf_logic_or(r, a, b);- break;- case OP_xor:- ret = bf_logic_xor(r, a, b);- break;- default:- abort();- }- JS_FreeBigInt(ctx, a, &a_s);- JS_FreeBigInt(ctx, b, &b_s);- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- if (unlikely(ret)) {- JS_FreeValue(ctx, res);- throw_bf_exception(ctx, ret);- return -1;- }- *pres = JS_CompactBigInt(ctx, res);- return 0;- fail:- JS_FreeValue(ctx, res);- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- return -1;-}--/* b must be a positive integer */-static int js_bfdec_pow(bfdec_t *r, const bfdec_t *a, const bfdec_t *b)-{- bfdec_t b1;- int32_t b2;- int ret;-- bfdec_init(b->ctx, &b1);- ret = bfdec_set(&b1, b);- if (ret) {- bfdec_delete(&b1);- return ret;- }- ret = bfdec_rint(&b1, BF_RNDZ);- if (ret) {- bfdec_delete(&b1);- return BF_ST_INVALID_OP; /* must be an integer */- }- ret = bfdec_get_int32(&b2, &b1);- bfdec_delete(&b1);- if (ret)- return ret; /* overflow */- if (b2 < 0)- return BF_ST_INVALID_OP; /* must be positive */- return bfdec_pow_ui(r, a, b2);-}--static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op,- JSValue *pres, JSValue op1, JSValue op2)-{- bfdec_t *r, *a, *b;- int ret;- JSValue res;-- res = JS_NewBigDecimal(ctx);- if (JS_IsException(res))- goto fail;- r = JS_GetBigDecimal(res);- - a = JS_ToBigDecimal(ctx, op1);- if (!a)- goto fail;- b = JS_ToBigDecimal(ctx, op2);- if (!b)- goto fail;- switch(op) {- case OP_add:- ret = bfdec_add(r, a, b, BF_PREC_INF, BF_RNDZ);- break;- case OP_sub:- ret = bfdec_sub(r, a, b, BF_PREC_INF, BF_RNDZ);- break;- case OP_mul:- ret = bfdec_mul(r, a, b, BF_PREC_INF, BF_RNDZ);- break;- case OP_div:- ret = bfdec_div(r, a, b, BF_PREC_INF, BF_RNDZ);- break;- case OP_math_mod:- /* Euclidian remainder */- ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_DIVREM_EUCLIDIAN);- break;- case OP_mod:- ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_RNDZ);- break;- case OP_pow:- ret = js_bfdec_pow(r, a, b);- break;- default:- abort();- }- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- if (unlikely(ret)) {- JS_FreeValue(ctx, res);- throw_bf_exception(ctx, ret);- return -1;- }- *pres = res;- return 0;- fail:- JS_FreeValue(ctx, res);- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- return -1;-}--static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp,- OPCodeEnum op)-{- JSValue op1, op2, res;- uint32_t tag1, tag2;- int ret;- double d1, d2;-- op1 = sp[-2];- op2 = sp[-1];- tag1 = JS_VALUE_GET_NORM_TAG(op1);- tag2 = JS_VALUE_GET_NORM_TAG(op2);- /* fast path for float operations */- if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) {- d1 = JS_VALUE_GET_FLOAT64(op1);- d2 = JS_VALUE_GET_FLOAT64(op2);- goto handle_float64;- }-- /* try to call an overloaded operator */- if ((tag1 == JS_TAG_OBJECT &&- (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||- (tag2 == JS_TAG_OBJECT &&- (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {- ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0);- if (ret != 0) {- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- if (ret < 0) {- goto exception;- } else {- sp[-2] = res;- return 0;- }- }- }-- op1 = JS_ToNumericFree(ctx, op1);- if (JS_IsException(op1)) {- JS_FreeValue(ctx, op2);- goto exception;- }- op2 = JS_ToNumericFree(ctx, op2);- if (JS_IsException(op2)) {- JS_FreeValue(ctx, op1);- goto exception;- }- tag1 = JS_VALUE_GET_NORM_TAG(op1);- tag2 = JS_VALUE_GET_NORM_TAG(op2);-- if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {- int32_t v1, v2;- int64_t v;- v1 = JS_VALUE_GET_INT(op1);- v2 = JS_VALUE_GET_INT(op2);- switch(op) {- case OP_sub:- v = (int64_t)v1 - (int64_t)v2;- break;- case OP_mul:- v = (int64_t)v1 * (int64_t)v2;- if (is_math_mode(ctx) &&- (v < -MAX_SAFE_INTEGER || v > MAX_SAFE_INTEGER))- goto handle_bigint;- if (v == 0 && (v1 | v2) < 0) {- sp[-2] = __JS_NewFloat64(ctx, -0.0);- return 0;- }- break;- case OP_div:- if (is_math_mode(ctx))- goto handle_bigint;- sp[-2] = __JS_NewFloat64(ctx, (double)v1 / (double)v2);- return 0;- case OP_math_mod:- if (unlikely(v2 == 0)) {- throw_bf_exception(ctx, BF_ST_DIVIDE_ZERO);- goto exception;- }- v = (int64_t)v1 % (int64_t)v2;- if (v < 0) {- if (v2 < 0)- v -= v2;- else- v += v2;- }- break;- case OP_mod:- if (v1 < 0 || v2 <= 0) {- sp[-2] = JS_NewFloat64(ctx, fmod(v1, v2));- return 0;- } else {- v = (int64_t)v1 % (int64_t)v2;- }- break;- case OP_pow:- if (!is_math_mode(ctx)) {- sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2));- return 0;- } else {- goto handle_bigint;- }- break;- default:- abort();- }- sp[-2] = JS_NewInt64(ctx, v);- } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {- if (ctx->rt->bigdecimal_ops.binary_arith(ctx, op, sp - 2, op1, op2))- goto exception;- } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {- if (ctx->rt->bigfloat_ops.binary_arith(ctx, op, sp - 2, op1, op2))- goto exception;- } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {- handle_bigint:- if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))- goto exception;- } else {- double dr;- /* float64 result */- if (JS_ToFloat64Free(ctx, &d1, op1)) {- JS_FreeValue(ctx, op2);- goto exception;- }- if (JS_ToFloat64Free(ctx, &d2, op2))- goto exception;- handle_float64:- if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2))- goto handle_bigint;- switch(op) {- case OP_sub:- dr = d1 - d2;- break;- case OP_mul:- dr = d1 * d2;- break;- case OP_div:- dr = d1 / d2;- break;- case OP_mod:- dr = fmod(d1, d2);- break;- case OP_math_mod:- d2 = fabs(d2);- dr = fmod(d1, d2);- /* XXX: loss of accuracy if dr < 0 */- if (dr < 0)- dr += d2;- break;- case OP_pow:- dr = js_pow(d1, d2);- break;- default:- abort();- }- sp[-2] = __JS_NewFloat64(ctx, dr);- }- return 0;- exception:- sp[-2] = JS_UNDEFINED;- sp[-1] = JS_UNDEFINED;- return -1;-}--static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)-{- JSValue op1, op2, res;- uint32_t tag1, tag2;- int ret;-- op1 = sp[-2];- op2 = sp[-1];-- tag1 = JS_VALUE_GET_NORM_TAG(op1);- tag2 = JS_VALUE_GET_NORM_TAG(op2);- /* fast path for float64 */- if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) {- double d1, d2;- d1 = JS_VALUE_GET_FLOAT64(op1);- d2 = JS_VALUE_GET_FLOAT64(op2);- sp[-2] = __JS_NewFloat64(ctx, d1 + d2);- return 0;- }-- if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) {- /* try to call an overloaded operator */- if ((tag1 == JS_TAG_OBJECT &&- (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED &&- tag2 != JS_TAG_STRING)) ||- (tag2 == JS_TAG_OBJECT &&- (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED &&- tag1 != JS_TAG_STRING))) {- ret = js_call_binary_op_fallback(ctx, &res, op1, op2, OP_add,- FALSE, HINT_NONE);- if (ret != 0) {- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- if (ret < 0) {- goto exception;- } else {- sp[-2] = res;- return 0;- }- }- }-- op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);- if (JS_IsException(op1)) {- JS_FreeValue(ctx, op2);- goto exception;- }-- op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);- if (JS_IsException(op2)) {- JS_FreeValue(ctx, op1);- goto exception;- }- tag1 = JS_VALUE_GET_NORM_TAG(op1);- tag2 = JS_VALUE_GET_NORM_TAG(op2);- }-- if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) {- sp[-2] = JS_ConcatString(ctx, op1, op2);- if (JS_IsException(sp[-2]))- goto exception;- return 0;- }-- op1 = JS_ToNumericFree(ctx, op1);- if (JS_IsException(op1)) {- JS_FreeValue(ctx, op2);- goto exception;- }- op2 = JS_ToNumericFree(ctx, op2);- if (JS_IsException(op2)) {- JS_FreeValue(ctx, op1);- goto exception;- }- tag1 = JS_VALUE_GET_NORM_TAG(op1);- tag2 = JS_VALUE_GET_NORM_TAG(op2);-- if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {- int32_t v1, v2;- int64_t v;- v1 = JS_VALUE_GET_INT(op1);- v2 = JS_VALUE_GET_INT(op2);- v = (int64_t)v1 + (int64_t)v2;- sp[-2] = JS_NewInt64(ctx, v);- } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {- if (ctx->rt->bigdecimal_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))- goto exception;- } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {- if (ctx->rt->bigfloat_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))- goto exception;- } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {- handle_bigint:- if (ctx->rt->bigint_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))- goto exception;- } else {- double d1, d2;- /* float64 result */- if (JS_ToFloat64Free(ctx, &d1, op1)) {- JS_FreeValue(ctx, op2);- goto exception;- }- if (JS_ToFloat64Free(ctx, &d2, op2))- goto exception;- if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2))- goto handle_bigint;- sp[-2] = __JS_NewFloat64(ctx, d1 + d2);- }- return 0;- exception:- sp[-2] = JS_UNDEFINED;- sp[-1] = JS_UNDEFINED;- return -1;-}--static no_inline __exception int js_binary_logic_slow(JSContext *ctx,- JSValue *sp,- OPCodeEnum op)-{- JSValue op1, op2, res;- int ret;- uint32_t tag1, tag2;- uint32_t v1, v2, r;-- op1 = sp[-2];- op2 = sp[-1];- tag1 = JS_VALUE_GET_NORM_TAG(op1);- tag2 = JS_VALUE_GET_NORM_TAG(op2);-- /* try to call an overloaded operator */- if ((tag1 == JS_TAG_OBJECT &&- (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||- (tag2 == JS_TAG_OBJECT &&- (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {- ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0);- if (ret != 0) {- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- if (ret < 0) {- goto exception;- } else {- sp[-2] = res;- return 0;- }- }- }-- op1 = JS_ToNumericFree(ctx, op1);- if (JS_IsException(op1)) {- JS_FreeValue(ctx, op2);- goto exception;- }- op2 = JS_ToNumericFree(ctx, op2);- if (JS_IsException(op2)) {- JS_FreeValue(ctx, op1);- goto exception;- }-- if (is_math_mode(ctx))- goto bigint_op;-- tag1 = JS_VALUE_GET_TAG(op1);- tag2 = JS_VALUE_GET_TAG(op2);- if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {- if (tag1 != tag2) {- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- JS_ThrowTypeError(ctx, "both operands must be bigint");- goto exception;- } else {- bigint_op:- if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))- goto exception;- }- } else {- if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) {- JS_FreeValue(ctx, op2);- goto exception;- }- if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2)))- goto exception;- switch(op) {- case OP_shl:- r = v1 << (v2 & 0x1f);- break;- case OP_sar:- r = (int)v1 >> (v2 & 0x1f);- break;- case OP_and:- r = v1 & v2;- break;- case OP_or:- r = v1 | v2;- break;- case OP_xor:- r = v1 ^ v2;- break;- default:- abort();- }- sp[-2] = JS_NewInt32(ctx, r);- }- return 0;- exception:- sp[-2] = JS_UNDEFINED;- sp[-1] = JS_UNDEFINED;- return -1;-}--/* Note: also used for bigint */-static int js_compare_bigfloat(JSContext *ctx, OPCodeEnum op,- JSValue op1, JSValue op2)-{- bf_t a_s, b_s, *a, *b;- int res;- - a = JS_ToBigFloat(ctx, &a_s, op1);- if (!a) {- JS_FreeValue(ctx, op2);- return -1;- }- b = JS_ToBigFloat(ctx, &b_s, op2);- if (!b) {- if (a == &a_s)- bf_delete(a);- JS_FreeValue(ctx, op1);- return -1;- }- switch(op) {- case OP_lt:- res = bf_cmp_lt(a, b); /* if NaN return false */- break;- case OP_lte:- res = bf_cmp_le(a, b); /* if NaN return false */- break;- case OP_gt:- res = bf_cmp_lt(b, a); /* if NaN return false */- break;- case OP_gte:- res = bf_cmp_le(b, a); /* if NaN return false */- break;- case OP_eq:- res = bf_cmp_eq(a, b); /* if NaN return false */- break;- default:- abort();- }- if (a == &a_s)- bf_delete(a);- if (b == &b_s)- bf_delete(b);- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- return res;-}--static int js_compare_bigdecimal(JSContext *ctx, OPCodeEnum op,- JSValue op1, JSValue op2)-{- bfdec_t *a, *b;- int res;-- /* Note: binary floats are converted to bigdecimal with- toString(). It is not mathematically correct but is consistent- with the BigDecimal() constructor behavior */- op1 = JS_ToBigDecimalFree(ctx, op1, TRUE);- if (JS_IsException(op1)) {- JS_FreeValue(ctx, op2);- return -1;- }- op2 = JS_ToBigDecimalFree(ctx, op2, TRUE);- if (JS_IsException(op2)) {- JS_FreeValue(ctx, op1);- return -1;- }- a = JS_ToBigDecimal(ctx, op1);- b = JS_ToBigDecimal(ctx, op2);- - switch(op) {- case OP_lt:- res = bfdec_cmp_lt(a, b); /* if NaN return false */- break;- case OP_lte:- res = bfdec_cmp_le(a, b); /* if NaN return false */- break;- case OP_gt:- res = bfdec_cmp_lt(b, a); /* if NaN return false */- break;- case OP_gte:- res = bfdec_cmp_le(b, a); /* if NaN return false */- break;- case OP_eq:- res = bfdec_cmp_eq(a, b); /* if NaN return false */- break;- default:- abort();- }- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- return res;-}--static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,- OPCodeEnum op)-{- JSValue op1, op2, ret;- int res;- uint32_t tag1, tag2;-- op1 = sp[-2];- op2 = sp[-1];- tag1 = JS_VALUE_GET_NORM_TAG(op1);- tag2 = JS_VALUE_GET_NORM_TAG(op2);- /* try to call an overloaded operator */- if ((tag1 == JS_TAG_OBJECT &&- (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||- (tag2 == JS_TAG_OBJECT &&- (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {- res = js_call_binary_op_fallback(ctx, &ret, op1, op2, op,- FALSE, HINT_NUMBER);- if (res != 0) {- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- if (res < 0) {- goto exception;- } else {- sp[-2] = ret;- return 0;- }- }- }- op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER);- if (JS_IsException(op1)) {- JS_FreeValue(ctx, op2);- goto exception;- }- op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER);- if (JS_IsException(op2)) {- JS_FreeValue(ctx, op1);- goto exception;- }- tag1 = JS_VALUE_GET_NORM_TAG(op1);- tag2 = JS_VALUE_GET_NORM_TAG(op2);-- if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) {- JSString *p1, *p2;- p1 = JS_VALUE_GET_STRING(op1);- p2 = JS_VALUE_GET_STRING(op2);- res = js_string_compare(ctx, p1, p2);- switch(op) {- case OP_lt:- res = (res < 0);- break;- case OP_lte:- res = (res <= 0);- break;- case OP_gt:- res = (res > 0);- break;- default:- case OP_gte:- res = (res >= 0);- break;- }- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- } else if ((tag1 <= JS_TAG_NULL || tag1 == JS_TAG_FLOAT64) &&- (tag2 <= JS_TAG_NULL || tag2 == JS_TAG_FLOAT64)) {- /* fast path for float64/int */- goto float64_compare;- } else {- if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) ||- (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING)) &&- !is_math_mode(ctx)) {- if (tag1 == JS_TAG_STRING) {- op1 = JS_StringToBigInt(ctx, op1);- if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT)- goto invalid_bigint_string;- }- if (tag2 == JS_TAG_STRING) {- op2 = JS_StringToBigInt(ctx, op2);- if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) {- invalid_bigint_string:- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- res = FALSE;- goto done;- }- }- } else {- op1 = JS_ToNumericFree(ctx, op1);- if (JS_IsException(op1)) {- JS_FreeValue(ctx, op2);- goto exception;- }- op2 = JS_ToNumericFree(ctx, op2);- if (JS_IsException(op2)) {- JS_FreeValue(ctx, op1);- goto exception;- }- }-- tag1 = JS_VALUE_GET_NORM_TAG(op1);- tag2 = JS_VALUE_GET_NORM_TAG(op2);-- if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {- res = ctx->rt->bigdecimal_ops.compare(ctx, op, op1, op2);- if (res < 0)- goto exception;- } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {- res = ctx->rt->bigfloat_ops.compare(ctx, op, op1, op2);- if (res < 0)- goto exception;- } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {- res = ctx->rt->bigint_ops.compare(ctx, op, op1, op2);- if (res < 0)- goto exception;- } else {- double d1, d2;-- float64_compare:- /* can use floating point comparison */- if (tag1 == JS_TAG_FLOAT64) {- d1 = JS_VALUE_GET_FLOAT64(op1);- } else {- d1 = JS_VALUE_GET_INT(op1);- }- if (tag2 == JS_TAG_FLOAT64) {- d2 = JS_VALUE_GET_FLOAT64(op2);- } else {- d2 = JS_VALUE_GET_INT(op2);- }- switch(op) {- case OP_lt:- res = (d1 < d2); /* if NaN return false */- break;- case OP_lte:- res = (d1 <= d2); /* if NaN return false */- break;- case OP_gt:- res = (d1 > d2); /* if NaN return false */- break;- default:- case OP_gte:- res = (d1 >= d2); /* if NaN return false */- break;- }- }- }- done:- sp[-2] = JS_NewBool(ctx, res);- return 0;- exception:- sp[-2] = JS_UNDEFINED;- sp[-1] = JS_UNDEFINED;- return -1;-}--static BOOL tag_is_number(uint32_t tag)-{- return (tag == JS_TAG_INT || tag == JS_TAG_BIG_INT ||- tag == JS_TAG_FLOAT64 || tag == JS_TAG_BIG_FLOAT ||- tag == JS_TAG_BIG_DECIMAL);-}--static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,- BOOL is_neq)-{- JSValue op1, op2, ret;- int res;- uint32_t tag1, tag2;-- op1 = sp[-2];- op2 = sp[-1];- redo:- tag1 = JS_VALUE_GET_NORM_TAG(op1);- tag2 = JS_VALUE_GET_NORM_TAG(op2);- if (tag_is_number(tag1) && tag_is_number(tag2)) {- if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {- res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2);- } else if ((tag1 == JS_TAG_FLOAT64 &&- (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64)) ||- (tag2 == JS_TAG_FLOAT64 &&- (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64))) {- double d1, d2;- if (tag1 == JS_TAG_FLOAT64) {- d1 = JS_VALUE_GET_FLOAT64(op1);- } else {- d1 = JS_VALUE_GET_INT(op1);- }- if (tag2 == JS_TAG_FLOAT64) {- d2 = JS_VALUE_GET_FLOAT64(op2);- } else {- d2 = JS_VALUE_GET_INT(op2);- }- res = (d1 == d2);- } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {- res = ctx->rt->bigdecimal_ops.compare(ctx, OP_eq, op1, op2);- if (res < 0)- goto exception;- } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {- res = ctx->rt->bigfloat_ops.compare(ctx, OP_eq, op1, op2);- if (res < 0)- goto exception;- } else {- res = ctx->rt->bigint_ops.compare(ctx, OP_eq, op1, op2);- if (res < 0)- goto exception;- }- } else if (tag1 == tag2) {- if (tag1 == JS_TAG_OBJECT) {- /* try the fallback operator */- res = js_call_binary_op_fallback(ctx, &ret, op1, op2,- is_neq ? OP_neq : OP_eq,- FALSE, HINT_NONE);- if (res != 0) {- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- if (res < 0) {- goto exception;- } else {- sp[-2] = ret;- return 0;- }- }- }- res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);- } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) ||- (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) {- res = TRUE;- } else if ((tag1 == JS_TAG_STRING && tag_is_number(tag2)) ||- (tag2 == JS_TAG_STRING && tag_is_number(tag1))) {-- if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) &&- !is_math_mode(ctx)) {- if (tag1 == JS_TAG_STRING) {- op1 = JS_StringToBigInt(ctx, op1);- if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT)- goto invalid_bigint_string;- }- if (tag2 == JS_TAG_STRING) {- op2 = JS_StringToBigInt(ctx, op2);- if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) {- invalid_bigint_string:- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- res = FALSE;- goto done;- }- }- } else {- op1 = JS_ToNumericFree(ctx, op1);- if (JS_IsException(op1)) {- JS_FreeValue(ctx, op2);- goto exception;- }- op2 = JS_ToNumericFree(ctx, op2);- if (JS_IsException(op2)) {- JS_FreeValue(ctx, op1);- goto exception;- }- }- res = js_strict_eq(ctx, op1, op2);- } else if (tag1 == JS_TAG_BOOL) {- op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1));- goto redo;- } else if (tag2 == JS_TAG_BOOL) {- op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2));- goto redo;- } else if ((tag1 == JS_TAG_OBJECT &&- (tag_is_number(tag2) || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) ||- (tag2 == JS_TAG_OBJECT &&- (tag_is_number(tag1) || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL))) {-- /* try the fallback operator */- res = js_call_binary_op_fallback(ctx, &ret, op1, op2,- is_neq ? OP_neq : OP_eq,- FALSE, HINT_NONE);- if (res != 0) {- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- if (res < 0) {- goto exception;- } else {- sp[-2] = ret;- return 0;- }- }-- op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);- if (JS_IsException(op1)) {- JS_FreeValue(ctx, op2);- goto exception;- }- op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);- if (JS_IsException(op2)) {- JS_FreeValue(ctx, op1);- goto exception;- }- goto redo;- } else {- /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */- if ((JS_IsHTMLDDA(ctx, op1) &&- (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) ||- (JS_IsHTMLDDA(ctx, op2) &&- (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) {- res = TRUE;- } else {- res = FALSE;- }- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- }- done:- sp[-2] = JS_NewBool(ctx, res ^ is_neq);- return 0;- exception:- sp[-2] = JS_UNDEFINED;- sp[-1] = JS_UNDEFINED;- return -1;-}--static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp)-{- JSValue op1, op2;- uint32_t v1, v2, r;-- op1 = sp[-2];- op2 = sp[-1];- op1 = JS_ToNumericFree(ctx, op1);- if (JS_IsException(op1)) {- JS_FreeValue(ctx, op2);- goto exception;- }- op2 = JS_ToNumericFree(ctx, op2);- if (JS_IsException(op2)) {- JS_FreeValue(ctx, op1);- goto exception;- }- /* XXX: could forbid >>> in bignum mode */- if (!is_math_mode(ctx) &&- (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT ||- JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT)) {- JS_ThrowTypeError(ctx, "bigint operands are forbidden for >>>");- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- goto exception;- }- /* cannot give an exception */- JS_ToUint32Free(ctx, &v1, op1);- JS_ToUint32Free(ctx, &v2, op2);- r = v1 >> (v2 & 0x1f);- sp[-2] = JS_NewUint32(ctx, r);- return 0;- exception:- sp[-2] = JS_UNDEFINED;- sp[-1] = JS_UNDEFINED;- return -1;-}--static JSValue js_mul_pow10_to_float64(JSContext *ctx, const bf_t *a,- int64_t exponent)-{- bf_t r_s, *r = &r_s;- double d;- int ret;- - /* always convert to Float64 */- bf_init(ctx->bf_ctx, r);- ret = bf_mul_pow_radix(r, a, 10, exponent,- 53, bf_set_exp_bits(11) | BF_RNDN |- BF_FLAG_SUBNORMAL);- bf_get_float64(r, &d, BF_RNDN);- bf_delete(r);- if (ret & BF_ST_MEM_ERROR)- return JS_ThrowOutOfMemory(ctx);- else- return __JS_NewFloat64(ctx, d);-}--static no_inline int js_mul_pow10(JSContext *ctx, JSValue *sp)-{- bf_t a_s, *a, *r;- JSValue op1, op2, res;- int64_t e;- int ret;-- res = JS_NewBigFloat(ctx);- if (JS_IsException(res))- return -1;- r = JS_GetBigFloat(res);- op1 = sp[-2];- op2 = sp[-1];- a = JS_ToBigFloat(ctx, &a_s, op1);- if (!a)- return -1;- if (JS_IsBigInt(ctx, op2)) {- ret = JS_ToBigInt64(ctx, &e, op2);- } else {- ret = JS_ToInt64(ctx, &e, op2);- }- if (ret) {- if (a == &a_s)- bf_delete(a);- JS_FreeValue(ctx, res);- return -1;- }-- bf_mul_pow_radix(r, a, 10, e, ctx->fp_env.prec, ctx->fp_env.flags);- if (a == &a_s)- bf_delete(a);- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- sp[-2] = res;- return 0;-}--#else /* !CONFIG_BIGNUM */--static JSValue JS_ThrowUnsupportedBigint(JSContext *ctx)-{- return JS_ThrowTypeError(ctx, "bigint is not supported");-}--JSValue JS_NewBigInt64(JSContext *ctx, int64_t v)-{- return JS_ThrowUnsupportedBigint(ctx);-}--JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v)-{- return JS_ThrowUnsupportedBigint(ctx);-}--int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)-{- JS_ThrowUnsupportedBigint(ctx);- *pres = 0;- return -1;-}--static no_inline __exception int js_unary_arith_slow(JSContext *ctx,- JSValue *sp,- OPCodeEnum op)-{- JSValue op1;- double d;-- op1 = sp[-1];- if (unlikely(JS_ToFloat64Free(ctx, &d, op1))) {- sp[-1] = JS_UNDEFINED;- return -1;- }- switch(op) {- case OP_inc:- d++;- break;- case OP_dec:- d--;- break;- case OP_plus:- break;- case OP_neg:- d = -d;- break;- default:- abort();- }- sp[-1] = JS_NewFloat64(ctx, d);- return 0;-}--/* specific case necessary for correct return value semantics */-static __exception int js_post_inc_slow(JSContext *ctx,- JSValue *sp, OPCodeEnum op)-{- JSValue op1;- double d, r;-- op1 = sp[-1];- if (unlikely(JS_ToFloat64Free(ctx, &d, op1))) {- sp[-1] = JS_UNDEFINED;- return -1;- }- r = d + 2 * (op - OP_post_dec) - 1;- sp[0] = JS_NewFloat64(ctx, r);- sp[-1] = JS_NewFloat64(ctx, d);- return 0;-}--static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp,- OPCodeEnum op)-{- JSValue op1, op2;- double d1, d2, r;-- op1 = sp[-2];- op2 = sp[-1];- if (unlikely(JS_ToFloat64Free(ctx, &d1, op1))) {- JS_FreeValue(ctx, op2);- goto exception;- }- if (unlikely(JS_ToFloat64Free(ctx, &d2, op2))) {- goto exception;- }- switch(op) {- case OP_sub:- r = d1 - d2;- break;- case OP_mul:- r = d1 * d2;- break;- case OP_div:- r = d1 / d2;- break;- case OP_mod:- r = fmod(d1, d2);- break;- case OP_pow:- r = js_pow(d1, d2);- break;- default:- abort();- }- sp[-2] = JS_NewFloat64(ctx, r);- return 0;- exception:- sp[-2] = JS_UNDEFINED;- sp[-1] = JS_UNDEFINED;- return -1;-}--static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)-{- JSValue op1, op2;- uint32_t tag1, tag2;-- op1 = sp[-2];- op2 = sp[-1];- tag1 = JS_VALUE_GET_TAG(op1);- tag2 = JS_VALUE_GET_TAG(op2);- if ((tag1 == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag1)) &&- (tag2 == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag2))) {- goto add_numbers;- } else {- op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);- if (JS_IsException(op1)) {- JS_FreeValue(ctx, op2);- goto exception;- }- op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);- if (JS_IsException(op2)) {- JS_FreeValue(ctx, op1);- goto exception;- }- tag1 = JS_VALUE_GET_TAG(op1);- tag2 = JS_VALUE_GET_TAG(op2);- if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) {- sp[-2] = JS_ConcatString(ctx, op1, op2);- if (JS_IsException(sp[-2]))- goto exception;- } else {- double d1, d2;- add_numbers:- if (JS_ToFloat64Free(ctx, &d1, op1)) {- JS_FreeValue(ctx, op2);- goto exception;- }- if (JS_ToFloat64Free(ctx, &d2, op2))- goto exception;- sp[-2] = JS_NewFloat64(ctx, d1 + d2);- }- }- return 0;- exception:- sp[-2] = JS_UNDEFINED;- sp[-1] = JS_UNDEFINED;- return -1;-}--static no_inline __exception int js_binary_logic_slow(JSContext *ctx,- JSValue *sp,- OPCodeEnum op)-{- JSValue op1, op2;- uint32_t v1, v2, r;-- op1 = sp[-2];- op2 = sp[-1];- if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) {- JS_FreeValue(ctx, op2);- goto exception;- }- if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2)))- goto exception;- switch(op) {- case OP_shl:- r = v1 << (v2 & 0x1f);- break;- case OP_sar:- r = (int)v1 >> (v2 & 0x1f);- break;- case OP_and:- r = v1 & v2;- break;- case OP_or:- r = v1 | v2;- break;- case OP_xor:- r = v1 ^ v2;- break;- default:- abort();- }- sp[-2] = JS_NewInt32(ctx, r);- return 0;- exception:- sp[-2] = JS_UNDEFINED;- sp[-1] = JS_UNDEFINED;- return -1;-}--static no_inline int js_not_slow(JSContext *ctx, JSValue *sp)-{- int32_t v1;-- if (unlikely(JS_ToInt32Free(ctx, &v1, sp[-1]))) {- sp[-1] = JS_UNDEFINED;- return -1;- }- sp[-1] = JS_NewInt32(ctx, ~v1);- return 0;-}--static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,- OPCodeEnum op)-{- JSValue op1, op2;- int res;-- op1 = sp[-2];- op2 = sp[-1];- op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER);- if (JS_IsException(op1)) {- JS_FreeValue(ctx, op2);- goto exception;- }- op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER);- if (JS_IsException(op2)) {- JS_FreeValue(ctx, op1);- goto exception;- }- if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING &&- JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) {- JSString *p1, *p2;- p1 = JS_VALUE_GET_STRING(op1);- p2 = JS_VALUE_GET_STRING(op2);- res = js_string_compare(ctx, p1, p2);- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- switch(op) {- case OP_lt:- res = (res < 0);- break;- case OP_lte:- res = (res <= 0);- break;- case OP_gt:- res = (res > 0);- break;- default:- case OP_gte:- res = (res >= 0);- break;- }- } else {- double d1, d2;- if (JS_ToFloat64Free(ctx, &d1, op1)) {- JS_FreeValue(ctx, op2);- goto exception;- }- if (JS_ToFloat64Free(ctx, &d2, op2))- goto exception;- switch(op) {- case OP_lt:- res = (d1 < d2); /* if NaN return false */- break;- case OP_lte:- res = (d1 <= d2); /* if NaN return false */- break;- case OP_gt:- res = (d1 > d2); /* if NaN return false */- break;- default:- case OP_gte:- res = (d1 >= d2); /* if NaN return false */- break;- }- }- sp[-2] = JS_NewBool(ctx, res);- return 0;- exception:- sp[-2] = JS_UNDEFINED;- sp[-1] = JS_UNDEFINED;- return -1;-}--static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,- BOOL is_neq)-{- JSValue op1, op2;- int tag1, tag2;- BOOL res;-- op1 = sp[-2];- op2 = sp[-1];- redo:- tag1 = JS_VALUE_GET_NORM_TAG(op1);- tag2 = JS_VALUE_GET_NORM_TAG(op2);- if (tag1 == tag2 ||- (tag1 == JS_TAG_INT && tag2 == JS_TAG_FLOAT64) ||- (tag2 == JS_TAG_INT && tag1 == JS_TAG_FLOAT64)) {- res = js_strict_eq(ctx, op1, op2);- } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) ||- (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) {- res = TRUE;- } else if ((tag1 == JS_TAG_STRING && (tag2 == JS_TAG_INT ||- tag2 == JS_TAG_FLOAT64)) ||- (tag2 == JS_TAG_STRING && (tag1 == JS_TAG_INT ||- tag1 == JS_TAG_FLOAT64))) {- double d1;- double d2;- if (JS_ToFloat64Free(ctx, &d1, op1)) {- JS_FreeValue(ctx, op2);- goto exception;- }- if (JS_ToFloat64Free(ctx, &d2, op2))- goto exception;- res = (d1 == d2);- } else if (tag1 == JS_TAG_BOOL) {- op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1));- goto redo;- } else if (tag2 == JS_TAG_BOOL) {- op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2));- goto redo;- } else if (tag1 == JS_TAG_OBJECT &&- (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64 || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) {- op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);- if (JS_IsException(op1)) {- JS_FreeValue(ctx, op2);- goto exception;- }- goto redo;- } else if (tag2 == JS_TAG_OBJECT &&- (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64 || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL)) {- op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);- if (JS_IsException(op2)) {- JS_FreeValue(ctx, op1);- goto exception;- }- goto redo;- } else {- /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */- if ((JS_IsHTMLDDA(ctx, op1) &&- (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) ||- (JS_IsHTMLDDA(ctx, op2) &&- (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) {- res = TRUE;- } else {- res = FALSE;- }- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- }- sp[-2] = JS_NewBool(ctx, res ^ is_neq);- return 0;- exception:- sp[-2] = JS_UNDEFINED;- sp[-1] = JS_UNDEFINED;- return -1;-}--static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp)-{- JSValue op1, op2;- uint32_t v1, v2, r;-- op1 = sp[-2];- op2 = sp[-1];- if (unlikely(JS_ToUint32Free(ctx, &v1, op1))) {- JS_FreeValue(ctx, op2);- goto exception;- }- if (unlikely(JS_ToUint32Free(ctx, &v2, op2)))- goto exception;- r = v1 >> (v2 & 0x1f);- sp[-2] = JS_NewUint32(ctx, r);- return 0;- exception:- sp[-2] = JS_UNDEFINED;- sp[-1] = JS_UNDEFINED;- return -1;-}--#endif /* !CONFIG_BIGNUM */--/* XXX: Should take JSValueConst arguments */-static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,- JSStrictEqModeEnum eq_mode)-{- BOOL res;- int tag1, tag2;- double d1, d2;-- tag1 = JS_VALUE_GET_NORM_TAG(op1);- tag2 = JS_VALUE_GET_NORM_TAG(op2);- switch(tag1) {- case JS_TAG_BOOL:- if (tag1 != tag2) {- res = FALSE;- } else {- res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2);- goto done_no_free;- }- break;- case JS_TAG_NULL:- case JS_TAG_UNDEFINED:- res = (tag1 == tag2);- break;- case JS_TAG_STRING:- {- JSString *p1, *p2;- if (tag1 != tag2) {- res = FALSE;- } else {- p1 = JS_VALUE_GET_STRING(op1);- p2 = JS_VALUE_GET_STRING(op2);- res = (js_string_compare(ctx, p1, p2) == 0);- }- }- break;- case JS_TAG_SYMBOL:- {- JSAtomStruct *p1, *p2;- if (tag1 != tag2) {- res = FALSE;- } else {- p1 = JS_VALUE_GET_PTR(op1);- p2 = JS_VALUE_GET_PTR(op2);- res = (p1 == p2);- }- }- break;- case JS_TAG_OBJECT:- if (tag1 != tag2)- res = FALSE;- else- res = JS_VALUE_GET_OBJ(op1) == JS_VALUE_GET_OBJ(op2);- break;- case JS_TAG_INT:- d1 = JS_VALUE_GET_INT(op1);- if (tag2 == JS_TAG_INT) {- d2 = JS_VALUE_GET_INT(op2);- goto number_test;- } else if (tag2 == JS_TAG_FLOAT64) {- d2 = JS_VALUE_GET_FLOAT64(op2);- goto number_test;- } else {- res = FALSE;- }- break;- case JS_TAG_FLOAT64:- d1 = JS_VALUE_GET_FLOAT64(op1);- if (tag2 == JS_TAG_FLOAT64) {- d2 = JS_VALUE_GET_FLOAT64(op2);- } else if (tag2 == JS_TAG_INT) {- d2 = JS_VALUE_GET_INT(op2);- } else {- res = FALSE;- break;- }- number_test:- if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) {- JSFloat64Union u1, u2;- /* NaN is not always normalized, so this test is necessary */- if (isnan(d1) || isnan(d2)) {- res = isnan(d1) == isnan(d2);- } else if (eq_mode == JS_EQ_SAME_VALUE_ZERO) {- res = (d1 == d2); /* +0 == -0 */- } else {- u1.d = d1;- u2.d = d2;- res = (u1.u64 == u2.u64); /* +0 != -0 */- }- } else {- res = (d1 == d2); /* if NaN return false and +0 == -0 */- }- goto done_no_free;-#ifdef CONFIG_BIGNUM- case JS_TAG_BIG_INT:- {- bf_t a_s, *a, b_s, *b;- if (tag1 != tag2) {- res = FALSE;- break;- }- a = JS_ToBigFloat(ctx, &a_s, op1);- b = JS_ToBigFloat(ctx, &b_s, op2);- res = bf_cmp_eq(a, b);- if (a == &a_s)- bf_delete(a);- if (b == &b_s)- bf_delete(b);- }- break;- case JS_TAG_BIG_FLOAT:- {- JSBigFloat *p1, *p2;- const bf_t *a, *b;- if (tag1 != tag2) {- res = FALSE;- break;- }- p1 = JS_VALUE_GET_PTR(op1);- p2 = JS_VALUE_GET_PTR(op2);- a = &p1->num;- b = &p2->num;- if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) {- if (eq_mode == JS_EQ_SAME_VALUE_ZERO &&- a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO) {- res = TRUE;- } else {- res = (bf_cmp_full(a, b) == 0);- }- } else {- res = bf_cmp_eq(a, b);- }- }- break;- case JS_TAG_BIG_DECIMAL:- {- JSBigDecimal *p1, *p2;- const bfdec_t *a, *b;- if (tag1 != tag2) {- res = FALSE;- break;- }- p1 = JS_VALUE_GET_PTR(op1);- p2 = JS_VALUE_GET_PTR(op2);- a = &p1->num;- b = &p2->num;- res = bfdec_cmp_eq(a, b);- }- break;-#endif- default:- res = FALSE;- break;- }- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- done_no_free:- return res;-}--static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2)-{- return js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);-}--static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2)-{- return js_strict_eq2(ctx,- JS_DupValue(ctx, op1), JS_DupValue(ctx, op2),- JS_EQ_SAME_VALUE);-}--static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2)-{- return js_strict_eq2(ctx,- JS_DupValue(ctx, op1), JS_DupValue(ctx, op2),- JS_EQ_SAME_VALUE_ZERO);-}--static no_inline int js_strict_eq_slow(JSContext *ctx, JSValue *sp,- BOOL is_neq)-{- BOOL res;- res = js_strict_eq(ctx, sp[-2], sp[-1]);- sp[-2] = JS_NewBool(ctx, res ^ is_neq);- return 0;-}--static __exception int js_operator_in(JSContext *ctx, JSValue *sp)-{- JSValue op1, op2;- JSAtom atom;- int ret;-- op1 = sp[-2];- op2 = sp[-1];-- if (JS_VALUE_GET_TAG(op2) != JS_TAG_OBJECT) {- JS_ThrowTypeError(ctx, "invalid 'in' operand");- return -1;- }- atom = JS_ValueToAtom(ctx, op1);- if (unlikely(atom == JS_ATOM_NULL))- return -1;- ret = JS_HasProperty(ctx, op2, atom);- JS_FreeAtom(ctx, atom);- if (ret < 0)- return -1;- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- sp[-2] = JS_NewBool(ctx, ret);- return 0;-}--static __exception int js_has_unscopable(JSContext *ctx, JSValueConst obj,- JSAtom atom)-{- JSValue arr, val;- int ret;-- arr = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_unscopables);- if (JS_IsException(arr))- return -1;- ret = 0;- if (JS_IsObject(arr)) {- val = JS_GetProperty(ctx, arr, atom);- ret = JS_ToBoolFree(ctx, val);- }- JS_FreeValue(ctx, arr);- return ret;-}--static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp)-{- JSValue op1, op2;- BOOL ret;-- op1 = sp[-2];- op2 = sp[-1];- ret = JS_IsInstanceOf(ctx, op1, op2);- if (ret < 0)- return ret;- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- sp[-2] = JS_NewBool(ctx, ret);- return 0;-}--static __exception int js_operator_typeof(JSContext *ctx, JSValueConst op1)-{- JSAtom atom;- uint32_t tag;-- tag = JS_VALUE_GET_NORM_TAG(op1);- switch(tag) {-#ifdef CONFIG_BIGNUM- case JS_TAG_BIG_INT:- atom = JS_ATOM_bigint;- break;- case JS_TAG_BIG_FLOAT:- atom = JS_ATOM_bigfloat;- break;- case JS_TAG_BIG_DECIMAL:- atom = JS_ATOM_bigdecimal;- break;-#endif- case JS_TAG_INT:- case JS_TAG_FLOAT64:- atom = JS_ATOM_number;- break;- case JS_TAG_UNDEFINED:- atom = JS_ATOM_undefined;- break;- case JS_TAG_BOOL:- atom = JS_ATOM_boolean;- break;- case JS_TAG_STRING:- atom = JS_ATOM_string;- break;- case JS_TAG_OBJECT:- {- JSObject *p;- p = JS_VALUE_GET_OBJ(op1);- if (unlikely(p->is_HTMLDDA)) - atom = JS_ATOM_undefined;- else if (JS_IsFunction(ctx, op1))- atom = JS_ATOM_function;- else- goto obj_type;- }- break;- case JS_TAG_NULL:- obj_type:- atom = JS_ATOM_object;- break;- case JS_TAG_SYMBOL:- atom = JS_ATOM_symbol;- break;- default:- atom = JS_ATOM_unknown;- break;- }- return atom;-}--static __exception int js_operator_delete(JSContext *ctx, JSValue *sp)-{- JSValue op1, op2;- JSAtom atom;- int ret;-- op1 = sp[-2];- op2 = sp[-1];- atom = JS_ValueToAtom(ctx, op2);- if (unlikely(atom == JS_ATOM_NULL))- return -1;- ret = JS_DeleteProperty(ctx, op1, atom, JS_PROP_THROW_STRICT);- JS_FreeAtom(ctx, atom);- if (unlikely(ret < 0))- return -1;- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- sp[-2] = JS_NewBool(ctx, ret);- return 0;-}--static JSValue js_throw_type_error(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return JS_ThrowTypeError(ctx, "invalid property access");-}--/* XXX: not 100% compatible, but mozilla seems to use a similar- implementation to ensure that caller in non strict mode does not- throw (ES5 compatibility) */-static JSValue js_function_proto_caller(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);- if (!b || (b->js_mode & JS_MODE_STRICT) || !b->has_prototype) {- return js_throw_type_error(ctx, this_val, 0, NULL);- }- return JS_UNDEFINED;-}--static JSValue js_function_proto_fileName(JSContext *ctx,- JSValueConst this_val)-{- JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);- if (b && b->has_debug) {- return JS_AtomToString(ctx, b->debug.filename);- }- return JS_UNDEFINED;-}--static JSValue js_function_proto_lineNumber(JSContext *ctx,- JSValueConst this_val)-{- JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);- if (b && b->has_debug) {- return JS_NewInt32(ctx, b->debug.line_num);- }- return JS_UNDEFINED;-}--static int js_arguments_define_own_property(JSContext *ctx,- JSValueConst this_obj,- JSAtom prop, JSValueConst val,- JSValueConst getter, JSValueConst setter, int flags)-{- JSObject *p;- uint32_t idx;- p = JS_VALUE_GET_OBJ(this_obj);- /* convert to normal array when redefining an existing numeric field */- if (p->fast_array && JS_AtomIsArrayIndex(ctx, &idx, prop) &&- idx < p->u.array.count) {- if (convert_fast_array_to_array(ctx, p))- return -1;- }- /* run the default define own property */- return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter,- flags | JS_PROP_NO_EXOTIC);-}--static const JSClassExoticMethods js_arguments_exotic_methods = {- .define_own_property = js_arguments_define_own_property,-};--static JSValue js_build_arguments(JSContext *ctx, int argc, JSValueConst *argv)-{- JSValue val, *tab;- JSProperty *pr;- JSObject *p;- int i;-- val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],- JS_CLASS_ARGUMENTS);- if (JS_IsException(val))- return val;- p = JS_VALUE_GET_OBJ(val);-- /* add the length field (cannot fail) */- pr = add_property(ctx, p, JS_ATOM_length,- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);- pr->u.value = JS_NewInt32(ctx, argc);-- /* initialize the fast array part */- tab = NULL;- if (argc > 0) {- tab = js_malloc(ctx, sizeof(tab[0]) * argc);- if (!tab) {- JS_FreeValue(ctx, val);- return JS_EXCEPTION;- }- for(i = 0; i < argc; i++) {- tab[i] = JS_DupValue(ctx, argv[i]);- }- }- p->u.array.u.values = tab;- p->u.array.count = argc;-- JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator,- JS_DupValue(ctx, ctx->array_proto_values),- JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);- /* add callee property to throw a TypeError in strict mode */- JS_DefineProperty(ctx, val, JS_ATOM_callee, JS_UNDEFINED,- ctx->throw_type_error, ctx->throw_type_error,- JS_PROP_HAS_GET | JS_PROP_HAS_SET);- return val;-}--#define GLOBAL_VAR_OFFSET 0x40000000-#define ARGUMENT_VAR_OFFSET 0x20000000--/* legacy arguments object: add references to the function arguments */-static JSValue js_build_mapped_arguments(JSContext *ctx, int argc,- JSValueConst *argv,- JSStackFrame *sf, int arg_count)-{- JSValue val;- JSProperty *pr;- JSObject *p;- int i;-- val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],- JS_CLASS_MAPPED_ARGUMENTS);- if (JS_IsException(val))- return val;- p = JS_VALUE_GET_OBJ(val);-- /* add the length field (cannot fail) */- pr = add_property(ctx, p, JS_ATOM_length,- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);- pr->u.value = JS_NewInt32(ctx, argc);-- for(i = 0; i < arg_count; i++) {- JSVarRef *var_ref;- var_ref = get_var_ref(ctx, sf, i, TRUE);- if (!var_ref)- goto fail;- pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF);- if (!pr) {- free_var_ref(ctx->rt, var_ref);- goto fail;- }- pr->u.var_ref = var_ref;- }-- /* the arguments not mapped to the arguments of the function can- be normal properties */- for(i = arg_count; i < argc; i++) {- if (JS_DefinePropertyValueUint32(ctx, val, i,- JS_DupValue(ctx, argv[i]),- JS_PROP_C_W_E) < 0)- goto fail;- }-- JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator,- JS_DupValue(ctx, ctx->array_proto_values),- JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);- /* callee returns this function in non strict mode */- JS_DefinePropertyValue(ctx, val, JS_ATOM_callee,- JS_DupValue(ctx, ctx->rt->current_stack_frame->cur_func),- JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);- return val;- fail:- JS_FreeValue(ctx, val);- return JS_EXCEPTION;-}--static JSValue js_build_rest(JSContext *ctx, int first, int argc, JSValueConst *argv)-{- JSValue val;- int i, ret;-- val = JS_NewArray(ctx);- if (JS_IsException(val))- return val;- for (i = first; i < argc; i++) {- ret = JS_DefinePropertyValueUint32(ctx, val, i - first,- JS_DupValue(ctx, argv[i]),- JS_PROP_C_W_E);- if (ret < 0) {- JS_FreeValue(ctx, val);- return JS_EXCEPTION;- }- }- return val;-}--static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj)-{- JSObject *p, *p1;- JSPropertyEnum *tab_atom;- int i;- JSValue enum_obj;- JSForInIterator *it;- uint32_t tag, tab_atom_count;-- tag = JS_VALUE_GET_TAG(obj);- if (tag != JS_TAG_OBJECT && tag != JS_TAG_NULL && tag != JS_TAG_UNDEFINED) {- obj = JS_ToObjectFree(ctx, obj);- }-- it = js_malloc(ctx, sizeof(*it));- if (!it) {- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;- }- enum_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_FOR_IN_ITERATOR);- if (JS_IsException(enum_obj)) {- js_free(ctx, it);- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;- }- it->is_array = FALSE;- it->obj = obj;- it->idx = 0;- p = JS_VALUE_GET_OBJ(enum_obj);- p->u.for_in_iterator = it;-- if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)- return enum_obj;-- p = JS_VALUE_GET_OBJ(obj);-- /* fast path: assume no enumerable properties in the prototype chain */- p1 = p->shape->proto;- while (p1 != NULL) {- if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p1,- JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY))- goto fail;- js_free_prop_enum(ctx, tab_atom, tab_atom_count);- if (tab_atom_count != 0) {- goto slow_path;- }- p1 = p1->shape->proto;- }- if (p->fast_array) {- JSShape *sh;- JSShapeProperty *prs;- /* check that there are no enumerable normal fields */- sh = p->shape;- for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {- if (prs->flags & JS_PROP_ENUMERABLE)- goto normal_case;- }- /* the implicit GetOwnProperty raises an exception if the- typed array is detached */- if ((p->class_id >= JS_CLASS_UINT8C_ARRAY &&- p->class_id <= JS_CLASS_FLOAT64_ARRAY) &&- typed_array_is_detached(ctx, p) &&- typed_array_get_length(ctx, p) != 0) {- JS_ThrowTypeErrorDetachedArrayBuffer(ctx);- goto fail;- }- /* for fast arrays, we only store the number of elements */- it->is_array = TRUE;- it->array_length = p->u.array.count;- } else {- normal_case:- if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,- JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY))- goto fail;- for(i = 0; i < tab_atom_count; i++) {- JS_SetPropertyInternal(ctx, enum_obj, tab_atom[i].atom, JS_NULL, 0);- }- js_free_prop_enum(ctx, tab_atom, tab_atom_count);- }- return enum_obj;-- slow_path:- /* non enumerable properties hide the enumerables ones in the- prototype chain */- while (p != NULL) {- if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,- JS_GPN_STRING_MASK | JS_GPN_SET_ENUM))- goto fail;- for(i = 0; i < tab_atom_count; i++) {- JS_DefinePropertyValue(ctx, enum_obj, tab_atom[i].atom, JS_NULL,- (tab_atom[i].is_enumerable ?- JS_PROP_ENUMERABLE : 0));- }- js_free_prop_enum(ctx, tab_atom, tab_atom_count);- p = p->shape->proto;- }- return enum_obj;-- fail:- JS_FreeValue(ctx, enum_obj);- return JS_EXCEPTION;-}--/* obj -> enum_obj */-static __exception int js_for_in_start(JSContext *ctx, JSValue *sp)-{- sp[-1] = build_for_in_iterator(ctx, sp[-1]);- if (JS_IsException(sp[-1]))- return -1;- return 0;-}--/* enum_obj -> enum_obj value done */-static __exception int js_for_in_next(JSContext *ctx, JSValue *sp)-{- JSValueConst enum_obj;- JSObject *p;- JSAtom prop;- JSForInIterator *it;- int ret;-- enum_obj = sp[-1];- /* fail safe */- if (JS_VALUE_GET_TAG(enum_obj) != JS_TAG_OBJECT)- goto done;- p = JS_VALUE_GET_OBJ(enum_obj);- if (p->class_id != JS_CLASS_FOR_IN_ITERATOR)- goto done;- it = p->u.for_in_iterator;-- for(;;) {- if (it->is_array) {- if (it->idx >= it->array_length)- goto done;- prop = __JS_AtomFromUInt32(it->idx);- it->idx++;- } else {- JSShape *sh = p->shape;- JSShapeProperty *prs;- if (it->idx >= sh->prop_count)- goto done;- prs = get_shape_prop(sh) + it->idx;- prop = prs->atom;- it->idx++;- if (prop == JS_ATOM_NULL || !(prs->flags & JS_PROP_ENUMERABLE))- continue;- }- /* check if the property was deleted */- ret = JS_HasProperty(ctx, it->obj, prop);- if (ret < 0)- return ret;- if (ret)- break;- }- /* return the property */- sp[0] = JS_AtomToValue(ctx, prop);- sp[1] = JS_FALSE;- return 0;- done:- /* return the end */- sp[0] = JS_UNDEFINED;- sp[1] = JS_TRUE;- return 0;-}--static JSValue JS_GetIterator2(JSContext *ctx, JSValueConst obj,- JSValueConst method)-{- JSValue enum_obj;-- enum_obj = JS_Call(ctx, method, obj, 0, NULL);- if (JS_IsException(enum_obj))- return enum_obj;- if (!JS_IsObject(enum_obj)) {- JS_FreeValue(ctx, enum_obj);- return JS_ThrowTypeErrorNotAnObject(ctx);- }- return enum_obj;-}--static JSValue JS_GetIterator(JSContext *ctx, JSValueConst obj, BOOL is_async)-{- JSValue method, ret, sync_iter;-- if (is_async) {- method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_asyncIterator);- if (JS_IsException(method))- return method;- if (JS_IsUndefined(method) || JS_IsNull(method)) {- method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);- if (JS_IsException(method))- return method;- sync_iter = JS_GetIterator2(ctx, obj, method);- JS_FreeValue(ctx, method);- if (JS_IsException(sync_iter))- return sync_iter;- ret = JS_CreateAsyncFromSyncIterator(ctx, sync_iter);- JS_FreeValue(ctx, sync_iter);- return ret;- }- } else {- method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);- if (JS_IsException(method))- return method;- }- if (!JS_IsFunction(ctx, method)) {- JS_FreeValue(ctx, method);- return JS_ThrowTypeError(ctx, "value is not iterable");- }- ret = JS_GetIterator2(ctx, obj, method);- JS_FreeValue(ctx, method);- return ret;-}--/* return *pdone = 2 if the iterator object is not parsed */-static JSValue JS_IteratorNext2(JSContext *ctx, JSValueConst enum_obj,- JSValueConst method,- int argc, JSValueConst *argv, int *pdone)-{- JSValue obj;-- /* fast path for the built-in iterators (avoid creating the- intermediate result object) */- if (JS_IsObject(method)) {- JSObject *p = JS_VALUE_GET_OBJ(method);- if (p->class_id == JS_CLASS_C_FUNCTION &&- p->u.cfunc.cproto == JS_CFUNC_iterator_next) {- JSCFunctionType func;- JSValueConst args[1];-- /* in case the function expects one argument */- if (argc == 0) {- args[0] = JS_UNDEFINED;- argv = args;- }- func = p->u.cfunc.c_function;- return func.iterator_next(ctx, enum_obj, argc, argv,- pdone, p->u.cfunc.magic);- }- }- obj = JS_Call(ctx, method, enum_obj, argc, argv);- if (JS_IsException(obj))- goto fail;- if (!JS_IsObject(obj)) {- JS_FreeValue(ctx, obj);- JS_ThrowTypeError(ctx, "iterator must return an object");- goto fail;- }- *pdone = 2;- return obj;- fail:- *pdone = FALSE;- return JS_EXCEPTION;-}--static JSValue JS_IteratorNext(JSContext *ctx, JSValueConst enum_obj,- JSValueConst method,- int argc, JSValueConst *argv, BOOL *pdone)-{- JSValue obj, value, done_val;- int done;-- obj = JS_IteratorNext2(ctx, enum_obj, method, argc, argv, &done);- if (JS_IsException(obj))- goto fail;- if (done != 2) {- *pdone = done;- return obj;- } else {- done_val = JS_GetProperty(ctx, obj, JS_ATOM_done);- if (JS_IsException(done_val))- goto fail;- *pdone = JS_ToBoolFree(ctx, done_val);- value = JS_UNDEFINED;- if (!*pdone) {- value = JS_GetProperty(ctx, obj, JS_ATOM_value);- }- JS_FreeValue(ctx, obj);- return value;- }- fail:- JS_FreeValue(ctx, obj);- *pdone = FALSE;- return JS_EXCEPTION;-}--/* return < 0 in case of exception */-static int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj,- BOOL is_exception_pending)-{- JSValue method, ret, ex_obj;- int res;-- if (is_exception_pending) {- ex_obj = ctx->rt->current_exception;- ctx->rt->current_exception = JS_NULL;- res = -1;- } else {- ex_obj = JS_UNDEFINED;- res = 0;- }- method = JS_GetProperty(ctx, enum_obj, JS_ATOM_return);- if (JS_IsException(method)) {- res = -1;- goto done;- }- if (JS_IsUndefined(method) || JS_IsNull(method)) {- goto done;- }- ret = JS_CallFree(ctx, method, enum_obj, 0, NULL);- if (!is_exception_pending) {- if (JS_IsException(ret)) {- res = -1;- } else if (!JS_IsObject(ret)) {- JS_ThrowTypeErrorNotAnObject(ctx);- res = -1;- }- }- JS_FreeValue(ctx, ret);- done:- if (is_exception_pending) {- JS_Throw(ctx, ex_obj);- }- return res;-}--/* obj -> enum_rec (3 slots) */-static __exception int js_for_of_start(JSContext *ctx, JSValue *sp,- BOOL is_async)-{- JSValue op1, obj, method;- op1 = sp[-1];- obj = JS_GetIterator(ctx, op1, is_async);- if (JS_IsException(obj))- return -1;- JS_FreeValue(ctx, op1);- sp[-1] = obj;- method = JS_GetProperty(ctx, obj, JS_ATOM_next);- if (JS_IsException(method))- return -1;- sp[0] = method;- return 0;-}--/* enum_rec [objs] -> enum_rec [objs] value done. There are 'offset'- objs. If 'done' is true or in case of exception, 'enum_rec' is set- to undefined. If 'done' is true, 'value' is always set to- undefined. */-static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset)-{- JSValue value = JS_UNDEFINED;- int done = 1;-- if (likely(!JS_IsUndefined(sp[offset]))) {- value = JS_IteratorNext(ctx, sp[offset], sp[offset + 1], 0, NULL, &done);- if (JS_IsException(value))- done = -1;- if (done) {- /* value is JS_UNDEFINED or JS_EXCEPTION */- /* replace the iteration object with undefined */- JS_FreeValue(ctx, sp[offset]);- sp[offset] = JS_UNDEFINED;- if (done < 0) {- return -1;- } else {- JS_FreeValue(ctx, value);- value = JS_UNDEFINED;- }- }- }- sp[0] = value;- sp[1] = JS_NewBool(ctx, done);- return 0;-}--static __exception int js_for_await_of_next(JSContext *ctx, JSValue *sp)-{- JSValue result;- result = JS_Call(ctx, sp[-2], sp[-3], 0, NULL);- if (JS_IsException(result))- return -1;- sp[0] = result;- return 0;-}--static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValueConst obj,- BOOL *pdone)-{- JSValue done_val, value;- BOOL done;- done_val = JS_GetProperty(ctx, obj, JS_ATOM_done);- if (JS_IsException(done_val))- goto fail;- done = JS_ToBoolFree(ctx, done_val);- value = JS_GetProperty(ctx, obj, JS_ATOM_value);- if (JS_IsException(value))- goto fail;- *pdone = done;- return value;- fail:- *pdone = FALSE;- return JS_EXCEPTION;-}--static __exception int js_iterator_get_value_done(JSContext *ctx, JSValue *sp)-{- JSValue obj, value;- BOOL done;- obj = sp[-1];- if (!JS_IsObject(obj)) {- JS_ThrowTypeError(ctx, "iterator must return an object");- return -1;- }- value = JS_IteratorGetCompleteValue(ctx, obj, &done);- if (JS_IsException(value))- return -1;- JS_FreeValue(ctx, obj);- sp[-1] = value;- sp[0] = JS_NewBool(ctx, done);- return 0;-}--static JSValue js_create_iterator_result(JSContext *ctx,- JSValue val,- BOOL done)-{- JSValue obj;- obj = JS_NewObject(ctx);- if (JS_IsException(obj)) {- JS_FreeValue(ctx, val);- return obj;- }- if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_value,- val, JS_PROP_C_W_E) < 0) {- goto fail;- }- if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_done,- JS_NewBool(ctx, done), JS_PROP_C_W_E) < 0) {- fail:- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;- }- return obj;-}--static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv,- BOOL *pdone, int magic);--static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic);--static BOOL js_is_fast_array(JSContext *ctx, JSValueConst obj)-{- /* Try and handle fast arrays explicitly */- if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {- JSObject *p = JS_VALUE_GET_OBJ(obj);- if (p->class_id == JS_CLASS_ARRAY && p->fast_array) {- return TRUE;- }- }- return FALSE;-}--/* Access an Array's internal JSValue array if available */-static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj,- JSValue **arrpp, uint32_t *countp)-{- /* Try and handle fast arrays explicitly */- if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {- JSObject *p = JS_VALUE_GET_OBJ(obj);- if (p->class_id == JS_CLASS_ARRAY && p->fast_array) {- *countp = p->u.array.count;- *arrpp = p->u.array.u.values;- return TRUE;- }- }- return FALSE;-}--static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp)-{- JSValue iterator, enumobj, method, value;- int is_array_iterator;- JSValue *arrp;- uint32_t i, count32, pos;- - if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) {- JS_ThrowInternalError(ctx, "invalid index for append");- return -1;- }-- pos = JS_VALUE_GET_INT(sp[-2]);-- /* XXX: further optimisations:- - use ctx->array_proto_values?- - check if array_iterator_prototype next method is built-in and- avoid constructing actual iterator object?- - build this into js_for_of_start and use in all `for (x of o)` loops- */- iterator = JS_GetProperty(ctx, sp[-1], JS_ATOM_Symbol_iterator);- if (JS_IsException(iterator))- return -1;- is_array_iterator = JS_IsCFunction(ctx, iterator,- (JSCFunction *)js_create_array_iterator,- JS_ITERATOR_KIND_VALUE);- JS_FreeValue(ctx, iterator);-- enumobj = JS_GetIterator(ctx, sp[-1], FALSE);- if (JS_IsException(enumobj))- return -1;- method = JS_GetProperty(ctx, enumobj, JS_ATOM_next);- if (JS_IsException(method)) {- JS_FreeValue(ctx, enumobj);- return -1;- }- if (is_array_iterator- && JS_IsCFunction(ctx, method, (JSCFunction *)js_array_iterator_next, 0)- && js_get_fast_array(ctx, sp[-1], &arrp, &count32)) {- uint32_t len;- if (js_get_length32(ctx, &len, sp[-1]))- goto exception;- /* if len > count32, the elements >= count32 might be read in- the prototypes and might have side effects */- if (len != count32)- goto general_case;- /* Handle fast arrays explicitly */- for (i = 0; i < count32; i++) {- if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++,- JS_DupValue(ctx, arrp[i]), JS_PROP_C_W_E) < 0)- goto exception;- }- } else {- general_case:- for (;;) {- BOOL done;- value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done);- if (JS_IsException(value))- goto exception;- if (done) {- /* value is JS_UNDEFINED */- break;- }- if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, value, JS_PROP_C_W_E) < 0)- goto exception;- }- }- /* Note: could raise an error if too many elements */- sp[-2] = JS_NewInt32(ctx, pos);- JS_FreeValue(ctx, enumobj);- JS_FreeValue(ctx, method);- return 0;--exception:- JS_IteratorClose(ctx, enumobj, TRUE);- JS_FreeValue(ctx, enumobj);- JS_FreeValue(ctx, method);- return -1;-}--static __exception int JS_CopyDataProperties(JSContext *ctx,- JSValueConst target,- JSValueConst source,- JSValueConst excluded,- BOOL setprop)-{- JSPropertyEnum *tab_atom;- JSValue val;- uint32_t i, tab_atom_count;- JSObject *p;- JSObject *pexcl = NULL;- int ret = 0, flags;-- if (JS_VALUE_GET_TAG(source) != JS_TAG_OBJECT)- return 0;-- if (JS_VALUE_GET_TAG(excluded) == JS_TAG_OBJECT)- pexcl = JS_VALUE_GET_OBJ(excluded);-- p = JS_VALUE_GET_OBJ(source);- if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,- JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK |- JS_GPN_ENUM_ONLY))- return -1;-- flags = JS_PROP_C_W_E;-- for (i = 0; i < tab_atom_count; i++) {- if (pexcl) {- ret = JS_GetOwnPropertyInternal(ctx, NULL, pexcl, tab_atom[i].atom);- if (ret) {- if (ret < 0)- break;- ret = 0;- continue;- }- }- ret = -1;- val = JS_GetProperty(ctx, source, tab_atom[i].atom);- if (JS_IsException(val))- break;- if (setprop)- ret = JS_SetProperty(ctx, target, tab_atom[i].atom, val);- else- ret = JS_DefinePropertyValue(ctx, target, tab_atom[i].atom, val, flags);- if (ret < 0)- break;- ret = 0;- }- js_free_prop_enum(ctx, tab_atom, tab_atom_count);- return ret;-}--/* only valid inside C functions */-static JSValueConst JS_GetActiveFunction(JSContext *ctx)-{- return ctx->rt->current_stack_frame->cur_func;-}--static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf,- int var_idx, BOOL is_arg)-{- JSVarRef *var_ref;- struct list_head *el;-- list_for_each(el, &sf->var_ref_list) {- var_ref = list_entry(el, JSVarRef, header.link);- if (var_ref->var_idx == var_idx && var_ref->is_arg == is_arg) {- var_ref->header.ref_count++;- return var_ref;- }- }- /* create a new one */- var_ref = js_malloc(ctx, sizeof(JSVarRef));- if (!var_ref)- return NULL;- var_ref->header.ref_count = 1;- var_ref->is_detached = FALSE;- var_ref->is_arg = is_arg;- var_ref->var_idx = var_idx;- list_add_tail(&var_ref->header.link, &sf->var_ref_list);- if (is_arg)- var_ref->pvalue = &sf->arg_buf[var_idx];- else- var_ref->pvalue = &sf->var_buf[var_idx];- var_ref->value = JS_UNDEFINED;- return var_ref;-}--static JSValue js_closure2(JSContext *ctx, JSValue func_obj,- JSFunctionBytecode *b,- JSVarRef **cur_var_refs,- JSStackFrame *sf)-{- JSObject *p;- JSVarRef **var_refs;- int i;-- p = JS_VALUE_GET_OBJ(func_obj);- p->u.func.function_bytecode = b;- p->u.func.home_object = NULL;- p->u.func.var_refs = NULL;- if (b->closure_var_count) {- var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count);- if (!var_refs)- goto fail;- p->u.func.var_refs = var_refs;- for(i = 0; i < b->closure_var_count; i++) {- JSClosureVar *cv = &b->closure_var[i];- JSVarRef *var_ref;- if (cv->is_local) {- /* reuse the existing variable reference if it already exists */- var_ref = get_var_ref(ctx, sf, cv->var_idx, cv->is_arg);- if (!var_ref)- goto fail;- } else {- var_ref = cur_var_refs[cv->var_idx];- var_ref->header.ref_count++;- }- var_refs[i] = var_ref;- }- }- return func_obj;- fail:- /* bfunc is freed when func_obj is freed */- JS_FreeValue(ctx, func_obj);- return JS_EXCEPTION;-}--static int js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque)-{- JSValue obj, this_val;- int ret;-- this_val = JS_MKPTR(JS_TAG_OBJECT, p);- obj = JS_NewObject(ctx);- if (JS_IsException(obj))- return -1;- set_cycle_flag(ctx, obj);- set_cycle_flag(ctx, this_val);- ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_constructor,- JS_DupValue(ctx, this_val),- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);- if (JS_DefinePropertyValue(ctx, this_val, atom, obj, JS_PROP_WRITABLE) < 0 || ret < 0)- return -1;- return 0;-}--static const uint16_t func_kind_to_class_id[] = {- [JS_FUNC_NORMAL] = JS_CLASS_BYTECODE_FUNCTION,- [JS_FUNC_GENERATOR] = JS_CLASS_GENERATOR_FUNCTION,- [JS_FUNC_ASYNC] = JS_CLASS_ASYNC_FUNCTION,- [JS_FUNC_ASYNC_GENERATOR] = JS_CLASS_ASYNC_GENERATOR_FUNCTION,-};--static JSValue js_closure(JSContext *ctx, JSValue bfunc,- JSVarRef **cur_var_refs,- JSStackFrame *sf)-{- JSFunctionBytecode *b;- JSValue func_obj;- JSAtom name_atom;-- b = JS_VALUE_GET_PTR(bfunc);- func_obj = JS_NewObjectClass(ctx, func_kind_to_class_id[b->func_kind]);- if (JS_IsException(func_obj)) {- JS_FreeValue(ctx, bfunc);- return JS_EXCEPTION;- }- func_obj = js_closure2(ctx, func_obj, b, cur_var_refs, sf);- if (JS_IsException(func_obj)) {- /* bfunc has been freed */- goto fail;- }- name_atom = b->func_name;- if (name_atom == JS_ATOM_NULL)- name_atom = JS_ATOM_empty_string;- js_function_set_properties(ctx, func_obj, name_atom,- b->defined_arg_count);-- if (b->func_kind & JS_FUNC_GENERATOR) {- JSValue proto;- int proto_class_id;- /* generators have a prototype field which is used as- prototype for the generator object */- if (b->func_kind == JS_FUNC_ASYNC_GENERATOR)- proto_class_id = JS_CLASS_ASYNC_GENERATOR;- else- proto_class_id = JS_CLASS_GENERATOR;- proto = JS_NewObjectProto(ctx, ctx->class_proto[proto_class_id]);- if (JS_IsException(proto))- goto fail;- JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, proto,- JS_PROP_WRITABLE);- } else if (b->has_prototype) {- /* add the 'prototype' property: delay instantiation to avoid- creating cycles for every javascript function. The prototype- object is created on the fly when first accessed */- JS_SetConstructorBit(ctx, func_obj, TRUE);- JS_DefineAutoInitProperty(ctx, func_obj, JS_ATOM_prototype,- JS_AUTOINIT_ID_PROTOTYPE, NULL,- JS_PROP_WRITABLE);- }- return func_obj;- fail:- /* bfunc is freed when func_obj is freed */- JS_FreeValue(ctx, func_obj);- return JS_EXCEPTION;-}--#define JS_DEFINE_CLASS_HAS_HERITAGE (1 << 0)--static int js_op_define_class(JSContext *ctx, JSValue *sp,- JSAtom class_name, int class_flags,- JSVarRef **cur_var_refs,- JSStackFrame *sf, BOOL is_computed_name)-{- JSValue bfunc, parent_class, proto = JS_UNDEFINED;- JSValue ctor = JS_UNDEFINED, parent_proto = JS_UNDEFINED;- JSFunctionBytecode *b;-- parent_class = sp[-2];- bfunc = sp[-1];-- if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) {- if (JS_IsNull(parent_class)) {- parent_proto = JS_NULL;- parent_class = JS_DupValue(ctx, ctx->function_proto);- } else {- if (!JS_IsConstructor(ctx, parent_class)) {- JS_ThrowTypeError(ctx, "parent class must be constructor");- goto fail;- }- parent_proto = JS_GetProperty(ctx, parent_class, JS_ATOM_prototype);- if (JS_IsException(parent_proto))- goto fail;- if (!JS_IsNull(parent_proto) && !JS_IsObject(parent_proto)) {- JS_ThrowTypeError(ctx, "parent prototype must be an object or null");- goto fail;- }- }- } else {- /* parent_class is JS_UNDEFINED in this case */- parent_proto = JS_DupValue(ctx, ctx->class_proto[JS_CLASS_OBJECT]);- parent_class = JS_DupValue(ctx, ctx->function_proto);- }- proto = JS_NewObjectProto(ctx, parent_proto);- if (JS_IsException(proto))- goto fail;-- b = JS_VALUE_GET_PTR(bfunc);- assert(b->func_kind == JS_FUNC_NORMAL);- ctor = JS_NewObjectProtoClass(ctx, parent_class,- JS_CLASS_BYTECODE_FUNCTION);- if (JS_IsException(ctor))- goto fail;- ctor = js_closure2(ctx, ctor, b, cur_var_refs, sf);- bfunc = JS_UNDEFINED;- if (JS_IsException(ctor))- goto fail;- js_method_set_home_object(ctx, ctor, proto);- JS_SetConstructorBit(ctx, ctor, TRUE);-- JS_DefinePropertyValue(ctx, ctor, JS_ATOM_length,- JS_NewInt32(ctx, b->defined_arg_count),- JS_PROP_CONFIGURABLE);-- if (is_computed_name) {- if (JS_DefineObjectNameComputed(ctx, ctor, sp[-3],- JS_PROP_CONFIGURABLE) < 0)- goto fail;- } else {- if (JS_DefineObjectName(ctx, ctor, class_name, JS_PROP_CONFIGURABLE) < 0)- goto fail;- }-- /* the constructor property must be first. It can be overriden by- computed property names */- if (JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor,- JS_DupValue(ctx, ctor),- JS_PROP_CONFIGURABLE |- JS_PROP_WRITABLE | JS_PROP_THROW) < 0)- goto fail;- /* set the prototype property */- if (JS_DefinePropertyValue(ctx, ctor, JS_ATOM_prototype,- JS_DupValue(ctx, proto), JS_PROP_THROW) < 0)- goto fail;- set_cycle_flag(ctx, ctor);- set_cycle_flag(ctx, proto);-- JS_FreeValue(ctx, parent_proto);- JS_FreeValue(ctx, parent_class);-- sp[-2] = ctor;- sp[-1] = proto;- return 0;- fail:- JS_FreeValue(ctx, parent_class);- JS_FreeValue(ctx, parent_proto);- JS_FreeValue(ctx, bfunc);- JS_FreeValue(ctx, proto);- JS_FreeValue(ctx, ctor);- sp[-2] = JS_UNDEFINED;- sp[-1] = JS_UNDEFINED;- return -1;-}--static void close_var_refs(JSRuntime *rt, JSStackFrame *sf)-{- struct list_head *el, *el1;- JSVarRef *var_ref;- int var_idx;-- list_for_each_safe(el, el1, &sf->var_ref_list) {- var_ref = list_entry(el, JSVarRef, header.link);- var_idx = var_ref->var_idx;- if (var_ref->is_arg)- var_ref->value = JS_DupValueRT(rt, sf->arg_buf[var_idx]);- else- var_ref->value = JS_DupValueRT(rt, sf->var_buf[var_idx]);- var_ref->pvalue = &var_ref->value;- /* the reference is no longer to a local variable */- var_ref->is_detached = TRUE;- add_gc_object(rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);- }-}--static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int idx, int is_arg)-{- struct list_head *el, *el1;- JSVarRef *var_ref;- int var_idx = idx;-- list_for_each_safe(el, el1, &sf->var_ref_list) {- var_ref = list_entry(el, JSVarRef, header.link);- if (var_idx == var_ref->var_idx && var_ref->is_arg == is_arg) {- var_ref->value = JS_DupValue(ctx, sf->var_buf[var_idx]);- var_ref->pvalue = &var_ref->value;- list_del(&var_ref->header.link);- /* the reference is no longer to a local variable */- var_ref->is_detached = TRUE;- add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);- }- }-}--#define JS_CALL_FLAG_COPY_ARGV (1 << 1)-#define JS_CALL_FLAG_GENERATOR (1 << 2)--static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,- JSValueConst this_obj,- int argc, JSValueConst *argv, int flags)-{- JSRuntime *rt = ctx->rt;- JSCFunctionType func;- JSObject *p;- JSStackFrame sf_s, *sf = &sf_s, *prev_sf;- JSValue ret_val;- JSValueConst *arg_buf;- int arg_count, i;- JSCFunctionEnum cproto;-- p = JS_VALUE_GET_OBJ(func_obj);- cproto = p->u.cfunc.cproto;- arg_count = p->u.cfunc.length;-- /* better to always check stack overflow */- if (js_check_stack_overflow(rt, sizeof(arg_buf[0]) * arg_count))- return JS_ThrowStackOverflow(ctx);-- prev_sf = rt->current_stack_frame;- sf->prev_frame = prev_sf;- rt->current_stack_frame = sf;- ctx = p->u.cfunc.realm; /* change the current realm */- -#ifdef CONFIG_BIGNUM- /* we only propagate the bignum mode as some runtime functions- test it */- if (prev_sf)- sf->js_mode = prev_sf->js_mode & JS_MODE_MATH;- else- sf->js_mode = 0;-#else- sf->js_mode = 0;-#endif- sf->cur_func = (JSValue)func_obj;- sf->arg_count = argc;- arg_buf = argv;-- if (unlikely(argc < arg_count)) {- /* ensure that at least argc_count arguments are readable */- arg_buf = alloca(sizeof(arg_buf[0]) * arg_count);- for(i = 0; i < argc; i++)- arg_buf[i] = argv[i];- for(i = argc; i < arg_count; i++)- arg_buf[i] = JS_UNDEFINED;- sf->arg_count = arg_count;- }- sf->arg_buf = (JSValue*)arg_buf;-- func = p->u.cfunc.c_function;- switch(cproto) {- case JS_CFUNC_constructor:- case JS_CFUNC_constructor_or_func:- if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) {- if (cproto == JS_CFUNC_constructor) {- not_a_constructor:- ret_val = JS_ThrowTypeError(ctx, "must be called with new");- break;- } else {- this_obj = JS_UNDEFINED;- }- }- /* here this_obj is new_target */- /* fall thru */- case JS_CFUNC_generic:- ret_val = func.generic(ctx, this_obj, argc, arg_buf);- break;- case JS_CFUNC_constructor_magic:- case JS_CFUNC_constructor_or_func_magic:- if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) {- if (cproto == JS_CFUNC_constructor_magic) {- goto not_a_constructor;- } else {- this_obj = JS_UNDEFINED;- }- }- /* fall thru */- case JS_CFUNC_generic_magic:- ret_val = func.generic_magic(ctx, this_obj, argc, arg_buf,- p->u.cfunc.magic);- break;- case JS_CFUNC_getter:- ret_val = func.getter(ctx, this_obj);- break;- case JS_CFUNC_setter:- ret_val = func.setter(ctx, this_obj, arg_buf[0]);- break;- case JS_CFUNC_getter_magic:- ret_val = func.getter_magic(ctx, this_obj, p->u.cfunc.magic);- break;- case JS_CFUNC_setter_magic:- ret_val = func.setter_magic(ctx, this_obj, arg_buf[0], p->u.cfunc.magic);- break;- case JS_CFUNC_f_f:- {- double d1;-- if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) {- ret_val = JS_EXCEPTION;- break;- }- ret_val = JS_NewFloat64(ctx, func.f_f(d1));- }- break;- case JS_CFUNC_f_f_f:- {- double d1, d2;-- if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) {- ret_val = JS_EXCEPTION;- break;- }- if (unlikely(JS_ToFloat64(ctx, &d2, arg_buf[1]))) {- ret_val = JS_EXCEPTION;- break;- }- ret_val = JS_NewFloat64(ctx, func.f_f_f(d1, d2));- }- break;- case JS_CFUNC_iterator_next:- {- int done;- ret_val = func.iterator_next(ctx, this_obj, argc, arg_buf,- &done, p->u.cfunc.magic);- if (!JS_IsException(ret_val) && done != 2) {- ret_val = js_create_iterator_result(ctx, ret_val, done);- }- }- break;- default:- abort();- }-- rt->current_stack_frame = sf->prev_frame;- return ret_val;-}--static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj,- JSValueConst this_obj,- int argc, JSValueConst *argv, int flags)-{- JSObject *p;- JSBoundFunction *bf;- JSValueConst *arg_buf, new_target;- int arg_count, i;-- p = JS_VALUE_GET_OBJ(func_obj);- bf = p->u.bound_function;- arg_count = bf->argc + argc;- if (js_check_stack_overflow(ctx->rt, sizeof(JSValue) * arg_count))- return JS_ThrowStackOverflow(ctx);- arg_buf = alloca(sizeof(JSValue) * arg_count);- for(i = 0; i < bf->argc; i++) {- arg_buf[i] = bf->argv[i];- }- for(i = 0; i < argc; i++) {- arg_buf[bf->argc + i] = argv[i];- }- if (flags & JS_CALL_FLAG_CONSTRUCTOR) {- new_target = this_obj;- if (js_same_value(ctx, func_obj, new_target))- new_target = bf->func_obj;- return JS_CallConstructor2(ctx, bf->func_obj, new_target,- arg_count, arg_buf);- } else {- return JS_Call(ctx, bf->func_obj, bf->this_val,- arg_count, arg_buf);- }-}--/* argument of OP_special_object */-typedef enum {- OP_SPECIAL_OBJECT_ARGUMENTS,- OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS,- OP_SPECIAL_OBJECT_THIS_FUNC,- OP_SPECIAL_OBJECT_NEW_TARGET,- OP_SPECIAL_OBJECT_HOME_OBJECT,- OP_SPECIAL_OBJECT_VAR_OBJECT,- OP_SPECIAL_OBJECT_IMPORT_META,-} OPSpecialObjectEnum;--#define FUNC_RET_AWAIT 0-#define FUNC_RET_YIELD 1-#define FUNC_RET_YIELD_STAR 2--/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */-static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,- JSValueConst this_obj, JSValueConst new_target,- int argc, JSValue *argv, int flags)-{- JSRuntime *rt = caller_ctx->rt;- JSContext *ctx;- JSObject *p;- JSFunctionBytecode *b;- JSStackFrame sf_s, *sf = &sf_s;- const uint8_t *pc;- int opcode, arg_allocated_size, i;- JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, *sp, ret_val, *pval;- JSVarRef **var_refs;- size_t alloca_size;--#if !DIRECT_DISPATCH-#define SWITCH(pc) switch (opcode = *pc++)-#define CASE(op) case op-#define DEFAULT default-#define BREAK break-#else- static const void * const dispatch_table[256] = {-#define DEF(id, size, n_pop, n_push, f) && case_OP_ ## id,-#if SHORT_OPCODES-#define def(id, size, n_pop, n_push, f)-#else -#define def(id, size, n_pop, n_push, f) && case_default,-#endif-#include "quickjs-opcode.h"- [ OP_COUNT ... 255 ] = &&case_default- };-#define SWITCH(pc) goto *dispatch_table[opcode = *pc++];-#define CASE(op) case_ ## op-#define DEFAULT case_default-#define BREAK SWITCH(pc)-#endif-- if (js_poll_interrupts(caller_ctx))- return JS_EXCEPTION;- if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) {- if (flags & JS_CALL_FLAG_GENERATOR) {- JSAsyncFunctionState *s = JS_VALUE_GET_PTR(func_obj);- /* func_obj get contains a pointer to JSFuncAsyncState */- /* the stack frame is already allocated */- sf = &s->frame;- p = JS_VALUE_GET_OBJ(sf->cur_func);- b = p->u.func.function_bytecode;- ctx = b->realm;- var_refs = p->u.func.var_refs;- local_buf = arg_buf = sf->arg_buf;- var_buf = sf->var_buf;- stack_buf = sf->var_buf + b->var_count;- sp = sf->cur_sp;- sf->cur_sp = NULL; /* cur_sp is NULL if the function is running */- pc = sf->cur_pc;- sf->prev_frame = rt->current_stack_frame;- rt->current_stack_frame = sf;- if (s->throw_flag)- goto exception;- else- goto restart;- } else {- goto not_a_function;- }- }- p = JS_VALUE_GET_OBJ(func_obj);- if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) {- JSClassCall *call_func;- call_func = rt->class_array[p->class_id].call;- if (!call_func) {- not_a_function:- return JS_ThrowTypeError(caller_ctx, "not a function");- }- return call_func(caller_ctx, func_obj, this_obj, argc,- (JSValueConst *)argv, flags);- }- b = p->u.func.function_bytecode;-- if (unlikely(argc < b->arg_count || (flags & JS_CALL_FLAG_COPY_ARGV))) {- arg_allocated_size = b->arg_count;- } else {- arg_allocated_size = 0;- }-- alloca_size = sizeof(JSValue) * (arg_allocated_size + b->var_count +- b->stack_size);- if (js_check_stack_overflow(rt, alloca_size))- return JS_ThrowStackOverflow(caller_ctx);-- sf->js_mode = b->js_mode;- arg_buf = argv;- sf->arg_count = argc;- sf->cur_func = (JSValue)func_obj;- init_list_head(&sf->var_ref_list);- var_refs = p->u.func.var_refs;-- local_buf = alloca(alloca_size);- if (unlikely(arg_allocated_size)) {- int n = min_int(argc, b->arg_count);- arg_buf = local_buf;- for(i = 0; i < n; i++)- arg_buf[i] = JS_DupValue(caller_ctx, argv[i]);- for(; i < b->arg_count; i++)- arg_buf[i] = JS_UNDEFINED;- sf->arg_count = b->arg_count;- }- var_buf = local_buf + arg_allocated_size;- sf->var_buf = var_buf;- sf->arg_buf = arg_buf;-- for(i = 0; i < b->var_count; i++)- var_buf[i] = JS_UNDEFINED;-- stack_buf = var_buf + b->var_count;- sp = stack_buf;- pc = b->byte_code_buf;- sf->prev_frame = rt->current_stack_frame;- rt->current_stack_frame = sf;- ctx = b->realm; /* set the current realm */- - restart:- for(;;) {- int call_argc;- JSValue *call_argv;-- SWITCH(pc) {- CASE(OP_push_i32):- *sp++ = JS_NewInt32(ctx, get_u32(pc));- pc += 4;- BREAK;- CASE(OP_push_const):- *sp++ = JS_DupValue(ctx, b->cpool[get_u32(pc)]);- pc += 4;- BREAK;-#if SHORT_OPCODES- CASE(OP_push_minus1):- CASE(OP_push_0):- CASE(OP_push_1):- CASE(OP_push_2):- CASE(OP_push_3):- CASE(OP_push_4):- CASE(OP_push_5):- CASE(OP_push_6):- CASE(OP_push_7):- *sp++ = JS_NewInt32(ctx, opcode - OP_push_0);- BREAK;- CASE(OP_push_i8):- *sp++ = JS_NewInt32(ctx, get_i8(pc));- pc += 1;- BREAK;- CASE(OP_push_i16):- *sp++ = JS_NewInt32(ctx, get_i16(pc));- pc += 2;- BREAK;- CASE(OP_push_const8):- *sp++ = JS_DupValue(ctx, b->cpool[*pc++]);- BREAK;- CASE(OP_fclosure8):- *sp++ = js_closure(ctx, JS_DupValue(ctx, b->cpool[*pc++]), var_refs, sf);- if (unlikely(JS_IsException(sp[-1])))- goto exception;- BREAK;- CASE(OP_push_empty_string):- *sp++ = JS_AtomToString(ctx, JS_ATOM_empty_string);- BREAK;- CASE(OP_get_length):- {- JSValue val;-- val = JS_GetProperty(ctx, sp[-1], JS_ATOM_length);- if (unlikely(JS_IsException(val)))- goto exception;- JS_FreeValue(ctx, sp[-1]);- sp[-1] = val;- }- BREAK;-#endif- CASE(OP_push_atom_value):- *sp++ = JS_AtomToValue(ctx, get_u32(pc));- pc += 4;- BREAK;- CASE(OP_undefined):- *sp++ = JS_UNDEFINED;- BREAK;- CASE(OP_null):- *sp++ = JS_NULL;- BREAK;- CASE(OP_push_this):- /* OP_push_this is only called at the start of a function */- {- JSValue val;- if (!(b->js_mode & JS_MODE_STRICT)) {- uint32_t tag = JS_VALUE_GET_TAG(this_obj);- if (likely(tag == JS_TAG_OBJECT))- goto normal_this;- if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) {- val = JS_DupValue(ctx, ctx->global_obj);- } else {- val = JS_ToObject(ctx, this_obj);- if (JS_IsException(val))- goto exception;- }- } else {- normal_this:- val = JS_DupValue(ctx, this_obj);- }- *sp++ = val;- }- BREAK;- CASE(OP_push_false):- *sp++ = JS_FALSE;- BREAK;- CASE(OP_push_true):- *sp++ = JS_TRUE;- BREAK;- CASE(OP_object):- *sp++ = JS_NewObject(ctx);- if (unlikely(JS_IsException(sp[-1])))- goto exception;- BREAK;- CASE(OP_special_object):- {- int arg = *pc++;- switch(arg) {- case OP_SPECIAL_OBJECT_ARGUMENTS:- *sp++ = js_build_arguments(ctx, argc, (JSValueConst *)argv);- if (unlikely(JS_IsException(sp[-1])))- goto exception;- break;- case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS:- *sp++ = js_build_mapped_arguments(ctx, argc, (JSValueConst *)argv,- sf, min_int(argc, b->arg_count));- if (unlikely(JS_IsException(sp[-1])))- goto exception;- break;- case OP_SPECIAL_OBJECT_THIS_FUNC:- *sp++ = JS_DupValue(ctx, sf->cur_func);- break;- case OP_SPECIAL_OBJECT_NEW_TARGET:- *sp++ = JS_DupValue(ctx, new_target);- break;- case OP_SPECIAL_OBJECT_HOME_OBJECT:- {- JSObject *p1;- p1 = p->u.func.home_object;- if (unlikely(!p1))- *sp++ = JS_UNDEFINED;- else- *sp++ = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));- }- break;- case OP_SPECIAL_OBJECT_VAR_OBJECT:- *sp++ = JS_NewObjectProto(ctx, JS_NULL);- if (unlikely(JS_IsException(sp[-1])))- goto exception;- break;- case OP_SPECIAL_OBJECT_IMPORT_META:- *sp++ = js_import_meta(ctx);- if (unlikely(JS_IsException(sp[-1])))- goto exception;- break;- default:- abort();- }- }- BREAK;- CASE(OP_rest):- {- int first = get_u16(pc);- pc += 2;- *sp++ = js_build_rest(ctx, first, argc, (JSValueConst *)argv);- if (unlikely(JS_IsException(sp[-1])))- goto exception;- }- BREAK;-- CASE(OP_drop):- JS_FreeValue(ctx, sp[-1]);- sp--;- BREAK;- CASE(OP_nip):- JS_FreeValue(ctx, sp[-2]);- sp[-2] = sp[-1];- sp--;- BREAK;- CASE(OP_nip1): /* a b c -> b c */- JS_FreeValue(ctx, sp[-3]);- sp[-3] = sp[-2];- sp[-2] = sp[-1];- sp--;- BREAK;- CASE(OP_dup):- sp[0] = JS_DupValue(ctx, sp[-1]);- sp++;- BREAK;- CASE(OP_dup2): /* a b -> a b a b */- sp[0] = JS_DupValue(ctx, sp[-2]);- sp[1] = JS_DupValue(ctx, sp[-1]);- sp += 2;- BREAK;- CASE(OP_dup3): /* a b c -> a b c a b c */- sp[0] = JS_DupValue(ctx, sp[-3]);- sp[1] = JS_DupValue(ctx, sp[-2]);- sp[2] = JS_DupValue(ctx, sp[-1]);- sp += 3;- BREAK;- CASE(OP_dup1): /* a b -> a a b */- sp[0] = sp[-1];- sp[-1] = JS_DupValue(ctx, sp[-2]);- sp++;- BREAK;- CASE(OP_insert2): /* obj a -> a obj a (dup_x1) */- sp[0] = sp[-1];- sp[-1] = sp[-2];- sp[-2] = JS_DupValue(ctx, sp[0]);- sp++;- BREAK;- CASE(OP_insert3): /* obj prop a -> a obj prop a (dup_x2) */- sp[0] = sp[-1];- sp[-1] = sp[-2];- sp[-2] = sp[-3];- sp[-3] = JS_DupValue(ctx, sp[0]);- sp++;- BREAK;- CASE(OP_insert4): /* this obj prop a -> a this obj prop a */- sp[0] = sp[-1];- sp[-1] = sp[-2];- sp[-2] = sp[-3];- sp[-3] = sp[-4];- sp[-4] = JS_DupValue(ctx, sp[0]);- sp++;- BREAK;- CASE(OP_perm3): /* obj a b -> a obj b (213) */- {- JSValue tmp;- tmp = sp[-2];- sp[-2] = sp[-3];- sp[-3] = tmp;- }- BREAK;- CASE(OP_rot3l): /* x a b -> a b x (231) */- {- JSValue tmp;- tmp = sp[-3];- sp[-3] = sp[-2];- sp[-2] = sp[-1];- sp[-1] = tmp;- }- BREAK;- CASE(OP_rot4l): /* x a b c -> a b c x */- {- JSValue tmp;- tmp = sp[-4];- sp[-4] = sp[-3];- sp[-3] = sp[-2];- sp[-2] = sp[-1];- sp[-1] = tmp;- }- BREAK;- CASE(OP_rot5l): /* x a b c d -> a b c d x */- {- JSValue tmp;- tmp = sp[-5];- sp[-5] = sp[-4];- sp[-4] = sp[-3];- sp[-3] = sp[-2];- sp[-2] = sp[-1];- sp[-1] = tmp;- }- BREAK;- CASE(OP_rot3r): /* a b x -> x a b (312) */- {- JSValue tmp;- tmp = sp[-1];- sp[-1] = sp[-2];- sp[-2] = sp[-3];- sp[-3] = tmp;- }- BREAK;- CASE(OP_perm4): /* obj prop a b -> a obj prop b */- {- JSValue tmp;- tmp = sp[-2];- sp[-2] = sp[-3];- sp[-3] = sp[-4];- sp[-4] = tmp;- }- BREAK;- CASE(OP_perm5): /* this obj prop a b -> a this obj prop b */- {- JSValue tmp;- tmp = sp[-2];- sp[-2] = sp[-3];- sp[-3] = sp[-4];- sp[-4] = sp[-5];- sp[-5] = tmp;- }- BREAK;- CASE(OP_swap): /* a b -> b a */- {- JSValue tmp;- tmp = sp[-2];- sp[-2] = sp[-1];- sp[-1] = tmp;- }- BREAK;- CASE(OP_swap2): /* a b c d -> c d a b */- {- JSValue tmp1, tmp2;- tmp1 = sp[-4];- tmp2 = sp[-3];- sp[-4] = sp[-2];- sp[-3] = sp[-1];- sp[-2] = tmp1;- sp[-1] = tmp2;- }- BREAK;-- CASE(OP_fclosure):- {- JSValue bfunc = JS_DupValue(ctx, b->cpool[get_u32(pc)]);- pc += 4;- *sp++ = js_closure(ctx, bfunc, var_refs, sf);- if (unlikely(JS_IsException(sp[-1])))- goto exception;- }- BREAK;-#if SHORT_OPCODES- CASE(OP_call0):- CASE(OP_call1):- CASE(OP_call2):- CASE(OP_call3):- call_argc = opcode - OP_call0;- goto has_call_argc;-#endif- CASE(OP_call):- CASE(OP_tail_call):- {- call_argc = get_u16(pc);- pc += 2;- goto has_call_argc;- has_call_argc:- call_argv = sp - call_argc;- sf->cur_pc = pc;- ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED,- JS_UNDEFINED, call_argc, call_argv, 0);- if (unlikely(JS_IsException(ret_val)))- goto exception;- if (opcode == OP_tail_call)- goto done;- for(i = -1; i < call_argc; i++)- JS_FreeValue(ctx, call_argv[i]);- sp -= call_argc + 1;- *sp++ = ret_val;- }- BREAK;- CASE(OP_call_constructor):- {- call_argc = get_u16(pc);- pc += 2;- call_argv = sp - call_argc;- sf->cur_pc = pc;- ret_val = JS_CallConstructorInternal(ctx, call_argv[-2],- call_argv[-1],- call_argc, call_argv, 0);- if (unlikely(JS_IsException(ret_val)))- goto exception;- for(i = -2; i < call_argc; i++)- JS_FreeValue(ctx, call_argv[i]);- sp -= call_argc + 2;- *sp++ = ret_val;- }- BREAK;- CASE(OP_call_method):- CASE(OP_tail_call_method):- {- call_argc = get_u16(pc);- pc += 2;- call_argv = sp - call_argc;- sf->cur_pc = pc;- ret_val = JS_CallInternal(ctx, call_argv[-1], call_argv[-2],- JS_UNDEFINED, call_argc, call_argv, 0);- if (unlikely(JS_IsException(ret_val)))- goto exception;- if (opcode == OP_tail_call_method)- goto done;- for(i = -2; i < call_argc; i++)- JS_FreeValue(ctx, call_argv[i]);- sp -= call_argc + 2;- *sp++ = ret_val;- }- BREAK;- CASE(OP_array_from):- {- int i, ret;-- call_argc = get_u16(pc);- pc += 2;- ret_val = JS_NewArray(ctx);- if (unlikely(JS_IsException(ret_val)))- goto exception;- call_argv = sp - call_argc;- for(i = 0; i < call_argc; i++) {- ret = JS_DefinePropertyValue(ctx, ret_val, __JS_AtomFromUInt32(i), call_argv[i],- JS_PROP_C_W_E | JS_PROP_THROW);- call_argv[i] = JS_UNDEFINED;- if (ret < 0) {- JS_FreeValue(ctx, ret_val);- goto exception;- }- }- sp -= call_argc;- *sp++ = ret_val;- }- BREAK;-- CASE(OP_apply):- {- int magic;- magic = get_u16(pc);- pc += 2;-- ret_val = js_function_apply(ctx, sp[-3], 2, (JSValueConst *)&sp[-2], magic);- if (unlikely(JS_IsException(ret_val)))- goto exception;- JS_FreeValue(ctx, sp[-3]);- JS_FreeValue(ctx, sp[-2]);- JS_FreeValue(ctx, sp[-1]);- sp -= 3;- *sp++ = ret_val;- }- BREAK;- CASE(OP_return):- ret_val = *--sp;- goto done;- CASE(OP_return_undef):- ret_val = JS_UNDEFINED;- goto done;-- CASE(OP_check_ctor_return):- /* return TRUE if 'this' should be returned */- if (!JS_IsObject(sp[-1])) {- if (!JS_IsUndefined(sp[-1])) {- JS_ThrowTypeError(caller_ctx, "derived class constructor must return an object or undefined");- goto exception;- }- sp[0] = JS_TRUE;- } else {- sp[0] = JS_FALSE;- }- sp++;- BREAK;- CASE(OP_check_ctor):- if (JS_IsUndefined(new_target)) {- JS_ThrowTypeError(caller_ctx, "class constructors must be invoked with 'new'");- goto exception;- }- BREAK;- CASE(OP_check_brand):- if (JS_CheckBrand(ctx, sp[-2], sp[-1]) < 0)- goto exception;- BREAK;- CASE(OP_add_brand):- if (JS_AddBrand(ctx, sp[-2], sp[-1]) < 0)- goto exception;- JS_FreeValue(ctx, sp[-2]);- JS_FreeValue(ctx, sp[-1]);- sp -= 2;- BREAK;- - CASE(OP_throw):- JS_Throw(ctx, *--sp);- goto exception;-- CASE(OP_throw_var):-#define JS_THROW_VAR_RO 0-#define JS_THROW_VAR_REDECL 1-#define JS_THROW_VAR_UNINITIALIZED 2-#define JS_THROW_VAR_DELETE_SUPER 3- {- JSAtom atom;- int type;- atom = get_u32(pc);- type = pc[4];- pc += 5;- if (type == JS_THROW_VAR_RO)- JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, atom);- else- if (type == JS_THROW_VAR_REDECL)- JS_ThrowSyntaxErrorVarRedeclaration(ctx, atom);- else- if (type == JS_THROW_VAR_UNINITIALIZED)- JS_ThrowReferenceErrorUninitialized(ctx, atom);- else- if (type == JS_THROW_VAR_DELETE_SUPER)- JS_ThrowReferenceError(ctx, "unsupported reference to 'super'");- else- JS_ThrowInternalError(ctx, "invalid throw var type %d", type);- }- goto exception;-- CASE(OP_eval):- {- JSValueConst obj;- int scope_idx;- call_argc = get_u16(pc);- scope_idx = get_u16(pc + 2) - 1;- pc += 4;- call_argv = sp - call_argc;- sf->cur_pc = pc;- if (js_same_value(ctx, call_argv[-1], ctx->eval_obj)) {- if (call_argc >= 1)- obj = call_argv[0];- else- obj = JS_UNDEFINED;- ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj,- JS_EVAL_TYPE_DIRECT, scope_idx);- } else {- ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED,- JS_UNDEFINED, call_argc, call_argv, 0);- }- if (unlikely(JS_IsException(ret_val)))- goto exception;- for(i = -1; i < call_argc; i++)- JS_FreeValue(ctx, call_argv[i]);- sp -= call_argc + 1;- *sp++ = ret_val;- }- BREAK;- /* could merge with OP_apply */- CASE(OP_apply_eval):- {- int scope_idx;- uint32_t len;- JSValue *tab;- JSValueConst obj;-- scope_idx = get_u16(pc) - 1;- pc += 2;- tab = build_arg_list(ctx, &len, sp[-1]);- if (!tab)- goto exception;- if (js_same_value(ctx, sp[-2], ctx->eval_obj)) {- if (len >= 1)- obj = tab[0];- else- obj = JS_UNDEFINED;- ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj,- JS_EVAL_TYPE_DIRECT, scope_idx);- } else {- ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len,- (JSValueConst *)tab);- }- free_arg_list(ctx, tab, len);- if (unlikely(JS_IsException(ret_val)))- goto exception;- JS_FreeValue(ctx, sp[-2]);- JS_FreeValue(ctx, sp[-1]);- sp -= 2;- *sp++ = ret_val;- }- BREAK;-- CASE(OP_regexp):- {- sp[-2] = js_regexp_constructor_internal(ctx, JS_UNDEFINED,- sp[-2], sp[-1]);- sp--;- }- BREAK;-- CASE(OP_get_super):- {- JSValue proto;- proto = JS_GetPrototype(ctx, sp[-1]);- if (JS_IsException(proto))- goto exception;- JS_FreeValue(ctx, sp[-1]);- sp[-1] = proto;- }- BREAK;-- CASE(OP_import):- {- JSValue val;- val = js_dynamic_import(ctx, sp[-1]);- if (JS_IsException(val))- goto exception;- JS_FreeValue(ctx, sp[-1]);- sp[-1] = val;- }- BREAK;-- CASE(OP_check_var):- {- int ret;- JSAtom atom;- atom = get_u32(pc);- pc += 4;-- ret = JS_CheckGlobalVar(ctx, atom);- if (ret < 0)- goto exception;- *sp++ = JS_NewBool(ctx, ret);- }- BREAK;-- CASE(OP_get_var_undef):- CASE(OP_get_var):- {- JSValue val;- JSAtom atom;- atom = get_u32(pc);- pc += 4;-- val = JS_GetGlobalVar(ctx, atom, opcode - OP_get_var_undef);- if (unlikely(JS_IsException(val)))- goto exception;- *sp++ = val;- }- BREAK;-- CASE(OP_put_var):- CASE(OP_put_var_init):- {- int ret;- JSAtom atom;- atom = get_u32(pc);- pc += 4;-- ret = JS_SetGlobalVar(ctx, atom, sp[-1], opcode - OP_put_var);- sp--;- if (unlikely(ret < 0))- goto exception;- }- BREAK;-- CASE(OP_put_var_strict):- {- int ret;- JSAtom atom;- atom = get_u32(pc);- pc += 4;-- /* sp[-2] is JS_TRUE or JS_FALSE */- if (unlikely(!JS_VALUE_GET_INT(sp[-2]))) {- JS_ThrowReferenceErrorNotDefined(ctx, atom);- goto exception;- }- ret = JS_SetGlobalVar(ctx, atom, sp[-1], 2);- sp -= 2;- if (unlikely(ret < 0))- goto exception;- }- BREAK;-- CASE(OP_check_define_var):- {- JSAtom atom;- int flags;- atom = get_u32(pc);- flags = pc[4];- pc += 5;- if (JS_CheckDefineGlobalVar(ctx, atom, flags))- goto exception;- }- BREAK;- CASE(OP_define_var):- {- JSAtom atom;- int flags;- atom = get_u32(pc);- flags = pc[4];- pc += 5;- if (JS_DefineGlobalVar(ctx, atom, flags))- goto exception;- }- BREAK;- CASE(OP_define_func):- {- JSAtom atom;- int flags;- atom = get_u32(pc);- flags = pc[4];- pc += 5;- if (JS_DefineGlobalFunction(ctx, atom, sp[-1], flags))- goto exception;- JS_FreeValue(ctx, sp[-1]);- sp--;- }- BREAK;-- CASE(OP_get_loc):- {- int idx;- idx = get_u16(pc);- pc += 2;- sp[0] = JS_DupValue(ctx, var_buf[idx]);- sp++;- }- BREAK;- CASE(OP_put_loc):- {- int idx;- idx = get_u16(pc);- pc += 2;- set_value(ctx, &var_buf[idx], sp[-1]);- sp--;- }- BREAK;- CASE(OP_set_loc):- {- int idx;- idx = get_u16(pc);- pc += 2;- set_value(ctx, &var_buf[idx], JS_DupValue(ctx, sp[-1]));- }- BREAK;- CASE(OP_get_arg):- {- int idx;- idx = get_u16(pc);- pc += 2;- sp[0] = JS_DupValue(ctx, arg_buf[idx]);- sp++;- }- BREAK;- CASE(OP_put_arg):- {- int idx;- idx = get_u16(pc);- pc += 2;- set_value(ctx, &arg_buf[idx], sp[-1]);- sp--;- }- BREAK;- CASE(OP_set_arg):- {- int idx;- idx = get_u16(pc);- pc += 2;- set_value(ctx, &arg_buf[idx], JS_DupValue(ctx, sp[-1]));- }- BREAK;--#if SHORT_OPCODES- CASE(OP_get_loc8): *sp++ = JS_DupValue(ctx, var_buf[*pc++]); BREAK;- CASE(OP_put_loc8): set_value(ctx, &var_buf[*pc++], *--sp); BREAK;- CASE(OP_set_loc8): set_value(ctx, &var_buf[*pc++], JS_DupValue(ctx, sp[-1])); BREAK;-- CASE(OP_get_loc0): *sp++ = JS_DupValue(ctx, var_buf[0]); BREAK;- CASE(OP_get_loc1): *sp++ = JS_DupValue(ctx, var_buf[1]); BREAK;- CASE(OP_get_loc2): *sp++ = JS_DupValue(ctx, var_buf[2]); BREAK;- CASE(OP_get_loc3): *sp++ = JS_DupValue(ctx, var_buf[3]); BREAK;- CASE(OP_put_loc0): set_value(ctx, &var_buf[0], *--sp); BREAK;- CASE(OP_put_loc1): set_value(ctx, &var_buf[1], *--sp); BREAK;- CASE(OP_put_loc2): set_value(ctx, &var_buf[2], *--sp); BREAK;- CASE(OP_put_loc3): set_value(ctx, &var_buf[3], *--sp); BREAK;- CASE(OP_set_loc0): set_value(ctx, &var_buf[0], JS_DupValue(ctx, sp[-1])); BREAK;- CASE(OP_set_loc1): set_value(ctx, &var_buf[1], JS_DupValue(ctx, sp[-1])); BREAK;- CASE(OP_set_loc2): set_value(ctx, &var_buf[2], JS_DupValue(ctx, sp[-1])); BREAK;- CASE(OP_set_loc3): set_value(ctx, &var_buf[3], JS_DupValue(ctx, sp[-1])); BREAK;- CASE(OP_get_arg0): *sp++ = JS_DupValue(ctx, arg_buf[0]); BREAK;- CASE(OP_get_arg1): *sp++ = JS_DupValue(ctx, arg_buf[1]); BREAK;- CASE(OP_get_arg2): *sp++ = JS_DupValue(ctx, arg_buf[2]); BREAK;- CASE(OP_get_arg3): *sp++ = JS_DupValue(ctx, arg_buf[3]); BREAK;- CASE(OP_put_arg0): set_value(ctx, &arg_buf[0], *--sp); BREAK;- CASE(OP_put_arg1): set_value(ctx, &arg_buf[1], *--sp); BREAK;- CASE(OP_put_arg2): set_value(ctx, &arg_buf[2], *--sp); BREAK;- CASE(OP_put_arg3): set_value(ctx, &arg_buf[3], *--sp); BREAK;- CASE(OP_set_arg0): set_value(ctx, &arg_buf[0], JS_DupValue(ctx, sp[-1])); BREAK;- CASE(OP_set_arg1): set_value(ctx, &arg_buf[1], JS_DupValue(ctx, sp[-1])); BREAK;- CASE(OP_set_arg2): set_value(ctx, &arg_buf[2], JS_DupValue(ctx, sp[-1])); BREAK;- CASE(OP_set_arg3): set_value(ctx, &arg_buf[3], JS_DupValue(ctx, sp[-1])); BREAK;- CASE(OP_get_var_ref0): *sp++ = JS_DupValue(ctx, *var_refs[0]->pvalue); BREAK;- CASE(OP_get_var_ref1): *sp++ = JS_DupValue(ctx, *var_refs[1]->pvalue); BREAK;- CASE(OP_get_var_ref2): *sp++ = JS_DupValue(ctx, *var_refs[2]->pvalue); BREAK;- CASE(OP_get_var_ref3): *sp++ = JS_DupValue(ctx, *var_refs[3]->pvalue); BREAK;- CASE(OP_put_var_ref0): set_value(ctx, var_refs[0]->pvalue, *--sp); BREAK;- CASE(OP_put_var_ref1): set_value(ctx, var_refs[1]->pvalue, *--sp); BREAK;- CASE(OP_put_var_ref2): set_value(ctx, var_refs[2]->pvalue, *--sp); BREAK;- CASE(OP_put_var_ref3): set_value(ctx, var_refs[3]->pvalue, *--sp); BREAK;- CASE(OP_set_var_ref0): set_value(ctx, var_refs[0]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;- CASE(OP_set_var_ref1): set_value(ctx, var_refs[1]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;- CASE(OP_set_var_ref2): set_value(ctx, var_refs[2]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;- CASE(OP_set_var_ref3): set_value(ctx, var_refs[3]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;-#endif-- CASE(OP_get_var_ref):- {- int idx;- JSValue val;- idx = get_u16(pc);- pc += 2;- val = *var_refs[idx]->pvalue;- sp[0] = JS_DupValue(ctx, val);- sp++;- }- BREAK;- CASE(OP_put_var_ref):- {- int idx;- idx = get_u16(pc);- pc += 2;- set_value(ctx, var_refs[idx]->pvalue, sp[-1]);- sp--;- }- BREAK;- CASE(OP_set_var_ref):- {- int idx;- idx = get_u16(pc);- pc += 2;- set_value(ctx, var_refs[idx]->pvalue, JS_DupValue(ctx, sp[-1]));- }- BREAK;- CASE(OP_get_var_ref_check):- {- int idx;- JSValue val;- idx = get_u16(pc);- pc += 2;- val = *var_refs[idx]->pvalue;- if (unlikely(JS_IsUninitialized(val))) {- JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL);- goto exception;- }- sp[0] = JS_DupValue(ctx, val);- sp++;- }- BREAK;- CASE(OP_put_var_ref_check):- {- int idx;- idx = get_u16(pc);- pc += 2;- if (unlikely(JS_IsUninitialized(*var_refs[idx]->pvalue))) {- JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL);- goto exception;- }- set_value(ctx, var_refs[idx]->pvalue, sp[-1]);- sp--;- }- BREAK;- CASE(OP_put_var_ref_check_init):- {- int idx;- idx = get_u16(pc);- pc += 2;- if (unlikely(!JS_IsUninitialized(*var_refs[idx]->pvalue))) {- JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL);- goto exception;- }- set_value(ctx, var_refs[idx]->pvalue, sp[-1]);- sp--;- }- BREAK;- CASE(OP_set_loc_uninitialized):- {- int idx;- idx = get_u16(pc);- pc += 2;- set_value(ctx, &var_buf[idx], JS_UNINITIALIZED);- }- BREAK;- CASE(OP_get_loc_check):- {- int idx;- idx = get_u16(pc);- pc += 2;- if (unlikely(JS_IsUninitialized(var_buf[idx]))) {- JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL);- goto exception;- }- sp[0] = JS_DupValue(ctx, var_buf[idx]);- sp++;- }- BREAK;- CASE(OP_put_loc_check):- {- int idx;- idx = get_u16(pc);- pc += 2;- if (unlikely(JS_IsUninitialized(var_buf[idx]))) {- JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL);- goto exception;- }- set_value(ctx, &var_buf[idx], sp[-1]);- sp--;- }- BREAK;- CASE(OP_put_loc_check_init):- {- int idx;- idx = get_u16(pc);- pc += 2;- if (unlikely(!JS_IsUninitialized(var_buf[idx]))) {- JS_ThrowReferenceError(ctx, "'this' can be initialized only once");- goto exception;- }- set_value(ctx, &var_buf[idx], sp[-1]);- sp--;- }- BREAK;- CASE(OP_close_loc):- {- int idx;- idx = get_u16(pc);- pc += 2;- close_lexical_var(ctx, sf, idx, FALSE);- }- BREAK;-- CASE(OP_make_loc_ref):- CASE(OP_make_arg_ref):- CASE(OP_make_var_ref_ref):- {- JSVarRef *var_ref;- JSProperty *pr;- JSAtom atom;- int idx;- atom = get_u32(pc);- idx = get_u16(pc + 4);- pc += 6;- *sp++ = JS_NewObjectProto(ctx, JS_NULL);- if (unlikely(JS_IsException(sp[-1])))- goto exception;- if (opcode == OP_make_var_ref_ref) {- var_ref = var_refs[idx];- var_ref->header.ref_count++;- } else {- var_ref = get_var_ref(ctx, sf, idx, opcode == OP_make_arg_ref);- if (!var_ref)- goto exception;- }- pr = add_property(ctx, JS_VALUE_GET_OBJ(sp[-1]), atom,- JS_PROP_WRITABLE | JS_PROP_VARREF);- if (!pr) {- free_var_ref(rt, var_ref);- goto exception;- }- pr->u.var_ref = var_ref;- *sp++ = JS_AtomToValue(ctx, atom);- }- BREAK;- CASE(OP_make_var_ref):- {- JSAtom atom;- atom = get_u32(pc);- pc += 4;-- if (JS_GetGlobalVarRef(ctx, atom, sp))- goto exception;- sp += 2;- }- BREAK;-- CASE(OP_goto):- pc += (int32_t)get_u32(pc);- if (unlikely(js_poll_interrupts(ctx)))- goto exception;- BREAK;-#if SHORT_OPCODES- CASE(OP_goto16):- pc += (int16_t)get_u16(pc);- if (unlikely(js_poll_interrupts(ctx)))- goto exception;- BREAK;- CASE(OP_goto8):- pc += (int8_t)pc[0];- if (unlikely(js_poll_interrupts(ctx)))- goto exception;- BREAK;-#endif- CASE(OP_if_true):- {- int res;- JSValue op1;-- op1 = sp[-1];- pc += 4;- if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {- res = JS_VALUE_GET_INT(op1);- } else {- res = JS_ToBoolFree(ctx, op1);- }- sp--;- if (res) {- pc += (int32_t)get_u32(pc - 4) - 4;- }- if (unlikely(js_poll_interrupts(ctx)))- goto exception;- }- BREAK;- CASE(OP_if_false):- {- int res;- JSValue op1;-- op1 = sp[-1];- pc += 4;- if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {- res = JS_VALUE_GET_INT(op1);- } else {- res = JS_ToBoolFree(ctx, op1);- }- sp--;- if (!res) {- pc += (int32_t)get_u32(pc - 4) - 4;- }- if (unlikely(js_poll_interrupts(ctx)))- goto exception;- }- BREAK;-#if SHORT_OPCODES- CASE(OP_if_true8):- {- int res;- JSValue op1;-- op1 = sp[-1];- pc += 1;- if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {- res = JS_VALUE_GET_INT(op1);- } else {- res = JS_ToBoolFree(ctx, op1);- }- sp--;- if (res) {- pc += (int8_t)pc[-1] - 1;- }- if (unlikely(js_poll_interrupts(ctx)))- goto exception;- }- BREAK;- CASE(OP_if_false8):- {- int res;- JSValue op1;-- op1 = sp[-1];- pc += 1;- if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {- res = JS_VALUE_GET_INT(op1);- } else {- res = JS_ToBoolFree(ctx, op1);- }- sp--;- if (!res) {- pc += (int8_t)pc[-1] - 1;- }- if (unlikely(js_poll_interrupts(ctx)))- goto exception;- }- BREAK;-#endif- CASE(OP_catch):- {- int32_t diff;- diff = get_u32(pc);- sp[0] = JS_NewCatchOffset(ctx, pc + diff - b->byte_code_buf);- sp++;- pc += 4;- }- BREAK;- CASE(OP_gosub):- {- int32_t diff;- diff = get_u32(pc);- /* XXX: should have a different tag to avoid security flaw */- sp[0] = JS_NewInt32(ctx, pc + 4 - b->byte_code_buf);- sp++;- pc += diff;- }- BREAK;- CASE(OP_ret):- {- JSValue op1;- uint32_t pos;- op1 = sp[-1];- if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_INT))- goto ret_fail;- pos = JS_VALUE_GET_INT(op1);- if (unlikely(pos >= b->byte_code_len)) {- ret_fail:- JS_ThrowInternalError(ctx, "invalid ret value");- goto exception;- }- sp--;- pc = b->byte_code_buf + pos;- }- BREAK;-- CASE(OP_for_in_start):- if (js_for_in_start(ctx, sp))- goto exception;- BREAK;- CASE(OP_for_in_next):- if (js_for_in_next(ctx, sp))- goto exception;- sp += 2;- BREAK;- CASE(OP_for_of_start):- if (js_for_of_start(ctx, sp, FALSE))- goto exception;- sp += 1;- *sp++ = JS_NewCatchOffset(ctx, 0);- BREAK;- CASE(OP_for_of_next):- {- int offset = -3 - pc[0];- pc += 1;- if (js_for_of_next(ctx, sp, offset))- goto exception;- sp += 2;- }- BREAK;- CASE(OP_for_await_of_start):- if (js_for_of_start(ctx, sp, TRUE))- goto exception;- sp += 1;- *sp++ = JS_NewCatchOffset(ctx, 0);- BREAK;- CASE(OP_for_await_of_next):- if (js_for_await_of_next(ctx, sp))- goto exception;- sp += 1;- BREAK;- CASE(OP_iterator_get_value_done):- if (js_iterator_get_value_done(ctx, sp))- goto exception;- sp += 1;- BREAK;-- CASE(OP_iterator_close):- /* iter_obj next catch_offset -> */- sp--; /* drop the catch offset to avoid getting caught by exception */- JS_FreeValue(ctx, sp[-1]); /* drop the next method */- sp--;- if (!JS_IsUndefined(sp[-1])) {- if (JS_IteratorClose(ctx, sp[-1], FALSE))- goto exception;- JS_FreeValue(ctx, sp[-1]);- }- sp--;- BREAK;- CASE(OP_iterator_close_return):- {- JSValue ret_val;- /* iter_obj next catch_offset ... ret_val ->- ret_eval iter_obj next catch_offset */- ret_val = *--sp;- while (sp > stack_buf &&- JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_CATCH_OFFSET) {- JS_FreeValue(ctx, *--sp);- }- if (unlikely(sp < stack_buf + 3)) {- JS_ThrowInternalError(ctx, "iterator_close_return");- JS_FreeValue(ctx, ret_val);- goto exception;- }- sp[0] = sp[-1];- sp[-1] = sp[-2];- sp[-2] = sp[-3];- sp[-3] = ret_val;- sp++;- }- BREAK;-- CASE(OP_async_iterator_close):- /* iter_obj next catch_offset -> value flag */- {- JSValue ret, method;- int ret_flag;- sp--; /* remove the catch offset */- method = JS_GetProperty(ctx, sp[-2], JS_ATOM_return);- if (JS_IsException(method))- goto exception;- if (JS_IsUndefined(method) || JS_IsNull(method)) {- ret = JS_UNDEFINED;- ret_flag = TRUE;- } else {- ret = JS_CallFree(ctx, method, sp[-2], 0, NULL);- if (JS_IsException(ret))- goto exception;- if (!JS_IsObject(ret)) {- JS_FreeValue(ctx, ret);- JS_ThrowTypeErrorNotAnObject(ctx);- goto exception;- }- ret_flag = FALSE;- }- JS_FreeValue(ctx, sp[-2]);- JS_FreeValue(ctx, sp[-1]);- sp[-2] = ret;- sp[-1] = JS_NewBool(ctx, ret_flag);- }- BREAK;-- CASE(OP_async_iterator_next):- /* stack: iter_obj next catch_offset val */- {- JSValue ret;- ret = JS_Call(ctx, sp[-3], sp[-4],- 1, (JSValueConst *)(sp - 1));- if (JS_IsException(ret))- goto exception;- JS_FreeValue(ctx, sp[-1]);- sp[-1] = ret;- }- BREAK;-- CASE(OP_async_iterator_get):- /* stack: iter_obj next catch_offset val */- {- JSValue method, ret;- BOOL ret_flag;- int flags;- flags = *pc++;- /* XXX: use another opcode such as OP_throw_var */- if (flags == 2) {- JS_ThrowTypeError(ctx, "iterator does not have a throw method");- goto exception;- }- method = JS_GetProperty(ctx, sp[-4], flags ? JS_ATOM_throw : JS_ATOM_return);- if (JS_IsException(method))- goto exception;- if (JS_IsUndefined(method) || JS_IsNull(method)) {- ret_flag = TRUE;- } else {- ret = JS_CallFree(ctx, method, sp[-4],- 1, (JSValueConst *)(sp - 1));- if (JS_IsException(ret))- goto exception;- JS_FreeValue(ctx, sp[-1]);- sp[-1] = ret;- ret_flag = FALSE;- }- sp[0] = JS_NewBool(ctx, ret_flag);- sp += 1;- }- BREAK;-- CASE(OP_lnot):- {- int res;- JSValue op1;-- op1 = sp[-1];- if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {- res = JS_VALUE_GET_INT(op1) != 0;- } else {- res = JS_ToBoolFree(ctx, op1);- }- sp[-1] = JS_NewBool(ctx, !res);- }- BREAK;-- CASE(OP_get_field):- {- JSValue val;- JSAtom atom;- atom = get_u32(pc);- pc += 4;-- val = JS_GetProperty(ctx, sp[-1], atom);- if (unlikely(JS_IsException(val)))- goto exception;- JS_FreeValue(ctx, sp[-1]);- sp[-1] = val;- }- BREAK;-- CASE(OP_get_field2):- {- JSValue val;- JSAtom atom;- atom = get_u32(pc);- pc += 4;-- val = JS_GetProperty(ctx, sp[-1], atom);- if (unlikely(JS_IsException(val)))- goto exception;- *sp++ = val;- }- BREAK;-- CASE(OP_put_field):- {- int ret;- JSAtom atom;- atom = get_u32(pc);- pc += 4;-- ret = JS_SetPropertyInternal(ctx, sp[-2], atom, sp[-1],- JS_PROP_THROW_STRICT);- JS_FreeValue(ctx, sp[-2]);- sp -= 2;- if (unlikely(ret < 0))- goto exception;- }- BREAK;-- CASE(OP_private_symbol):- {- JSAtom atom;- JSValue val;- - atom = get_u32(pc);- pc += 4;- val = JS_NewSymbolFromAtom(ctx, atom, JS_ATOM_TYPE_PRIVATE);- if (JS_IsException(val))- goto exception;- *sp++ = val;- }- BREAK;- - CASE(OP_get_private_field):- {- JSValue val;-- val = JS_GetPrivateField(ctx, sp[-2], sp[-1]);- JS_FreeValue(ctx, sp[-1]);- JS_FreeValue(ctx, sp[-2]);- sp[-2] = val;- sp--;- if (unlikely(JS_IsException(val)))- goto exception;- }- BREAK;-- CASE(OP_put_private_field):- {- int ret;- ret = JS_SetPrivateField(ctx, sp[-3], sp[-1], sp[-2]);- JS_FreeValue(ctx, sp[-3]);- JS_FreeValue(ctx, sp[-1]);- sp -= 3;- if (unlikely(ret < 0))- goto exception;- }- BREAK;-- CASE(OP_define_private_field):- {- int ret;- ret = JS_DefinePrivateField(ctx, sp[-3], sp[-2], sp[-1]);- JS_FreeValue(ctx, sp[-2]);- sp -= 2;- if (unlikely(ret < 0))- goto exception;- }- BREAK;-- CASE(OP_define_field):- {- int ret;- JSAtom atom;- atom = get_u32(pc);- pc += 4;-- ret = JS_DefinePropertyValue(ctx, sp[-2], atom, sp[-1],- JS_PROP_C_W_E | JS_PROP_THROW);- sp--;- if (unlikely(ret < 0))- goto exception;- }- BREAK;-- CASE(OP_set_name):- {- int ret;- JSAtom atom;- atom = get_u32(pc);- pc += 4;-- ret = JS_DefineObjectName(ctx, sp[-1], atom, JS_PROP_CONFIGURABLE);- if (unlikely(ret < 0))- goto exception;- }- BREAK;- CASE(OP_set_name_computed):- {- int ret;- ret = JS_DefineObjectNameComputed(ctx, sp[-1], sp[-2], JS_PROP_CONFIGURABLE);- if (unlikely(ret < 0))- goto exception;- }- BREAK;- CASE(OP_set_proto):- {- JSValue proto;- proto = sp[-1];- if (JS_IsObject(proto) || JS_IsNull(proto)) {- if (JS_SetPrototypeInternal(ctx, sp[-2], proto, TRUE) < 0)- goto exception;- }- JS_FreeValue(ctx, proto);- sp--;- }- BREAK;- CASE(OP_set_home_object):- js_method_set_home_object(ctx, sp[-1], sp[-2]);- BREAK;- CASE(OP_define_method):- CASE(OP_define_method_computed):- {- JSValue getter, setter, value;- JSValueConst obj;- JSAtom atom;- int flags, ret, op_flags;- BOOL is_computed;-#define OP_DEFINE_METHOD_METHOD 0-#define OP_DEFINE_METHOD_GETTER 1-#define OP_DEFINE_METHOD_SETTER 2-#define OP_DEFINE_METHOD_ENUMERABLE 4-- is_computed = (opcode == OP_define_method_computed);- if (is_computed) {- atom = JS_ValueToAtom(ctx, sp[-2]);- if (unlikely(atom == JS_ATOM_NULL))- goto exception;- opcode += OP_define_method - OP_define_method_computed;- } else {- atom = get_u32(pc);- pc += 4;- }- op_flags = *pc++;-- obj = sp[-2 - is_computed];- flags = JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE |- JS_PROP_HAS_ENUMERABLE | JS_PROP_THROW;- if (op_flags & OP_DEFINE_METHOD_ENUMERABLE)- flags |= JS_PROP_ENUMERABLE;- op_flags &= 3;- value = JS_UNDEFINED;- getter = JS_UNDEFINED;- setter = JS_UNDEFINED;- if (op_flags == OP_DEFINE_METHOD_METHOD) {- value = sp[-1];- flags |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE;- } else if (op_flags == OP_DEFINE_METHOD_GETTER) {- getter = sp[-1];- flags |= JS_PROP_HAS_GET;- } else {- setter = sp[-1];- flags |= JS_PROP_HAS_SET;- }- ret = js_method_set_properties(ctx, sp[-1], atom, flags, obj);- if (ret >= 0) {- ret = JS_DefineProperty(ctx, obj, atom, value,- getter, setter, flags);- }- JS_FreeValue(ctx, sp[-1]);- if (is_computed) {- JS_FreeAtom(ctx, atom);- JS_FreeValue(ctx, sp[-2]);- }- sp -= 1 + is_computed;- if (unlikely(ret < 0))- goto exception;- }- BREAK;-- CASE(OP_define_class):- CASE(OP_define_class_computed):- {- int class_flags;- JSAtom atom;- - atom = get_u32(pc);- class_flags = pc[4];- pc += 5;- if (js_op_define_class(ctx, sp, atom, class_flags,- var_refs, sf,- (opcode == OP_define_class_computed)) < 0)- goto exception;- }- BREAK;-- CASE(OP_get_array_el):- {- JSValue val;-- val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]);- JS_FreeValue(ctx, sp[-2]);- sp[-2] = val;- sp--;- if (unlikely(JS_IsException(val)))- goto exception;- }- BREAK;-- CASE(OP_get_array_el2):- {- JSValue val;-- val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]);- sp[-1] = val;- if (unlikely(JS_IsException(val)))- goto exception;- }- BREAK;-- CASE(OP_get_ref_value):- {- JSValue val;- if (unlikely(JS_IsUndefined(sp[-2]))) {- JSAtom atom = JS_ValueToAtom(ctx, sp[-1]);- if (atom != JS_ATOM_NULL) {- JS_ThrowReferenceErrorNotDefined(ctx, atom);- JS_FreeAtom(ctx, atom);- }- goto exception;- }- val = JS_GetPropertyValue(ctx, sp[-2],- JS_DupValue(ctx, sp[-1]));- if (unlikely(JS_IsException(val)))- goto exception;- sp[0] = val;- sp++;- }- BREAK;-- CASE(OP_get_super_value):- {- JSValue val;- JSAtom atom;- atom = JS_ValueToAtom(ctx, sp[-1]);- if (unlikely(atom == JS_ATOM_NULL))- goto exception;- val = JS_GetPropertyInternal(ctx, sp[-2], atom, sp[-3], FALSE);- JS_FreeAtom(ctx, atom);- if (unlikely(JS_IsException(val)))- goto exception;- JS_FreeValue(ctx, sp[-1]);- JS_FreeValue(ctx, sp[-2]);- JS_FreeValue(ctx, sp[-3]);- sp[-3] = val;- sp -= 2;- }- BREAK;-- CASE(OP_put_array_el):- {- int ret;-- ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT);- JS_FreeValue(ctx, sp[-3]);- sp -= 3;- if (unlikely(ret < 0))- goto exception;- }- BREAK;-- CASE(OP_put_ref_value):- {- int ret;- if (unlikely(JS_IsUndefined(sp[-3]))) {- if (is_strict_mode(ctx)) {- JSAtom atom = JS_ValueToAtom(ctx, sp[-2]);- if (atom != JS_ATOM_NULL) {- JS_ThrowReferenceErrorNotDefined(ctx, atom);- JS_FreeAtom(ctx, atom);- }- goto exception;- } else {- sp[-3] = JS_DupValue(ctx, ctx->global_obj);- }- }- ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT);- JS_FreeValue(ctx, sp[-3]);- sp -= 3;- if (unlikely(ret < 0))- goto exception;- }- BREAK;-- CASE(OP_put_super_value):- {- int ret;- JSAtom atom;- if (JS_VALUE_GET_TAG(sp[-3]) != JS_TAG_OBJECT) {- JS_ThrowTypeErrorNotAnObject(ctx);- goto exception;- }- atom = JS_ValueToAtom(ctx, sp[-2]);- if (unlikely(atom == JS_ATOM_NULL))- goto exception;- ret = JS_SetPropertyGeneric(ctx, JS_VALUE_GET_OBJ(sp[-3]),- atom, sp[-1], sp[-4],- JS_PROP_THROW_STRICT);- JS_FreeAtom(ctx, atom);- JS_FreeValue(ctx, sp[-4]);- JS_FreeValue(ctx, sp[-3]);- JS_FreeValue(ctx, sp[-2]);- sp -= 4;- if (ret < 0)- goto exception;- }- BREAK;-- CASE(OP_define_array_el):- {- int ret;- ret = JS_DefinePropertyValueValue(ctx, sp[-3], JS_DupValue(ctx, sp[-2]), sp[-1],- JS_PROP_C_W_E | JS_PROP_THROW);- sp -= 1;- if (unlikely(ret < 0))- goto exception;- }- BREAK;-- CASE(OP_append): /* array pos enumobj -- array pos */- {- if (js_append_enumerate(ctx, sp))- goto exception;- JS_FreeValue(ctx, *--sp);- }- BREAK;-- CASE(OP_copy_data_properties): /* target source excludeList */- {- /* stack offsets (-1 based):- 2 bits for target,- 3 bits for source,- 2 bits for exclusionList */- int mask;-- mask = *pc++;- if (JS_CopyDataProperties(ctx, sp[-1 - (mask & 3)],- sp[-1 - ((mask >> 2) & 7)],- sp[-1 - ((mask >> 5) & 7)], 0))- goto exception;- }- BREAK;-- CASE(OP_add):- {- JSValue op1, op2;- op1 = sp[-2];- op2 = sp[-1];- if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {- int64_t r;- r = (int64_t)JS_VALUE_GET_INT(op1) + JS_VALUE_GET_INT(op2);- if (unlikely((int)r != r))- goto add_slow;- sp[-2] = JS_NewInt32(ctx, r);- sp--;- } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {- sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) +- JS_VALUE_GET_FLOAT64(op2));- sp--;- } else {- add_slow:- if (js_add_slow(ctx, sp))- goto exception;- sp--;- }- }- BREAK;- CASE(OP_add_loc):- {- JSValue *pv;- int idx;- idx = *pc;- pc += 1;-- pv = &var_buf[idx];- if (likely(JS_VALUE_IS_BOTH_INT(*pv, sp[-1]))) {- int64_t r;- r = (int64_t)JS_VALUE_GET_INT(*pv) +- JS_VALUE_GET_INT(sp[-1]);- if (unlikely((int)r != r))- goto add_loc_slow;- *pv = JS_NewInt32(ctx, r);- sp--;- } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING) {- JSValue op1;- op1 = sp[-1];- sp--;- op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);- if (JS_IsException(op1))- goto exception;- op1 = JS_ConcatString(ctx, JS_DupValue(ctx, *pv), op1);- if (JS_IsException(op1))- goto exception;- set_value(ctx, pv, op1);- } else {- JSValue ops[2];- add_loc_slow:- /* In case of exception, js_add_slow frees ops[0]- and ops[1], so we must duplicate *pv */- ops[0] = JS_DupValue(ctx, *pv);- ops[1] = sp[-1];- sp--;- if (js_add_slow(ctx, ops + 2))- goto exception;- set_value(ctx, pv, ops[0]);- }- }- BREAK;- CASE(OP_sub):- {- JSValue op1, op2;- op1 = sp[-2];- op2 = sp[-1];- if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {- int64_t r;- r = (int64_t)JS_VALUE_GET_INT(op1) - JS_VALUE_GET_INT(op2);- if (unlikely((int)r != r))- goto binary_arith_slow;- sp[-2] = JS_NewInt32(ctx, r);- sp--;- } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {- sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) -- JS_VALUE_GET_FLOAT64(op2));- sp--;- } else {- goto binary_arith_slow;- }- }- BREAK;- CASE(OP_mul):- {- JSValue op1, op2;- double d;- op1 = sp[-2];- op2 = sp[-1];- if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {- int32_t v1, v2;- int64_t r;- v1 = JS_VALUE_GET_INT(op1);- v2 = JS_VALUE_GET_INT(op2);- r = (int64_t)v1 * v2;- if (unlikely((int)r != r)) {-#ifdef CONFIG_BIGNUM- if (unlikely(sf->js_mode & JS_MODE_MATH) &&- (r < -MAX_SAFE_INTEGER || r > MAX_SAFE_INTEGER))- goto binary_arith_slow;-#endif- d = (double)r;- goto mul_fp_res;- }- /* need to test zero case for -0 result */- if (unlikely(r == 0 && (v1 | v2) < 0)) {- d = -0.0;- goto mul_fp_res;- }- sp[-2] = JS_NewInt32(ctx, r);- sp--;- } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {-#ifdef CONFIG_BIGNUM- if (unlikely(sf->js_mode & JS_MODE_MATH))- goto binary_arith_slow;-#endif- d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2);- mul_fp_res:- sp[-2] = __JS_NewFloat64(ctx, d);- sp--;- } else {- goto binary_arith_slow;- }- }- BREAK;- CASE(OP_div):- {- JSValue op1, op2;- op1 = sp[-2];- op2 = sp[-1];- if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {- int v1, v2;- if (unlikely(sf->js_mode & JS_MODE_MATH))- goto binary_arith_slow;- v1 = JS_VALUE_GET_INT(op1);- v2 = JS_VALUE_GET_INT(op2);- sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2);- sp--;- } else {- goto binary_arith_slow;- }- }- BREAK;- CASE(OP_mod):-#ifdef CONFIG_BIGNUM- CASE(OP_math_mod):-#endif- {- JSValue op1, op2;- op1 = sp[-2];- op2 = sp[-1];- if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {- int v1, v2, r;- v1 = JS_VALUE_GET_INT(op1);- v2 = JS_VALUE_GET_INT(op2);- /* We must avoid v2 = 0, v1 = INT32_MIN and v2 =- -1 and the cases where the result is -0. */- if (unlikely(v1 < 0 || v2 <= 0))- goto binary_arith_slow;- r = v1 % v2;- sp[-2] = JS_NewInt32(ctx, r);- sp--;- } else {- goto binary_arith_slow;- }- }- BREAK;- CASE(OP_pow):- binary_arith_slow:- if (js_binary_arith_slow(ctx, sp, opcode))- goto exception;- sp--;- BREAK;-- CASE(OP_plus):- {- JSValue op1;- uint32_t tag;- op1 = sp[-1];- tag = JS_VALUE_GET_TAG(op1);- if (tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag)) {- } else {- if (js_unary_arith_slow(ctx, sp, opcode))- goto exception;- }- }- BREAK;- CASE(OP_neg):- {- JSValue op1;- uint32_t tag;- int val;- double d;- op1 = sp[-1];- tag = JS_VALUE_GET_TAG(op1);- if (tag == JS_TAG_INT) {- val = JS_VALUE_GET_INT(op1);- /* Note: -0 cannot be expressed as integer */- if (unlikely(val == 0)) {- d = -0.0;- goto neg_fp_res;- }- if (unlikely(val == INT32_MIN)) {- d = -(double)val;- goto neg_fp_res;- }- sp[-1] = JS_NewInt32(ctx, -val);- } else if (JS_TAG_IS_FLOAT64(tag)) {- d = -JS_VALUE_GET_FLOAT64(op1);- neg_fp_res:- sp[-1] = __JS_NewFloat64(ctx, d);- } else {- if (js_unary_arith_slow(ctx, sp, opcode))- goto exception;- }- }- BREAK;- CASE(OP_inc):- {- JSValue op1;- int val;- op1 = sp[-1];- if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {- val = JS_VALUE_GET_INT(op1);- if (unlikely(val == INT32_MAX))- goto inc_slow;- sp[-1] = JS_NewInt32(ctx, val + 1);- } else {- inc_slow:- if (js_unary_arith_slow(ctx, sp, opcode))- goto exception;- }- }- BREAK;- CASE(OP_dec):- {- JSValue op1;- int val;- op1 = sp[-1];- if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {- val = JS_VALUE_GET_INT(op1);- if (unlikely(val == INT32_MIN))- goto dec_slow;- sp[-1] = JS_NewInt32(ctx, val - 1);- } else {- dec_slow:- if (js_unary_arith_slow(ctx, sp, opcode))- goto exception;- }- }- BREAK;- CASE(OP_post_inc):- CASE(OP_post_dec):- if (js_post_inc_slow(ctx, sp, opcode))- goto exception;- sp++;- BREAK;- CASE(OP_inc_loc):- {- JSValue op1;- int val;- int idx;- idx = *pc;- pc += 1;-- op1 = var_buf[idx];- if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {- val = JS_VALUE_GET_INT(op1);- if (unlikely(val == INT32_MAX))- goto inc_loc_slow;- var_buf[idx] = JS_NewInt32(ctx, val + 1);- } else {- inc_loc_slow:- if (js_unary_arith_slow(ctx, var_buf + idx + 1, OP_inc))- goto exception;- }- }- BREAK;- CASE(OP_dec_loc):- {- JSValue op1;- int val;- int idx;- idx = *pc;- pc += 1;-- op1 = var_buf[idx];- if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {- val = JS_VALUE_GET_INT(op1);- if (unlikely(val == INT32_MIN))- goto dec_loc_slow;- var_buf[idx] = JS_NewInt32(ctx, val - 1);- } else {- dec_loc_slow:- if (js_unary_arith_slow(ctx, var_buf + idx + 1, OP_dec))- goto exception;- }- }- BREAK;- CASE(OP_not):- {- JSValue op1;- op1 = sp[-1];- if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {- sp[-1] = JS_NewInt32(ctx, ~JS_VALUE_GET_INT(op1));- } else {- if (js_not_slow(ctx, sp))- goto exception;- }- }- BREAK;-- CASE(OP_shl):- {- JSValue op1, op2;- op1 = sp[-2];- op2 = sp[-1];- if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {- uint32_t v1, v2;- v1 = JS_VALUE_GET_INT(op1);- v2 = JS_VALUE_GET_INT(op2);-#ifdef CONFIG_BIGNUM- {- int64_t r;- if (unlikely(sf->js_mode & JS_MODE_MATH)) {- if (v2 > 0x1f)- goto shl_slow;- r = (int64_t)v1 << v2;- if ((int)r != r)- goto shl_slow;- } else {- v2 &= 0x1f;- }- }-#else- v2 &= 0x1f;-#endif- sp[-2] = JS_NewInt32(ctx, v1 << v2);- sp--;- } else {-#ifdef CONFIG_BIGNUM- shl_slow:-#endif- if (js_binary_logic_slow(ctx, sp, opcode))- goto exception;- sp--;- }- }- BREAK;- CASE(OP_shr):- {- JSValue op1, op2;- op1 = sp[-2];- op2 = sp[-1];- if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {- uint32_t v2;- v2 = JS_VALUE_GET_INT(op2);- /* v1 >>> v2 retains its JS semantics if CONFIG_BIGNUM */- v2 &= 0x1f;- sp[-2] = JS_NewUint32(ctx,- (uint32_t)JS_VALUE_GET_INT(op1) >>- v2);- sp--;- } else {- if (js_shr_slow(ctx, sp))- goto exception;- sp--;- }- }- BREAK;- CASE(OP_sar):- {- JSValue op1, op2;- op1 = sp[-2];- op2 = sp[-1];- if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {- uint32_t v2;- v2 = JS_VALUE_GET_INT(op2);-#ifdef CONFIG_BIGNUM- if (unlikely(v2 > 0x1f)) {- if (unlikely(sf->js_mode & JS_MODE_MATH))- goto sar_slow;- else- v2 &= 0x1f;- }-#else- v2 &= 0x1f;-#endif- sp[-2] = JS_NewInt32(ctx,- (int)JS_VALUE_GET_INT(op1) >> v2);- sp--;- } else {-#ifdef CONFIG_BIGNUM- sar_slow:-#endif- if (js_binary_logic_slow(ctx, sp, opcode))- goto exception;- sp--;- }- }- BREAK;- CASE(OP_and):- {- JSValue op1, op2;- op1 = sp[-2];- op2 = sp[-1];- if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {- sp[-2] = JS_NewInt32(ctx,- JS_VALUE_GET_INT(op1) &- JS_VALUE_GET_INT(op2));- sp--;- } else {- if (js_binary_logic_slow(ctx, sp, opcode))- goto exception;- sp--;- }- }- BREAK;- CASE(OP_or):- {- JSValue op1, op2;- op1 = sp[-2];- op2 = sp[-1];- if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {- sp[-2] = JS_NewInt32(ctx,- JS_VALUE_GET_INT(op1) |- JS_VALUE_GET_INT(op2));- sp--;- } else {- if (js_binary_logic_slow(ctx, sp, opcode))- goto exception;- sp--;- }- }- BREAK;- CASE(OP_xor):- {- JSValue op1, op2;- op1 = sp[-2];- op2 = sp[-1];- if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {- sp[-2] = JS_NewInt32(ctx,- JS_VALUE_GET_INT(op1) ^- JS_VALUE_GET_INT(op2));- sp--;- } else {- if (js_binary_logic_slow(ctx, sp, opcode))- goto exception;- sp--;- }- }- BREAK;---#define OP_CMP(opcode, binary_op, slow_call) \- CASE(opcode): \- { \- JSValue op1, op2; \- op1 = sp[-2]; \- op2 = sp[-1]; \- if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { \- sp[-2] = JS_NewBool(ctx, JS_VALUE_GET_INT(op1) binary_op JS_VALUE_GET_INT(op2)); \- sp--; \- } else { \- if (slow_call) \- goto exception; \- sp--; \- } \- } \- BREAK-- OP_CMP(OP_lt, <, js_relational_slow(ctx, sp, opcode));- OP_CMP(OP_lte, <=, js_relational_slow(ctx, sp, opcode));- OP_CMP(OP_gt, >, js_relational_slow(ctx, sp, opcode));- OP_CMP(OP_gte, >=, js_relational_slow(ctx, sp, opcode));- OP_CMP(OP_eq, ==, js_eq_slow(ctx, sp, 0));- OP_CMP(OP_neq, !=, js_eq_slow(ctx, sp, 1));- OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, sp, 0));- OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, sp, 1));--#ifdef CONFIG_BIGNUM- CASE(OP_mul_pow10):- if (rt->bigfloat_ops.mul_pow10(ctx, sp))- goto exception;- sp--;- BREAK;-#endif- CASE(OP_in):- if (js_operator_in(ctx, sp))- goto exception;- sp--;- BREAK;- CASE(OP_instanceof):- if (js_operator_instanceof(ctx, sp))- goto exception;- sp--;- BREAK;- CASE(OP_typeof):- {- JSValue op1;- JSAtom atom;-- op1 = sp[-1];- atom = js_operator_typeof(ctx, op1);- JS_FreeValue(ctx, op1);- sp[-1] = JS_AtomToString(ctx, atom);- }- BREAK;- CASE(OP_delete):- if (js_operator_delete(ctx, sp))- goto exception;- sp--;- BREAK;- CASE(OP_delete_var):- {- JSAtom atom;- int ret;-- atom = get_u32(pc);- pc += 4;-- ret = JS_DeleteProperty(ctx, ctx->global_obj, atom, 0);- if (unlikely(ret < 0))- goto exception;- *sp++ = JS_NewBool(ctx, ret);- }- BREAK;-- CASE(OP_to_object):- if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_OBJECT) {- ret_val = JS_ToObject(ctx, sp[-1]);- if (JS_IsException(ret_val))- goto exception;- JS_FreeValue(ctx, sp[-1]);- sp[-1] = ret_val;- }- BREAK;-- CASE(OP_to_propkey):- switch (JS_VALUE_GET_TAG(sp[-1])) {- case JS_TAG_INT:- case JS_TAG_STRING:- case JS_TAG_SYMBOL:- break;- default:- ret_val = JS_ToPropertyKey(ctx, sp[-1]);- if (JS_IsException(ret_val))- goto exception;- JS_FreeValue(ctx, sp[-1]);- sp[-1] = ret_val;- break;- }- BREAK;-- CASE(OP_to_propkey2):- /* must be tested first */- if (unlikely(JS_IsUndefined(sp[-2]) || JS_IsNull(sp[-2]))) {- JS_ThrowTypeError(ctx, "value has no property");- goto exception;- }- switch (JS_VALUE_GET_TAG(sp[-1])) {- case JS_TAG_INT:- case JS_TAG_STRING:- case JS_TAG_SYMBOL:- break;- default:- ret_val = JS_ToPropertyKey(ctx, sp[-1]);- if (JS_IsException(ret_val))- goto exception;- JS_FreeValue(ctx, sp[-1]);- sp[-1] = ret_val;- break;- }- BREAK;-#if 0- CASE(OP_to_string):- if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_STRING) {- ret_val = JS_ToString(ctx, sp[-1]);- if (JS_IsException(ret_val))- goto exception;- JS_FreeValue(ctx, sp[-1]);- sp[-1] = ret_val;- }- BREAK;-#endif- CASE(OP_with_get_var):- CASE(OP_with_put_var):- CASE(OP_with_delete_var):- CASE(OP_with_make_ref):- CASE(OP_with_get_ref):- CASE(OP_with_get_ref_undef):- {- JSAtom atom;- int32_t diff;- JSValue obj, val;- int ret, is_with;- atom = get_u32(pc);- diff = get_u32(pc + 4);- is_with = pc[8];- pc += 9;-- obj = sp[-1];- ret = JS_HasProperty(ctx, obj, atom);- if (unlikely(ret < 0))- goto exception;- if (ret) {- if (is_with) {- ret = js_has_unscopable(ctx, obj, atom);- if (unlikely(ret < 0))- goto exception;- if (ret)- goto no_with;- }- switch (opcode) {- case OP_with_get_var:- val = JS_GetProperty(ctx, obj, atom);- if (unlikely(JS_IsException(val)))- goto exception;- set_value(ctx, &sp[-1], val);- break;- case OP_with_put_var:- ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2],- JS_PROP_THROW_STRICT);- JS_FreeValue(ctx, sp[-1]);- sp -= 2;- if (unlikely(ret < 0))- goto exception;- break;- case OP_with_delete_var:- ret = JS_DeleteProperty(ctx, obj, atom, 0);- if (unlikely(ret < 0))- goto exception;- JS_FreeValue(ctx, sp[-1]);- sp[-1] = JS_NewBool(ctx, ret);- break;- case OP_with_make_ref:- /* produce a pair object/propname on the stack */- *sp++ = JS_AtomToValue(ctx, atom);- break;- case OP_with_get_ref:- /* produce a pair object/method on the stack */- val = JS_GetProperty(ctx, obj, atom);- if (unlikely(JS_IsException(val)))- goto exception;- *sp++ = val;- break;- case OP_with_get_ref_undef:- /* produce a pair undefined/function on the stack */- val = JS_GetProperty(ctx, obj, atom);- if (unlikely(JS_IsException(val)))- goto exception;- JS_FreeValue(ctx, sp[-1]);- sp[-1] = JS_UNDEFINED;- *sp++ = val;- break;- }- pc += diff - 5;- } else {- no_with:- /* if not jumping, drop the object argument */- JS_FreeValue(ctx, sp[-1]);- sp--;- }- }- BREAK;-- CASE(OP_await):- ret_val = JS_NewInt32(ctx, FUNC_RET_AWAIT);- goto done_generator;- CASE(OP_yield):- ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD);- goto done_generator;- CASE(OP_yield_star):- CASE(OP_async_yield_star):- ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD_STAR);- goto done_generator;- CASE(OP_return_async):- CASE(OP_initial_yield):- ret_val = JS_UNDEFINED;- goto done_generator;-- CASE(OP_nop):- BREAK;- CASE(OP_is_undefined_or_null):- if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED ||- JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) {- goto set_true;- } else {- goto free_and_set_false;- }-#if SHORT_OPCODES- CASE(OP_is_undefined):- if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED) {- goto set_true;- } else {- goto free_and_set_false;- }- CASE(OP_is_null):- if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) {- goto set_true;- } else {- goto free_and_set_false;- }- /* XXX: could merge to a single opcode */- CASE(OP_typeof_is_undefined):- /* different from OP_is_undefined because of isHTMLDDA */- if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_undefined) {- goto free_and_set_true;- } else {- goto free_and_set_false;- }- CASE(OP_typeof_is_function):- if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) {- goto free_and_set_true;- } else {- goto free_and_set_false;- }- free_and_set_true:- JS_FreeValue(ctx, sp[-1]);-#endif- set_true:- sp[-1] = JS_TRUE;- BREAK;- free_and_set_false:- JS_FreeValue(ctx, sp[-1]);- sp[-1] = JS_FALSE;- BREAK;- CASE(OP_invalid):- DEFAULT:- JS_ThrowInternalError(ctx, "invalid opcode: pc=%u opcode=0x%02x",- (int)(pc - b->byte_code_buf - 1), opcode);- goto exception;- }- }- exception:- if (is_backtrace_needed(ctx, rt->current_exception)) {- /* add the backtrace information now (it is not done- before if the exception happens in a bytecode- operation */- sf->cur_pc = pc;- build_backtrace(ctx, rt->current_exception, NULL, 0, 0);- }- if (!JS_IsUncatchableError(ctx, rt->current_exception)) {- while (sp > stack_buf) {- JSValue val = *--sp;- JS_FreeValue(ctx, val);- if (JS_VALUE_GET_TAG(val) == JS_TAG_CATCH_OFFSET) {- int pos = JS_VALUE_GET_INT(val);- if (pos == 0) {- /* enumerator: close it with a throw */- JS_FreeValue(ctx, sp[-1]); /* drop the next method */- sp--;- JS_IteratorClose(ctx, sp[-1], TRUE);- } else {- *sp++ = rt->current_exception;- rt->current_exception = JS_NULL;- pc = b->byte_code_buf + pos;- goto restart;- }- }- }- }- ret_val = JS_EXCEPTION;- /* the local variables are freed by the caller in the generator- case. Hence the label 'done' should never be reached in a- generator function. */- if (b->func_kind != JS_FUNC_NORMAL) {- done_generator:- sf->cur_pc = pc;- sf->cur_sp = sp;- } else {- done:- if (unlikely(!list_empty(&sf->var_ref_list))) {- /* variable references reference the stack: must close them */- close_var_refs(rt, sf);- }- /* free the local variables and stack */- for(pval = local_buf; pval < sp; pval++) {- JS_FreeValue(ctx, *pval);- }- }- rt->current_stack_frame = sf->prev_frame;- return ret_val;-}--JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj,- int argc, JSValueConst *argv)-{- return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED,- argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV);-}--static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj,- int argc, JSValueConst *argv)-{- JSValue res = JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED,- argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV);- JS_FreeValue(ctx, func_obj);- return res;-}--/* warning: the refcount of the context is not incremented. Return- NULL in case of exception (case of revoked proxy only) */-static JSContext *JS_GetFunctionRealm(JSContext *ctx, JSValueConst func_obj)-{- JSObject *p;- JSContext *realm;- - if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)- return ctx;- p = JS_VALUE_GET_OBJ(func_obj);- switch(p->class_id) {- case JS_CLASS_C_FUNCTION:- realm = p->u.cfunc.realm;- break;- case JS_CLASS_BYTECODE_FUNCTION:- case JS_CLASS_GENERATOR_FUNCTION:- case JS_CLASS_ASYNC_FUNCTION:- case JS_CLASS_ASYNC_GENERATOR_FUNCTION:- {- JSFunctionBytecode *b;- b = p->u.func.function_bytecode;- realm = b->realm;- }- break;- case JS_CLASS_PROXY:- {- JSProxyData *s = p->u.opaque;- if (!s)- return ctx;- if (s->is_revoked) {- JS_ThrowTypeErrorRevokedProxy(ctx);- return NULL;- } else {- realm = JS_GetFunctionRealm(ctx, s->target);- }- }- break;- case JS_CLASS_BOUND_FUNCTION:- {- JSBoundFunction *bf = p->u.bound_function;- realm = JS_GetFunctionRealm(ctx, bf->func_obj);- }- break;- default:- realm = ctx;- break;- }- return realm;-}--static JSValue js_create_from_ctor(JSContext *ctx, JSValueConst ctor,- int class_id)-{- JSValue proto, obj;- JSContext *realm;- - if (JS_IsUndefined(ctor)) {- proto = JS_DupValue(ctx, ctx->class_proto[class_id]);- } else {- proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype);- if (JS_IsException(proto))- return proto;- if (!JS_IsObject(proto)) {- JS_FreeValue(ctx, proto);- realm = JS_GetFunctionRealm(ctx, ctor);- if (!realm)- return JS_EXCEPTION;- proto = JS_DupValue(ctx, realm->class_proto[class_id]);- }- }- obj = JS_NewObjectProtoClass(ctx, proto, class_id);- JS_FreeValue(ctx, proto);- return obj;-}--/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */-static JSValue JS_CallConstructorInternal(JSContext *ctx,- JSValueConst func_obj,- JSValueConst new_target,- int argc, JSValue *argv, int flags)-{- JSObject *p;- JSFunctionBytecode *b;-- if (js_poll_interrupts(ctx))- return JS_EXCEPTION;- flags |= JS_CALL_FLAG_CONSTRUCTOR;- if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT))- goto not_a_function;- p = JS_VALUE_GET_OBJ(func_obj);- if (unlikely(!p->is_constructor))- return JS_ThrowTypeError(ctx, "not a constructor");- if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) {- JSClassCall *call_func;- call_func = ctx->rt->class_array[p->class_id].call;- if (!call_func) {- not_a_function:- return JS_ThrowTypeError(ctx, "not a function");- }- return call_func(ctx, func_obj, new_target, argc,- (JSValueConst *)argv, flags);- }-- b = p->u.func.function_bytecode;- if (b->is_derived_class_constructor) {- return JS_CallInternal(ctx, func_obj, JS_UNDEFINED, new_target, argc, argv, flags);- } else {- JSValue obj, ret;- /* legacy constructor behavior */- obj = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);- if (JS_IsException(obj))- return JS_EXCEPTION;- ret = JS_CallInternal(ctx, func_obj, obj, new_target, argc, argv, flags);- if (JS_VALUE_GET_TAG(ret) == JS_TAG_OBJECT ||- JS_IsException(ret)) {- JS_FreeValue(ctx, obj);- return ret;- } else {- JS_FreeValue(ctx, ret);- return obj;- }- }-}--JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj,- JSValueConst new_target,- int argc, JSValueConst *argv)-{- return JS_CallConstructorInternal(ctx, func_obj, new_target,- argc, (JSValue *)argv,- JS_CALL_FLAG_COPY_ARGV);-}--JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj,- int argc, JSValueConst *argv)-{- return JS_CallConstructorInternal(ctx, func_obj, func_obj,- argc, (JSValue *)argv,- JS_CALL_FLAG_COPY_ARGV);-}--JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom,- int argc, JSValueConst *argv)-{- JSValue func_obj;- func_obj = JS_GetProperty(ctx, this_val, atom);- if (JS_IsException(func_obj))- return func_obj;- return JS_CallFree(ctx, func_obj, this_val, argc, argv);-}--static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,- int argc, JSValueConst *argv)-{- JSValue res = JS_Invoke(ctx, this_val, atom, argc, argv);- JS_FreeValue(ctx, this_val);- return res;-}--/* JSAsyncFunctionState (used by generator and async functions) */-static __exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s,- JSValueConst func_obj, JSValueConst this_obj,- int argc, JSValueConst *argv)-{- JSObject *p;- JSFunctionBytecode *b;- JSStackFrame *sf;- int local_count, i, arg_buf_len, n;-- sf = &s->frame;- init_list_head(&sf->var_ref_list);- p = JS_VALUE_GET_OBJ(func_obj);- b = p->u.func.function_bytecode;- sf->js_mode = b->js_mode;- sf->cur_pc = b->byte_code_buf;- arg_buf_len = max_int(b->arg_count, argc);- local_count = arg_buf_len + b->var_count + b->stack_size;- sf->arg_buf = js_malloc(ctx, sizeof(JSValue) * max_int(local_count, 1));- if (!sf->arg_buf)- return -1;- sf->cur_func = JS_DupValue(ctx, func_obj);- s->this_val = JS_DupValue(ctx, this_obj);- s->argc = argc;- sf->arg_count = arg_buf_len;- sf->var_buf = sf->arg_buf + arg_buf_len;- sf->cur_sp = sf->var_buf + b->var_count;- for(i = 0; i < argc; i++)- sf->arg_buf[i] = JS_DupValue(ctx, argv[i]);- n = arg_buf_len + b->var_count;- for(i = argc; i < n; i++)- sf->arg_buf[i] = JS_UNDEFINED;- return 0;-}--static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s,- JS_MarkFunc *mark_func)-{- JSStackFrame *sf;- JSValue *sp;-- sf = &s->frame;- JS_MarkValue(rt, sf->cur_func, mark_func);- JS_MarkValue(rt, s->this_val, mark_func);- if (sf->cur_sp) {- /* if the function is running, cur_sp is not known so we- cannot mark the stack. Marking the variables is not needed- because a running function cannot be part of a removable- cycle */- for(sp = sf->arg_buf; sp < sf->cur_sp; sp++)- JS_MarkValue(rt, *sp, mark_func);- }-}--static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)-{- JSStackFrame *sf;- JSValue *sp;-- sf = &s->frame;-- /* close the closure variables. */- close_var_refs(rt, sf);- - if (sf->arg_buf) {- /* cannot free the function if it is running */- assert(sf->cur_sp != NULL);- for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) {- JS_FreeValueRT(rt, *sp);- }- js_free_rt(rt, sf->arg_buf);- }- JS_FreeValueRT(rt, sf->cur_func);- JS_FreeValueRT(rt, s->this_val);-}--static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s)-{- JSValue func_obj;-- if (js_check_stack_overflow(ctx->rt, 0))- return JS_ThrowStackOverflow(ctx);-- /* the tag does not matter provided it is not an object */- func_obj = JS_MKPTR(JS_TAG_INT, s);- return JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED,- s->argc, s->frame.arg_buf, JS_CALL_FLAG_GENERATOR);-}---/* Generators */--typedef enum JSGeneratorStateEnum {- JS_GENERATOR_STATE_SUSPENDED_START,- JS_GENERATOR_STATE_SUSPENDED_YIELD,- JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR,- JS_GENERATOR_STATE_EXECUTING,- JS_GENERATOR_STATE_COMPLETED,-} JSGeneratorStateEnum;--typedef struct JSGeneratorData {- JSGeneratorStateEnum state;- JSAsyncFunctionState func_state;-} JSGeneratorData;--static void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s)-{- if (s->state == JS_GENERATOR_STATE_COMPLETED)- return;- async_func_free(rt, &s->func_state);- s->state = JS_GENERATOR_STATE_COMPLETED;-}--static void js_generator_finalizer(JSRuntime *rt, JSValue obj)-{- JSGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_GENERATOR);-- if (s) {- free_generator_stack_rt(rt, s);- js_free_rt(rt, s);- }-}--static void free_generator_stack(JSContext *ctx, JSGeneratorData *s)-{- free_generator_stack_rt(ctx->rt, s);-}--static void js_generator_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func)-{- JSObject *p = JS_VALUE_GET_OBJ(val);- JSGeneratorData *s = p->u.generator_data;-- if (!s || s->state == JS_GENERATOR_STATE_COMPLETED)- return;- async_func_mark(rt, &s->func_state, mark_func);-}--/* XXX: use enum */-#define GEN_MAGIC_NEXT 0-#define GEN_MAGIC_RETURN 1-#define GEN_MAGIC_THROW 2--static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv,- BOOL *pdone, int magic)-{- JSGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_GENERATOR);- JSStackFrame *sf;- JSValue ret, func_ret;- JSValueConst iter_args[1];-- *pdone = TRUE;- if (!s)- return JS_ThrowTypeError(ctx, "not a generator");- sf = &s->func_state.frame;- redo:- switch(s->state) {- default:- case JS_GENERATOR_STATE_SUSPENDED_START:- if (magic == GEN_MAGIC_NEXT) {- goto exec_no_arg;- } else {- free_generator_stack(ctx, s);- goto done;- }- break;- case JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR:- {- int done;- JSValue method, iter_obj;-- iter_obj = sf->cur_sp[-2];- if (magic == GEN_MAGIC_NEXT) {- method = JS_DupValue(ctx, sf->cur_sp[-1]);- } else {- method = JS_GetProperty(ctx, iter_obj,- magic == GEN_MAGIC_RETURN ?- JS_ATOM_return : JS_ATOM_throw);- if (JS_IsException(method))- goto iter_exception;- }- if (magic != GEN_MAGIC_NEXT &&- (JS_IsUndefined(method) || JS_IsNull(method))) {- /* default action */- if (magic == GEN_MAGIC_RETURN) {- ret = JS_DupValue(ctx, argv[0]);- goto iter_done;- } else {- if (JS_IteratorClose(ctx, iter_obj, FALSE))- goto iter_exception;- JS_ThrowTypeError(ctx, "iterator does not have a throw method");- goto iter_exception;- }- }- ret = JS_IteratorNext2(ctx, iter_obj, method, argc, argv, &done);- JS_FreeValue(ctx, method);- if (JS_IsException(ret)) {- iter_exception:- goto exec_throw;- }- /* if not done, the iterator returns the exact object- returned by 'method' */- if (done == 2) {- JSValue done_val, value;- done_val = JS_GetProperty(ctx, ret, JS_ATOM_done);- if (JS_IsException(done_val)) {- JS_FreeValue(ctx, ret);- goto iter_exception;- }- done = JS_ToBoolFree(ctx, done_val);- if (done) {- value = JS_GetProperty(ctx, ret, JS_ATOM_value);- JS_FreeValue(ctx, ret);- if (JS_IsException(value))- goto iter_exception;- ret = value;- goto iter_done;- } else {- *pdone = 2;- }- } else {- if (done) {- /* 'yield *' returns the value associated to done = true */- iter_done:- JS_FreeValue(ctx, sf->cur_sp[-2]);- JS_FreeValue(ctx, sf->cur_sp[-1]);- sf->cur_sp--;- goto exec_arg;- } else {- *pdone = FALSE;- }- }- break;- }- break;- case JS_GENERATOR_STATE_SUSPENDED_YIELD:- /* cur_sp[-1] was set to JS_UNDEFINED in the previous call */- ret = JS_DupValue(ctx, argv[0]);- if (magic == GEN_MAGIC_THROW) {- JS_Throw(ctx, ret);- exec_throw:- s->func_state.throw_flag = TRUE;- } else {- exec_arg:- sf->cur_sp[-1] = ret;- sf->cur_sp[0] = JS_NewBool(ctx, (magic == GEN_MAGIC_RETURN));- sf->cur_sp++;- exec_no_arg:- s->func_state.throw_flag = FALSE;- }- s->state = JS_GENERATOR_STATE_EXECUTING;- func_ret = async_func_resume(ctx, &s->func_state);- s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD;- if (JS_IsException(func_ret)) {- /* finalize the execution in case of exception */- free_generator_stack(ctx, s);- return func_ret;- }- if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) {- if (JS_VALUE_GET_INT(func_ret) == FUNC_RET_YIELD_STAR) {- /* 'yield *' */- s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR;- iter_args[0] = JS_UNDEFINED;- argc = 1;- argv = iter_args;- goto redo;- } else {- /* get the return the yield value at the top of the stack */- ret = sf->cur_sp[-1];- sf->cur_sp[-1] = JS_UNDEFINED;- *pdone = FALSE;- }- } else {- /* end of iterator */- ret = sf->cur_sp[-1];- sf->cur_sp[-1] = JS_UNDEFINED;- JS_FreeValue(ctx, func_ret);- free_generator_stack(ctx, s);- }- break;- case JS_GENERATOR_STATE_COMPLETED:- done:- /* execution is finished */- switch(magic) {- default:- case GEN_MAGIC_NEXT:- ret = JS_UNDEFINED;- break;- case GEN_MAGIC_RETURN:- ret = JS_DupValue(ctx, argv[0]);- break;- case GEN_MAGIC_THROW:- ret = JS_Throw(ctx, JS_DupValue(ctx, argv[0]));- break;- }- break;- case JS_GENERATOR_STATE_EXECUTING:- ret = JS_ThrowTypeError(ctx, "cannot invoke a running generator");- break;- }- return ret;-}--static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,- JSValueConst this_obj,- int argc, JSValueConst *argv,- int flags)-{- JSValue obj, func_ret;- JSGeneratorData *s;-- s = js_mallocz(ctx, sizeof(*s));- if (!s)- return JS_EXCEPTION;- s->state = JS_GENERATOR_STATE_SUSPENDED_START;- if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {- s->state = JS_GENERATOR_STATE_COMPLETED;- goto fail;- }-- /* execute the function up to 'OP_initial_yield' */- func_ret = async_func_resume(ctx, &s->func_state);- if (JS_IsException(func_ret))- goto fail;- JS_FreeValue(ctx, func_ret);-- obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_GENERATOR);- if (JS_IsException(obj))- goto fail;- JS_SetOpaque(obj, s);- return obj;- fail:- free_generator_stack_rt(ctx->rt, s);- js_free(ctx, s);- return JS_EXCEPTION;-}--/* AsyncFunction */--static void js_async_function_terminate(JSRuntime *rt, JSAsyncFunctionData *s)-{- if (s->is_active) {- async_func_free(rt, &s->func_state);- s->is_active = FALSE;- }-}--static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s)-{- js_async_function_terminate(rt, s);- JS_FreeValueRT(rt, s->resolving_funcs[0]);- JS_FreeValueRT(rt, s->resolving_funcs[1]);- remove_gc_object(&s->header);- js_free_rt(rt, s);-}--static void js_async_function_free(JSRuntime *rt, JSAsyncFunctionData *s)-{- if (--s->header.ref_count == 0) {- js_async_function_free0(rt, s);- }-}--static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val)-{- JSObject *p = JS_VALUE_GET_OBJ(val);- JSAsyncFunctionData *s = p->u.async_function_data;- if (s) {- js_async_function_free(rt, s);- }-}--static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func)-{- JSObject *p = JS_VALUE_GET_OBJ(val);- JSAsyncFunctionData *s = p->u.async_function_data;- if (s) {- mark_func(rt, &s->header);- }-}--static int js_async_function_resolve_create(JSContext *ctx,- JSAsyncFunctionData *s,- JSValue *resolving_funcs)-{- int i;- JSObject *p;-- for(i = 0; i < 2; i++) {- resolving_funcs[i] =- JS_NewObjectProtoClass(ctx, ctx->function_proto,- JS_CLASS_ASYNC_FUNCTION_RESOLVE + i);- if (JS_IsException(resolving_funcs[i])) {- if (i == 1)- JS_FreeValue(ctx, resolving_funcs[0]);- return -1;- }- p = JS_VALUE_GET_OBJ(resolving_funcs[i]);- s->header.ref_count++;- p->u.async_function_data = s;- }- return 0;-}--static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s)-{- JSValue func_ret, ret2;-- func_ret = async_func_resume(ctx, &s->func_state);- if (JS_IsException(func_ret)) {- JSValue error;- fail:- error = JS_GetException(ctx);- ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED,- 1, (JSValueConst *)&error);- JS_FreeValue(ctx, error);- js_async_function_terminate(ctx->rt, s);- JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */- } else {- JSValue value;- value = s->func_state.frame.cur_sp[-1];- s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;- if (JS_IsUndefined(func_ret)) {- /* function returned */- ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED,- 1, (JSValueConst *)&value);- JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */- JS_FreeValue(ctx, value);- js_async_function_terminate(ctx->rt, s);- } else {- JSValue promise, resolving_funcs[2], resolving_funcs1[2];- int i, res;-- /* await */- JS_FreeValue(ctx, func_ret); /* not used */- promise = js_promise_resolve(ctx, ctx->promise_ctor,- 1, (JSValueConst *)&value, 0);- JS_FreeValue(ctx, value);- if (JS_IsException(promise))- goto fail;- if (js_async_function_resolve_create(ctx, s, resolving_funcs)) {- JS_FreeValue(ctx, promise);- goto fail;- }-- /* Note: no need to create 'thrownawayCapability' as in- the spec */- for(i = 0; i < 2; i++)- resolving_funcs1[i] = JS_UNDEFINED;- res = perform_promise_then(ctx, promise,- (JSValueConst *)resolving_funcs,- (JSValueConst *)resolving_funcs1);- JS_FreeValue(ctx, promise);- for(i = 0; i < 2; i++)- JS_FreeValue(ctx, resolving_funcs[i]);- if (res)- goto fail;- }- }-}--static JSValue js_async_function_resolve_call(JSContext *ctx,- JSValueConst func_obj,- JSValueConst this_obj,- int argc, JSValueConst *argv,- int flags)-{- JSObject *p = JS_VALUE_GET_OBJ(func_obj);- JSAsyncFunctionData *s = p->u.async_function_data;- BOOL is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE;- JSValueConst arg;-- if (argc > 0)- arg = argv[0];- else- arg = JS_UNDEFINED;- s->func_state.throw_flag = is_reject;- if (is_reject) {- JS_Throw(ctx, JS_DupValue(ctx, arg));- } else {- /* return value of await */- s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg);- }- js_async_function_resume(ctx, s);- return JS_UNDEFINED;-}--static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj,- JSValueConst this_obj,- int argc, JSValueConst *argv, int flags)-{- JSValue promise;- JSAsyncFunctionData *s;-- s = js_mallocz(ctx, sizeof(*s));- if (!s)- return JS_EXCEPTION;- s->header.ref_count = 1;- add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION);- s->is_active = FALSE;- s->resolving_funcs[0] = JS_UNDEFINED;- s->resolving_funcs[1] = JS_UNDEFINED;-- promise = JS_NewPromiseCapability(ctx, s->resolving_funcs);- if (JS_IsException(promise))- goto fail;-- if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {- fail:- JS_FreeValue(ctx, promise);- js_async_function_free(ctx->rt, s);- return JS_EXCEPTION;- }- s->is_active = TRUE;-- js_async_function_resume(ctx, s);-- js_async_function_free(ctx->rt, s);-- return promise;-}--/* AsyncGenerator */--typedef enum JSAsyncGeneratorStateEnum {- JS_ASYNC_GENERATOR_STATE_SUSPENDED_START,- JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD,- JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR,- JS_ASYNC_GENERATOR_STATE_EXECUTING,- JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN,- JS_ASYNC_GENERATOR_STATE_COMPLETED,-} JSAsyncGeneratorStateEnum;--typedef struct JSAsyncGeneratorRequest {- struct list_head link;- /* completion */- int completion_type; /* GEN_MAGIC_x */- JSValue result;- /* promise capability */- JSValue promise;- JSValue resolving_funcs[2];-} JSAsyncGeneratorRequest;--typedef struct JSAsyncGeneratorData {- JSObject *generator; /* back pointer to the object (const) */- JSAsyncGeneratorStateEnum state;- JSAsyncFunctionState func_state;- struct list_head queue; /* list of JSAsyncGeneratorRequest.link */-} JSAsyncGeneratorData;--static void js_async_generator_free(JSRuntime *rt,- JSAsyncGeneratorData *s)-{- struct list_head *el, *el1;- JSAsyncGeneratorRequest *req;-- list_for_each_safe(el, el1, &s->queue) {- req = list_entry(el, JSAsyncGeneratorRequest, link);- JS_FreeValueRT(rt, req->result);- JS_FreeValueRT(rt, req->promise);- JS_FreeValueRT(rt, req->resolving_funcs[0]);- JS_FreeValueRT(rt, req->resolving_funcs[1]);- js_free_rt(rt, req);- }- if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED &&- s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) {- async_func_free(rt, &s->func_state);- }- js_free_rt(rt, s);-}--static void js_async_generator_finalizer(JSRuntime *rt, JSValue obj)-{- JSAsyncGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_ASYNC_GENERATOR);-- if (s) {- js_async_generator_free(rt, s);- }-}--static void js_async_generator_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func)-{- JSAsyncGeneratorData *s = JS_GetOpaque(val, JS_CLASS_ASYNC_GENERATOR);- struct list_head *el;- JSAsyncGeneratorRequest *req;- if (s) {- list_for_each(el, &s->queue) {- req = list_entry(el, JSAsyncGeneratorRequest, link);- JS_MarkValue(rt, req->result, mark_func);- JS_MarkValue(rt, req->promise, mark_func);- JS_MarkValue(rt, req->resolving_funcs[0], mark_func);- JS_MarkValue(rt, req->resolving_funcs[1], mark_func);- }- if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED &&- s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) {- async_func_mark(rt, &s->func_state, mark_func);- }- }-}--static JSValue js_async_generator_resolve_function(JSContext *ctx,- JSValueConst this_obj,- int argc, JSValueConst *argv,- int magic, JSValue *func_data);--static int js_async_generator_resolve_function_create(JSContext *ctx,- JSValueConst generator,- JSValue *resolving_funcs,- BOOL is_resume_next)-{- int i;- JSValue func;-- for(i = 0; i < 2; i++) {- func = JS_NewCFunctionData(ctx, js_async_generator_resolve_function, 1,- i + is_resume_next * 2, 1, &generator);- if (JS_IsException(func)) {- if (i == 1)- JS_FreeValue(ctx, resolving_funcs[0]);- return -1;- }- resolving_funcs[i] = func;- }- return 0;-}--static int js_async_generator_await(JSContext *ctx,- JSAsyncGeneratorData *s,- JSValueConst value)-{- JSValue promise, resolving_funcs[2], resolving_funcs1[2];- int i, res;-- promise = js_promise_resolve(ctx, ctx->promise_ctor,- 1, &value, 0);- if (JS_IsException(promise))- goto fail;-- if (js_async_generator_resolve_function_create(ctx, JS_MKPTR(JS_TAG_OBJECT, s->generator),- resolving_funcs, FALSE)) {- JS_FreeValue(ctx, promise);- goto fail;- }-- /* Note: no need to create 'thrownawayCapability' as in- the spec */- for(i = 0; i < 2; i++)- resolving_funcs1[i] = JS_UNDEFINED;- res = perform_promise_then(ctx, promise,- (JSValueConst *)resolving_funcs,- (JSValueConst *)resolving_funcs1);- JS_FreeValue(ctx, promise);- for(i = 0; i < 2; i++)- JS_FreeValue(ctx, resolving_funcs[i]);- if (res)- goto fail;- return 0;- fail:- return -1;-}--static void js_async_generator_resolve_or_reject(JSContext *ctx,- JSAsyncGeneratorData *s,- JSValueConst result,- int is_reject)-{- JSAsyncGeneratorRequest *next;- JSValue ret;-- next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link);- list_del(&next->link);- ret = JS_Call(ctx, next->resolving_funcs[is_reject], JS_UNDEFINED, 1,- &result);- JS_FreeValue(ctx, ret);- JS_FreeValue(ctx, next->result);- JS_FreeValue(ctx, next->promise);- JS_FreeValue(ctx, next->resolving_funcs[0]);- JS_FreeValue(ctx, next->resolving_funcs[1]);- js_free(ctx, next);-}--static void js_async_generator_resolve(JSContext *ctx,- JSAsyncGeneratorData *s,- JSValueConst value,- BOOL done)-{- JSValue result;- result = js_create_iterator_result(ctx, JS_DupValue(ctx, value), done);- /* XXX: better exception handling ? */- js_async_generator_resolve_or_reject(ctx, s, result, 0);- JS_FreeValue(ctx, result);- }--static void js_async_generator_reject(JSContext *ctx,- JSAsyncGeneratorData *s,- JSValueConst exception)-{- js_async_generator_resolve_or_reject(ctx, s, exception, 1);-}--static void js_async_generator_complete(JSContext *ctx,- JSAsyncGeneratorData *s)-{- if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) {- s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;- async_func_free(ctx->rt, &s->func_state);- }-}--static int js_async_generator_completed_return(JSContext *ctx,- JSAsyncGeneratorData *s,- JSValueConst value)-{- JSValue promise, resolving_funcs[2], resolving_funcs1[2];- int res;-- promise = js_promise_resolve(ctx, ctx->promise_ctor,- 1, (JSValueConst *)&value, 0);- if (JS_IsException(promise))- return -1;- if (js_async_generator_resolve_function_create(ctx,- JS_MKPTR(JS_TAG_OBJECT, s->generator),- resolving_funcs1,- TRUE)) {- JS_FreeValue(ctx, promise);- return -1;- }- resolving_funcs[0] = JS_UNDEFINED;- resolving_funcs[1] = JS_UNDEFINED;- res = perform_promise_then(ctx, promise,- (JSValueConst *)resolving_funcs1,- (JSValueConst *)resolving_funcs);- JS_FreeValue(ctx, resolving_funcs1[0]);- JS_FreeValue(ctx, resolving_funcs1[1]);- JS_FreeValue(ctx, promise);- return res;-}--static void js_async_generator_resume_next(JSContext *ctx,- JSAsyncGeneratorData *s)-{- JSAsyncGeneratorRequest *next;- JSValue func_ret, value;-- for(;;) {- if (list_empty(&s->queue))- break;- next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link);- switch(s->state) {- case JS_ASYNC_GENERATOR_STATE_EXECUTING:- /* only happens when restarting execution after await() */- goto resume_exec;- case JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN:- goto done;- case JS_ASYNC_GENERATOR_STATE_SUSPENDED_START:- if (next->completion_type == GEN_MAGIC_NEXT) {- goto exec_no_arg;- } else {- js_async_generator_complete(ctx, s);- }- break;- case JS_ASYNC_GENERATOR_STATE_COMPLETED:- if (next->completion_type == GEN_MAGIC_NEXT) {- js_async_generator_resolve(ctx, s, JS_UNDEFINED, TRUE);- } else if (next->completion_type == GEN_MAGIC_RETURN) {- s->state = JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN;- js_async_generator_completed_return(ctx, s, next->result);- goto done;- } else {- js_async_generator_reject(ctx, s, next->result);- }- goto done;- case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD:- case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR:- value = JS_DupValue(ctx, next->result);- if (next->completion_type == GEN_MAGIC_THROW &&- s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) {- JS_Throw(ctx, value);- s->func_state.throw_flag = TRUE;- } else {- /* 'yield' returns a value. 'yield *' also returns a value- in case the 'throw' method is called */- s->func_state.frame.cur_sp[-1] = value;- s->func_state.frame.cur_sp[0] =- JS_NewInt32(ctx, next->completion_type);- s->func_state.frame.cur_sp++;- exec_no_arg:- s->func_state.throw_flag = FALSE;- }- s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING;- resume_exec:- func_ret = async_func_resume(ctx, &s->func_state);- if (JS_IsException(func_ret)) {- value = JS_GetException(ctx);- js_async_generator_complete(ctx, s);- js_async_generator_reject(ctx, s, value);- JS_FreeValue(ctx, value);- } else if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) {- int func_ret_code;- value = s->func_state.frame.cur_sp[-1];- s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;- func_ret_code = JS_VALUE_GET_INT(func_ret);- switch(func_ret_code) {- case FUNC_RET_YIELD:- case FUNC_RET_YIELD_STAR:- if (func_ret_code == FUNC_RET_YIELD_STAR)- s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR;- else- s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD;- js_async_generator_resolve(ctx, s, value, FALSE);- JS_FreeValue(ctx, value);- break;- case FUNC_RET_AWAIT:- js_async_generator_await(ctx, s, value);- JS_FreeValue(ctx, value);- goto done;- default:- abort();- }- } else {- assert(JS_IsUndefined(func_ret));- /* end of function */- value = s->func_state.frame.cur_sp[-1];- s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;- js_async_generator_complete(ctx, s);- js_async_generator_resolve(ctx, s, value, TRUE);- JS_FreeValue(ctx, value);- }- break;- default:- abort();- }- }- done: ;-}--static JSValue js_async_generator_resolve_function(JSContext *ctx,- JSValueConst this_obj,- int argc, JSValueConst *argv,- int magic, JSValue *func_data)-{- BOOL is_reject = magic & 1;- JSAsyncGeneratorData *s = JS_GetOpaque(func_data[0], JS_CLASS_ASYNC_GENERATOR);- JSValueConst arg = argv[0];-- /* XXX: what if s == NULL */-- if (magic >= 2) {- /* resume next case in AWAITING_RETURN state */- assert(s->state == JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN ||- s->state == JS_ASYNC_GENERATOR_STATE_COMPLETED);- s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;- if (is_reject) {- js_async_generator_reject(ctx, s, arg);- } else {- js_async_generator_resolve(ctx, s, arg, TRUE);- }- } else {- /* restart function execution after await() */- assert(s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING);- s->func_state.throw_flag = is_reject;- if (is_reject) {- JS_Throw(ctx, JS_DupValue(ctx, arg));- } else {- /* return value of await */- s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg);- }- js_async_generator_resume_next(ctx, s);- }- return JS_UNDEFINED;-}--/* magic = GEN_MAGIC_x */-static JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv,- int magic)-{- JSAsyncGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_GENERATOR);- JSValue promise, resolving_funcs[2];- JSAsyncGeneratorRequest *req;-- promise = JS_NewPromiseCapability(ctx, resolving_funcs);- if (JS_IsException(promise))- return JS_EXCEPTION;- if (!s) {- JSValue err, res2;- JS_ThrowTypeError(ctx, "not an AsyncGenerator object");- err = JS_GetException(ctx);- res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,- 1, (JSValueConst *)&err);- JS_FreeValue(ctx, err);- JS_FreeValue(ctx, res2);- JS_FreeValue(ctx, resolving_funcs[0]);- JS_FreeValue(ctx, resolving_funcs[1]);- return promise;- }- req = js_mallocz(ctx, sizeof(*req));- if (!req)- goto fail;- req->completion_type = magic;- req->result = JS_DupValue(ctx, argv[0]);- req->promise = JS_DupValue(ctx, promise);- req->resolving_funcs[0] = resolving_funcs[0];- req->resolving_funcs[1] = resolving_funcs[1];- list_add_tail(&req->link, &s->queue);- if (s->state != JS_ASYNC_GENERATOR_STATE_EXECUTING) {- js_async_generator_resume_next(ctx, s);- }- return promise;- fail:- JS_FreeValue(ctx, resolving_funcs[0]);- JS_FreeValue(ctx, resolving_funcs[1]);- JS_FreeValue(ctx, promise);- return JS_EXCEPTION;-}--static JSValue js_async_generator_function_call(JSContext *ctx, JSValueConst func_obj,- JSValueConst this_obj,- int argc, JSValueConst *argv,- int flags)-{- JSValue obj, func_ret;- JSAsyncGeneratorData *s;-- s = js_mallocz(ctx, sizeof(*s));- if (!s)- return JS_EXCEPTION;- s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START;- init_list_head(&s->queue);- if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {- s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;- goto fail;- }-- /* execute the function up to 'OP_initial_yield' (no yield nor- await are possible) */- func_ret = async_func_resume(ctx, &s->func_state);- if (JS_IsException(func_ret))- goto fail;- JS_FreeValue(ctx, func_ret);-- obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_ASYNC_GENERATOR);- if (JS_IsException(obj))- goto fail;- s->generator = JS_VALUE_GET_OBJ(obj);- JS_SetOpaque(obj, s);- return obj;- fail:- js_async_generator_free(ctx->rt, s);- return JS_EXCEPTION;-}--/* JS parser */--enum {- TOK_NUMBER = -128,- TOK_STRING,- TOK_TEMPLATE,- TOK_IDENT,- TOK_REGEXP,- /* warning: order matters (see js_parse_assign_expr) */- TOK_MUL_ASSIGN,- TOK_DIV_ASSIGN,- TOK_MOD_ASSIGN,- TOK_PLUS_ASSIGN,- TOK_MINUS_ASSIGN,- TOK_SHL_ASSIGN,- TOK_SAR_ASSIGN,- TOK_SHR_ASSIGN,- TOK_AND_ASSIGN,- TOK_XOR_ASSIGN,- TOK_OR_ASSIGN,-#ifdef CONFIG_BIGNUM- TOK_MATH_POW_ASSIGN,-#endif- TOK_POW_ASSIGN,- TOK_LAND_ASSIGN,- TOK_LOR_ASSIGN,- TOK_DOUBLE_QUESTION_MARK_ASSIGN,- TOK_DEC,- TOK_INC,- TOK_SHL,- TOK_SAR,- TOK_SHR,- TOK_LT,- TOK_LTE,- TOK_GT,- TOK_GTE,- TOK_EQ,- TOK_STRICT_EQ,- TOK_NEQ,- TOK_STRICT_NEQ,- TOK_LAND,- TOK_LOR,-#ifdef CONFIG_BIGNUM- TOK_MATH_POW,-#endif- TOK_POW,- TOK_ARROW,- TOK_ELLIPSIS,- TOK_DOUBLE_QUESTION_MARK,- TOK_QUESTION_MARK_DOT,- TOK_ERROR,- TOK_PRIVATE_NAME,- TOK_EOF,- /* keywords: WARNING: same order as atoms */- TOK_NULL, /* must be first */- TOK_FALSE,- TOK_TRUE,- TOK_IF,- TOK_ELSE,- TOK_RETURN,- TOK_VAR,- TOK_THIS,- TOK_DELETE,- TOK_VOID,- TOK_TYPEOF,- TOK_NEW,- TOK_IN,- TOK_INSTANCEOF,- TOK_DO,- TOK_WHILE,- TOK_FOR,- TOK_BREAK,- TOK_CONTINUE,- TOK_SWITCH,- TOK_CASE,- TOK_DEFAULT,- TOK_THROW,- TOK_TRY,- TOK_CATCH,- TOK_FINALLY,- TOK_FUNCTION,- TOK_DEBUGGER,- TOK_WITH,- /* FutureReservedWord */- TOK_CLASS,- TOK_CONST,- TOK_ENUM,- TOK_EXPORT,- TOK_EXTENDS,- TOK_IMPORT,- TOK_SUPER,- /* FutureReservedWords when parsing strict mode code */- TOK_IMPLEMENTS,- TOK_INTERFACE,- TOK_LET,- TOK_PACKAGE,- TOK_PRIVATE,- TOK_PROTECTED,- TOK_PUBLIC,- TOK_STATIC,- TOK_YIELD,- TOK_AWAIT, /* must be last */- TOK_OF, /* only used for js_parse_skip_parens_token() */-};--#define TOK_FIRST_KEYWORD TOK_NULL-#define TOK_LAST_KEYWORD TOK_AWAIT--/* unicode code points */-#define CP_NBSP 0x00a0-#define CP_BOM 0xfeff--#define CP_LS 0x2028-#define CP_PS 0x2029--typedef struct BlockEnv {- struct BlockEnv *prev;- JSAtom label_name; /* JS_ATOM_NULL if none */- int label_break; /* -1 if none */- int label_cont; /* -1 if none */- int drop_count; /* number of stack elements to drop */- int label_finally; /* -1 if none */- int scope_level;- int has_iterator;-} BlockEnv;--typedef struct JSHoistedDef {- int cpool_idx; /* -1 means variable global definition */- uint8_t force_init : 1; /* initialize to undefined */- uint8_t is_lexical : 1; /* global let/const definition */- uint8_t is_const : 1; /* const definition */- int var_idx; /* function object index if cpool_idx >= 0 */- int scope_level; /* scope of definition */- JSAtom var_name; /* variable name if cpool_idx < 0 */-} JSHoistedDef;--typedef struct RelocEntry {- struct RelocEntry *next;- uint32_t addr; /* address to patch */- int size; /* address size: 1, 2 or 4 bytes */-} RelocEntry;--typedef struct JumpSlot {- int op;- int size;- int pos;- int label;-} JumpSlot;--typedef struct LabelSlot {- int ref_count;- int pos; /* phase 1 address, -1 means not resolved yet */- int pos2; /* phase 2 address, -1 means not resolved yet */- int addr; /* phase 3 address, -1 means not resolved yet */- RelocEntry *first_reloc;-} LabelSlot;--typedef struct LineNumberSlot {- uint32_t pc;- int line_num;-} LineNumberSlot;--typedef enum JSParseFunctionEnum {- JS_PARSE_FUNC_STATEMENT,- JS_PARSE_FUNC_VAR,- JS_PARSE_FUNC_EXPR,- JS_PARSE_FUNC_ARROW,- JS_PARSE_FUNC_GETTER,- JS_PARSE_FUNC_SETTER,- JS_PARSE_FUNC_METHOD,- JS_PARSE_FUNC_CLASS_CONSTRUCTOR,- JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR,-} JSParseFunctionEnum;--typedef enum JSParseExportEnum {- JS_PARSE_EXPORT_NONE,- JS_PARSE_EXPORT_NAMED,- JS_PARSE_EXPORT_DEFAULT,-} JSParseExportEnum;--typedef struct JSFunctionDef {- JSContext *ctx;- struct JSFunctionDef *parent;- int parent_cpool_idx; /* index in the constant pool of the parent- or -1 if none */- int parent_scope_level; /* scope level in parent at point of definition */- struct list_head child_list; /* list of JSFunctionDef.link */- struct list_head link;-- BOOL is_eval; /* TRUE if eval code */- int eval_type; /* only valid if is_eval = TRUE */- BOOL is_global_var; /* TRUE if variables are not defined locally:- eval global, eval module or non strict eval */- BOOL is_func_expr; /* TRUE if function expression */- BOOL has_home_object; /* TRUE if the home object is available */- BOOL has_prototype; /* true if a prototype field is necessary */- BOOL has_simple_parameter_list;- BOOL has_use_strict; /* to reject directive in special cases */- BOOL has_eval_call; /* true if the function contains a call to eval() */- BOOL has_arguments_binding; /* true if the 'arguments' binding is- available in the function */- BOOL has_this_binding; /* true if the 'this' and new.target binding are- available in the function */- BOOL new_target_allowed; /* true if the 'new.target' does not- throw a syntax error */- BOOL super_call_allowed; /* true if super() is allowed */- BOOL super_allowed; /* true if super. or super[] is allowed */- BOOL arguments_allowed; /* true if the 'arguments' identifier is allowed */- BOOL is_derived_class_constructor;- BOOL in_function_body;- BOOL backtrace_barrier;- JSFunctionKindEnum func_kind : 8;- JSParseFunctionEnum func_type : 8;- uint8_t js_mode; /* bitmap of JS_MODE_x */- JSAtom func_name; /* JS_ATOM_NULL if no name */-- JSVarDef *vars;- int var_size; /* allocated size for vars[] */- int var_count;- JSVarDef *args;- int arg_size; /* allocated size for args[] */- int arg_count; /* number of arguments */- int defined_arg_count;- int var_object_idx; /* -1 if none */- int arguments_var_idx; /* -1 if none */- int func_var_idx; /* variable containing the current function (-1- if none, only used if is_func_expr is true) */- int eval_ret_idx; /* variable containing the return value of the eval, -1 if none */- int this_var_idx; /* variable containg the 'this' value, -1 if none */- int new_target_var_idx; /* variable containg the 'new.target' value, -1 if none */- int this_active_func_var_idx; /* variable containg the 'this.active_func' value, -1 if none */- int home_object_var_idx;- BOOL need_home_object;- - int scope_level; /* index into fd->scopes if the current lexical scope */- int scope_first; /* index into vd->vars of first lexically scoped variable */- int scope_size; /* allocated size of fd->scopes array */- int scope_count; /* number of entries used in the fd->scopes array */- JSVarScope *scopes;- JSVarScope def_scope_array[4];-- int hoisted_def_count;- int hoisted_def_size;- JSHoistedDef *hoisted_def;-- DynBuf byte_code;- int last_opcode_pos; /* -1 if no last opcode */- int last_opcode_line_num;- BOOL use_short_opcodes; /* true if short opcodes are used in byte_code */- - LabelSlot *label_slots;- int label_size; /* allocated size for label_slots[] */- int label_count;- BlockEnv *top_break; /* break/continue label stack */-- /* constant pool (strings, functions, numbers) */- JSValue *cpool;- int cpool_count;- int cpool_size;-- /* list of variables in the closure */- int closure_var_count;- int closure_var_size;- JSClosureVar *closure_var;-- JumpSlot *jump_slots;- int jump_size;- int jump_count;-- LineNumberSlot *line_number_slots;- int line_number_size;- int line_number_count;- int line_number_last;- int line_number_last_pc;-- /* pc2line table */- JSAtom filename;- int line_num;- DynBuf pc2line;-- char *source; /* raw source, utf-8 encoded */- int source_len;-- JSModuleDef *module; /* != NULL when parsing a module */-} JSFunctionDef;--typedef struct JSToken {- int val;- int line_num; /* line number of token start */- const uint8_t *ptr;- union {- struct {- JSValue str;- int sep;- } str;- struct {- JSValue val;-#ifdef CONFIG_BIGNUM- slimb_t exponent; /* may be != 0 only if val is a float */-#endif- } num;- struct {- JSAtom atom;- BOOL has_escape;- BOOL is_reserved;- } ident;- struct {- JSValue body;- JSValue flags;- } regexp;- } u;-} JSToken;--typedef struct JSParseState {- JSContext *ctx;- int last_line_num; /* line number of last token */- int line_num; /* line number of current offset */- const char *filename;- JSToken token;- BOOL got_lf; /* true if got line feed before the current token */- const uint8_t *last_ptr;- const uint8_t *buf_ptr;- const uint8_t *buf_end;-- /* current function code */- JSFunctionDef *cur_func;- BOOL is_module; /* parsing a module */- BOOL allow_html_comments;- BOOL ext_json; /* true if accepting JSON superset */-} JSParseState;--typedef struct JSOpCode {-#ifdef DUMP_BYTECODE- const char *name;-#endif- uint8_t size; /* in bytes */- /* the opcodes remove n_pop items from the top of the stack, then- pushes n_push items */- uint8_t n_pop;- uint8_t n_push;- uint8_t fmt;-} JSOpCode;--static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = {-#define FMT(f)-#ifdef DUMP_BYTECODE-#define DEF(id, size, n_pop, n_push, f) { #id, size, n_pop, n_push, OP_FMT_ ## f },-#else-#define DEF(id, size, n_pop, n_push, f) { size, n_pop, n_push, OP_FMT_ ## f },-#endif-#include "quickjs-opcode.h"-#undef DEF-#undef FMT-};--#if SHORT_OPCODES-/* After the final compilation pass, short opcodes are used. Their- opcodes overlap with the temporary opcodes which cannot appear in- the final bytecode. Their description is after the temporary- opcodes in opcode_info[]. */-#define short_opcode_info(op) \- opcode_info[(op) >= OP_TEMP_START ? \- (op) + (OP_TEMP_END - OP_TEMP_START) : (op)]-#else-#define short_opcode_info(op) opcode_info[op]-#endif--static __exception int next_token(JSParseState *s);--static void free_token(JSParseState *s, JSToken *token)-{- switch(token->val) {-#ifdef CONFIG_BIGNUM- case TOK_NUMBER:- JS_FreeValue(s->ctx, token->u.num.val);- break;-#endif- case TOK_STRING:- case TOK_TEMPLATE:- JS_FreeValue(s->ctx, token->u.str.str);- break;- case TOK_REGEXP:- JS_FreeValue(s->ctx, token->u.regexp.body);- JS_FreeValue(s->ctx, token->u.regexp.flags);- break;- case TOK_IDENT:- case TOK_PRIVATE_NAME:- JS_FreeAtom(s->ctx, token->u.ident.atom);- break;- default:- if (token->val >= TOK_FIRST_KEYWORD &&- token->val <= TOK_LAST_KEYWORD) {- JS_FreeAtom(s->ctx, token->u.ident.atom);- }- break;- }-}--static void __attribute((unused)) dump_token(JSParseState *s,- const JSToken *token)-{- switch(token->val) {- case TOK_NUMBER:- {- double d;- JS_ToFloat64(s->ctx, &d, token->u.num.val); /* no exception possible */- printf("number: %.14g\n", d);- }- break;- case TOK_IDENT:- dump_atom:- {- char buf[ATOM_GET_STR_BUF_SIZE];- printf("ident: '%s'\n",- JS_AtomGetStr(s->ctx, buf, sizeof(buf), token->u.ident.atom));- }- break;- case TOK_STRING:- {- const char *str;- /* XXX: quote the string */- str = JS_ToCString(s->ctx, token->u.str.str);- printf("string: '%s'\n", str);- JS_FreeCString(s->ctx, str);- }- break;- case TOK_TEMPLATE:- {- const char *str;- str = JS_ToCString(s->ctx, token->u.str.str);- printf("template: `%s`\n", str);- JS_FreeCString(s->ctx, str);- }- break;- case TOK_REGEXP:- {- const char *str, *str2;- str = JS_ToCString(s->ctx, token->u.regexp.body);- str2 = JS_ToCString(s->ctx, token->u.regexp.flags);- printf("regexp: '%s' '%s'\n", str, str2);- JS_FreeCString(s->ctx, str);- JS_FreeCString(s->ctx, str2);- }- break;- case TOK_EOF:- printf("eof\n");- break;- default:- if (s->token.val >= TOK_NULL && s->token.val <= TOK_LAST_KEYWORD) {- goto dump_atom;- } else if (s->token.val >= 256) {- printf("token: %d\n", token->val);- } else {- printf("token: '%c'\n", token->val);- }- break;- }-}--int __attribute__((format(printf, 2, 3))) js_parse_error(JSParseState *s, const char *fmt, ...)-{- JSContext *ctx = s->ctx;- va_list ap;- int backtrace_flags;- - va_start(ap, fmt);- JS_ThrowError2(ctx, JS_SYNTAX_ERROR, fmt, ap, FALSE);- va_end(ap);- backtrace_flags = 0;- if (s->cur_func && s->cur_func->backtrace_barrier)- backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL;- build_backtrace(ctx, ctx->rt->current_exception, s->filename, s->line_num,- backtrace_flags);- return -1;-}--static int js_parse_expect(JSParseState *s, int tok)-{- if (s->token.val != tok) {- /* XXX: dump token correctly in all cases */- return js_parse_error(s, "expecting '%c'", tok);- }- return next_token(s);-}--static int js_parse_expect_semi(JSParseState *s)-{- if (s->token.val != ';') {- /* automatic insertion of ';' */- if (s->token.val == TOK_EOF || s->token.val == '}' || s->got_lf) {- return 0;- }- return js_parse_error(s, "expecting '%c'", ';');- }- return next_token(s);-}--static int js_parse_error_reserved_identifier(JSParseState *s)-{- char buf1[ATOM_GET_STR_BUF_SIZE];- return js_parse_error(s, "'%s' is a reserved identifier",- JS_AtomGetStr(s->ctx, buf1, sizeof(buf1),- s->token.u.ident.atom));-}--static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p)-{- uint32_t c;- StringBuffer b_s, *b = &b_s;-- /* p points to the first byte of the template part */- if (string_buffer_init(s->ctx, b, 32))- goto fail;- for(;;) {- if (p >= s->buf_end)- goto unexpected_eof;- c = *p++;- if (c == '`') {- /* template end part */- break;- }- if (c == '$' && *p == '{') {- /* template start or middle part */- p++;- break;- }- if (c == '\\') {- if (string_buffer_putc8(b, c))- goto fail;- if (p >= s->buf_end)- goto unexpected_eof;- c = *p++;- }- /* newline sequences are normalized as single '\n' bytes */- if (c == '\r') {- if (*p == '\n')- p++;- c = '\n';- }- if (c == '\n') {- s->line_num++;- } else if (c >= 0x80) {- const uint8_t *p_next;- c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);- if (c > 0x10FFFF) {- js_parse_error(s, "invalid UTF-8 sequence");- goto fail;- }- p = p_next;- }- if (string_buffer_putc(b, c))- goto fail;- }- s->token.val = TOK_TEMPLATE;- s->token.u.str.sep = c;- s->token.u.str.str = string_buffer_end(b);- s->buf_ptr = p;- return 0;-- unexpected_eof:- js_parse_error(s, "unexpected end of string");- fail:- string_buffer_free(b);- return -1;-}--static __exception int js_parse_string(JSParseState *s, int sep,- BOOL do_throw, const uint8_t *p,- JSToken *token, const uint8_t **pp)-{- int ret;- uint32_t c;- StringBuffer b_s, *b = &b_s;-- /* string */- if (string_buffer_init(s->ctx, b, 32))- goto fail;- for(;;) {- if (p >= s->buf_end)- goto invalid_char;- c = *p;- if (c < 0x20) {- if (!s->cur_func) {- if (do_throw)- js_parse_error(s, "invalid character in a JSON string");- goto fail;- }- if (sep == '`') {- if (c == '\r') {- if (p[1] == '\n')- p++;- c = '\n';- }- /* do not update s->line_num */- } else if (c == '\n' || c == '\r')- goto invalid_char;- }- p++;- if (c == sep)- break;- if (c == '$' && *p == '{' && sep == '`') {- /* template start or middle part */- p++;- break;- }- if (c == '\\') {- c = *p;- /* XXX: need a specific JSON case to avoid- accepting invalid escapes */- switch(c) {- case '\0':- if (p >= s->buf_end)- goto invalid_char;- p++;- break;- case '\'':- case '\"':- case '\\':- p++;- break;- case '\r': /* accept DOS and MAC newline sequences */- if (p[1] == '\n') {- p++;- }- /* fall thru */- case '\n':- /* ignore escaped newline sequence */- p++;- if (sep != '`')- s->line_num++;- continue;- default:- if (c >= '0' && c <= '9') {- if (!s->cur_func)- goto invalid_escape; /* JSON case */- if (!(s->cur_func->js_mode & JS_MODE_STRICT) && sep != '`')- goto parse_escape;- if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) {- p++;- c = '\0';- } else {- if (c >= '8' || sep == '`') {- /* Note: according to ES2021, \8 and \9 are not- accepted in strict mode or in templates. */- goto invalid_escape;- } else {- if (do_throw)- js_parse_error(s, "octal escape sequences are not allowed in strict mode");- }- goto fail;- }- } else if (c >= 0x80) {- const uint8_t *p_next;- c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);- if (c > 0x10FFFF) {- goto invalid_utf8;- }- p = p_next;- /* LS or PS are skipped */- if (c == CP_LS || c == CP_PS)- continue;- } else {- parse_escape:- ret = lre_parse_escape(&p, TRUE);- if (ret == -1) {- invalid_escape:- if (do_throw)- js_parse_error(s, "malformed escape sequence in string literal");- goto fail;- } else if (ret < 0) {- /* ignore the '\' (could output a warning) */- p++;- } else {- c = ret;- }- }- break;- }- } else if (c >= 0x80) {- const uint8_t *p_next;- c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);- if (c > 0x10FFFF)- goto invalid_utf8;- p = p_next;- }- if (string_buffer_putc(b, c))- goto fail;- }- token->val = TOK_STRING;- token->u.str.sep = c;- token->u.str.str = string_buffer_end(b);- *pp = p;- return 0;-- invalid_utf8:- if (do_throw)- js_parse_error(s, "invalid UTF-8 sequence");- goto fail;- invalid_char:- if (do_throw)- js_parse_error(s, "unexpected end of string");- fail:- string_buffer_free(b);- return -1;-}--static inline BOOL token_is_pseudo_keyword(JSParseState *s, JSAtom atom) {- return s->token.val == TOK_IDENT && s->token.u.ident.atom == atom &&- !s->token.u.ident.has_escape;-}--static __exception int js_parse_regexp(JSParseState *s)-{- const uint8_t *p;- BOOL in_class;- StringBuffer b_s, *b = &b_s;- StringBuffer b2_s, *b2 = &b2_s;- uint32_t c;-- p = s->buf_ptr;- p++;- in_class = FALSE;- if (string_buffer_init(s->ctx, b, 32))- return -1;- if (string_buffer_init(s->ctx, b2, 1))- goto fail;- for(;;) {- if (p >= s->buf_end) {- eof_error:- js_parse_error(s, "unexpected end of regexp");- goto fail;- }- c = *p++;- if (c == '\n' || c == '\r') {- goto eol_error;- } else if (c == '/') {- if (!in_class)- break;- } else if (c == '[') {- in_class = TRUE;- } else if (c == ']') {- /* XXX: incorrect as the first character in a class */- in_class = FALSE;- } else if (c == '\\') {- if (string_buffer_putc8(b, c))- goto fail;- c = *p++;- if (c == '\n' || c == '\r')- goto eol_error;- else if (c == '\0' && p >= s->buf_end)- goto eof_error;- else if (c >= 0x80) {- const uint8_t *p_next;- c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);- if (c > 0x10FFFF) {- goto invalid_utf8;- }- p = p_next;- if (c == CP_LS || c == CP_PS)- goto eol_error;- }- } else if (c >= 0x80) {- const uint8_t *p_next;- c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);- if (c > 0x10FFFF) {- invalid_utf8:- js_parse_error(s, "invalid UTF-8 sequence");- goto fail;- }- p = p_next;- /* LS or PS are considered as line terminator */- if (c == CP_LS || c == CP_PS) {- eol_error:- js_parse_error(s, "unexpected line terminator in regexp");- goto fail;- }- }- if (string_buffer_putc(b, c))- goto fail;- }-- /* flags */- for(;;) {- const uint8_t *p_next = p;- c = *p_next++;- if (c >= 0x80) {- c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);- if (c > 0x10FFFF) {- goto invalid_utf8;- }- }- if (!lre_js_is_ident_next(c))- break;- if (string_buffer_putc(b2, c))- goto fail;- p = p_next;- }-- s->token.val = TOK_REGEXP;- s->token.u.regexp.body = string_buffer_end(b);- s->token.u.regexp.flags = string_buffer_end(b2);- s->buf_ptr = p;- return 0;- fail:- string_buffer_free(b);- string_buffer_free(b2);- return -1;-}--static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize,- char *static_buf)-{- char *buf, *new_buf;- size_t size, new_size;- - buf = *pbuf;- size = *psize;- if (size >= (SIZE_MAX / 3) * 2)- new_size = SIZE_MAX;- else- new_size = size + (size >> 1);- if (buf == static_buf) {- new_buf = js_malloc(ctx, new_size);- if (!new_buf)- return -1;- memcpy(new_buf, buf, size);- } else {- new_buf = js_realloc(ctx, buf, new_size);- if (!new_buf)- return -1;- }- *pbuf = new_buf;- *psize = new_size;- return 0;-}--/* 'c' is the first character. Return JS_ATOM_NULL in case of error */-static JSAtom parse_ident(JSParseState *s, const uint8_t **pp,- BOOL *pident_has_escape, int c, BOOL is_private)-{- const uint8_t *p, *p1;- char ident_buf[128], *buf;- size_t ident_size, ident_pos;- JSAtom atom;- - p = *pp;- buf = ident_buf;- ident_size = sizeof(ident_buf);- ident_pos = 0;- if (is_private)- buf[ident_pos++] = '#';- for(;;) {- p1 = p;- - if (c < 128) {- buf[ident_pos++] = c;- } else {- ident_pos += unicode_to_utf8((uint8_t*)buf + ident_pos, c);- }- c = *p1++;- if (c == '\\' && *p1 == 'u') {- c = lre_parse_escape(&p1, TRUE);- *pident_has_escape = TRUE;- } else if (c >= 128) {- c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1);- }- if (!lre_js_is_ident_next(c))- break;- p = p1;- if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) {- if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) {- atom = JS_ATOM_NULL;- goto done;- }- }- }- atom = JS_NewAtomLen(s->ctx, buf, ident_pos);- done:- if (unlikely(buf != ident_buf))- js_free(s->ctx, buf);- *pp = p;- return atom;-}---static __exception int next_token(JSParseState *s)-{- const uint8_t *p;- int c;- BOOL ident_has_escape;- JSAtom atom;- - if (js_check_stack_overflow(s->ctx->rt, 0)) {- return js_parse_error(s, "stack overflow");- }- - free_token(s, &s->token);-- p = s->last_ptr = s->buf_ptr;- s->got_lf = FALSE;- s->last_line_num = s->token.line_num;- redo:- s->token.line_num = s->line_num;- s->token.ptr = p;- c = *p;- switch(c) {- case 0:- if (p >= s->buf_end) {- s->token.val = TOK_EOF;- } else {- goto def_token;- }- break;- case '`':- if (js_parse_template_part(s, p + 1))- goto fail;- p = s->buf_ptr;- break;- case '\'':- case '\"':- if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p))- goto fail;- break;- case '\r': /* accept DOS and MAC newline sequences */- if (p[1] == '\n') {- p++;- }- /* fall thru */- case '\n':- p++;- line_terminator:- s->got_lf = TRUE;- s->line_num++;- goto redo;- case '\f':- case '\v':- case ' ':- case '\t':- p++;- goto redo;- case '/':- if (p[1] == '*') {- /* comment */- p += 2;- for(;;) {- if (*p == '\0' && p >= s->buf_end) {- js_parse_error(s, "unexpected end of comment");- goto fail;- }- if (p[0] == '*' && p[1] == '/') {- p += 2;- break;- }- if (*p == '\n') {- s->line_num++;- s->got_lf = TRUE; /* considered as LF for ASI */- p++;- } else if (*p == '\r') {- s->got_lf = TRUE; /* considered as LF for ASI */- p++;- } else if (*p >= 0x80) {- c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);- if (c == CP_LS || c == CP_PS) {- s->got_lf = TRUE; /* considered as LF for ASI */- } else if (c == -1) {- p++; /* skip invalid UTF-8 */- }- } else {- p++;- }- }- goto redo;- } else if (p[1] == '/') {- /* line comment */- p += 2;- skip_line_comment:- for(;;) {- if (*p == '\0' && p >= s->buf_end)- break;- if (*p == '\r' || *p == '\n')- break;- if (*p >= 0x80) {- c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);- /* LS or PS are considered as line terminator */- if (c == CP_LS || c == CP_PS) {- break;- } else if (c == -1) {- p++; /* skip invalid UTF-8 */- }- } else {- p++;- }- }- goto redo;- } else if (p[1] == '=') {- p += 2;- s->token.val = TOK_DIV_ASSIGN;- } else {- p++;- s->token.val = c;- }- break;- case '\\':- if (p[1] == 'u') {- const uint8_t *p1 = p + 1;- int c1 = lre_parse_escape(&p1, TRUE);- if (c1 >= 0 && lre_js_is_ident_first(c1)) {- c = c1;- p = p1;- ident_has_escape = TRUE;- goto has_ident;- } else {- /* XXX: syntax error? */- }- }- goto def_token;- case 'a': case 'b': case 'c': case 'd':- case 'e': case 'f': case 'g': case 'h':- case 'i': case 'j': case 'k': case 'l':- case 'm': case 'n': case 'o': case 'p':- case 'q': case 'r': case 's': case 't':- case 'u': case 'v': case 'w': case 'x':- case 'y': case 'z': - case 'A': case 'B': case 'C': case 'D':- case 'E': case 'F': case 'G': case 'H':- case 'I': case 'J': case 'K': case 'L':- case 'M': case 'N': case 'O': case 'P':- case 'Q': case 'R': case 'S': case 'T':- case 'U': case 'V': case 'W': case 'X':- case 'Y': case 'Z': - case '_':- case '$':- /* identifier */- p++;- ident_has_escape = FALSE;- has_ident:- atom = parse_ident(s, &p, &ident_has_escape, c, FALSE);- if (atom == JS_ATOM_NULL)- goto fail;- s->token.u.ident.atom = atom;- s->token.u.ident.has_escape = ident_has_escape;- s->token.u.ident.is_reserved = FALSE;- if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD ||- (s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD &&- (s->cur_func->js_mode & JS_MODE_STRICT)) ||- (s->token.u.ident.atom == JS_ATOM_yield &&- ((s->cur_func->func_kind & JS_FUNC_GENERATOR) ||- (s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&- !s->cur_func->in_function_body && s->cur_func->parent &&- (s->cur_func->parent->func_kind & JS_FUNC_GENERATOR)))) ||- (s->token.u.ident.atom == JS_ATOM_await &&- (s->is_module ||- (((s->cur_func->func_kind & JS_FUNC_ASYNC) ||- (s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&- !s->cur_func->in_function_body && s->cur_func->parent &&- (s->cur_func->parent->func_kind & JS_FUNC_ASYNC))))))) {- if (ident_has_escape) {- s->token.u.ident.is_reserved = TRUE;- s->token.val = TOK_IDENT;- } else {- /* The keywords atoms are pre allocated */- s->token.val = s->token.u.ident.atom - 1 + TOK_FIRST_KEYWORD;- }- } else {- s->token.val = TOK_IDENT;- }- break;- case '#':- /* private name */- {- const uint8_t *p1;- p++;- p1 = p;- c = *p1++;- if (c == '\\' && *p1 == 'u') {- c = lre_parse_escape(&p1, TRUE);- } else if (c >= 128) {- c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1);- }- if (!lre_js_is_ident_first(c)) {- js_parse_error(s, "invalid first character of private name");- goto fail;- }- p = p1;- ident_has_escape = FALSE; /* not used */- atom = parse_ident(s, &p, &ident_has_escape, c, TRUE);- if (atom == JS_ATOM_NULL)- goto fail;- s->token.u.ident.atom = atom;- s->token.val = TOK_PRIVATE_NAME;- }- break;- case '.':- if (p[1] == '.' && p[2] == '.') {- p += 3;- s->token.val = TOK_ELLIPSIS;- break;- }- if (p[1] >= '0' && p[1] <= '9') {- goto parse_number;- } else {- goto def_token;- }- break;- case '0':- /* in strict mode, octal literals are not accepted */- if (is_digit(p[1]) && (s->cur_func->js_mode & JS_MODE_STRICT)) {- js_parse_error(s, "octal literals are deprecated in strict mode");- goto fail;- }- goto parse_number;- case '1': case '2': case '3': case '4':- case '5': case '6': case '7': case '8':- case '9': - /* number */- parse_number:- {- JSValue ret;- const uint8_t *p1;- int flags, radix;- flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL |- ATOD_ACCEPT_UNDERSCORES;-#ifdef CONFIG_BIGNUM- flags |= ATOD_ACCEPT_SUFFIX;- if (s->cur_func->js_mode & JS_MODE_MATH) {- flags |= ATOD_MODE_BIGINT;- if (s->cur_func->js_mode & JS_MODE_MATH)- flags |= ATOD_TYPE_BIG_FLOAT;- }-#endif- radix = 0;-#ifdef CONFIG_BIGNUM- s->token.u.num.exponent = 0;- ret = js_atof2(s->ctx, (const char *)p, (const char **)&p, radix,- flags, &s->token.u.num.exponent);-#else- ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix,- flags);-#endif- if (JS_IsException(ret))- goto fail;- /* reject `10instanceof Number` */- if (JS_VALUE_IS_NAN(ret) ||- lre_js_is_ident_next(unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1))) {- JS_FreeValue(s->ctx, ret);- js_parse_error(s, "invalid number literal");- goto fail;- }- s->token.val = TOK_NUMBER;- s->token.u.num.val = ret;- }- break;- case '*':- if (p[1] == '=') {- p += 2;- s->token.val = TOK_MUL_ASSIGN;- } else if (p[1] == '*') {- if (p[2] == '=') {- p += 3;- s->token.val = TOK_POW_ASSIGN;- } else {- p += 2;- s->token.val = TOK_POW;- }- } else {- goto def_token;- }- break;- case '%':- if (p[1] == '=') {- p += 2;- s->token.val = TOK_MOD_ASSIGN;- } else {- goto def_token;- }- break;- case '+':- if (p[1] == '=') {- p += 2;- s->token.val = TOK_PLUS_ASSIGN;- } else if (p[1] == '+') {- p += 2;- s->token.val = TOK_INC;- } else {- goto def_token;- }- break;- case '-':- if (p[1] == '=') {- p += 2;- s->token.val = TOK_MINUS_ASSIGN;- } else if (p[1] == '-') {- if (s->allow_html_comments &&- p[2] == '>' && s->last_line_num != s->line_num) {- /* Annex B: `-->` at beginning of line is an html comment end.- It extends to the end of the line.- */- goto skip_line_comment;- }- p += 2;- s->token.val = TOK_DEC;- } else {- goto def_token;- }- break;- case '<':- if (p[1] == '=') {- p += 2;- s->token.val = TOK_LTE;- } else if (p[1] == '<') {- if (p[2] == '=') {- p += 3;- s->token.val = TOK_SHL_ASSIGN;- } else {- p += 2;- s->token.val = TOK_SHL;- }- } else if (s->allow_html_comments &&- p[1] == '!' && p[2] == '-' && p[3] == '-') {- /* Annex B: handle `<!--` single line html comments */- goto skip_line_comment;- } else {- goto def_token;- }- break;- case '>':- if (p[1] == '=') {- p += 2;- s->token.val = TOK_GTE;- } else if (p[1] == '>') {- if (p[2] == '>') {- if (p[3] == '=') {- p += 4;- s->token.val = TOK_SHR_ASSIGN;- } else {- p += 3;- s->token.val = TOK_SHR;- }- } else if (p[2] == '=') {- p += 3;- s->token.val = TOK_SAR_ASSIGN;- } else {- p += 2;- s->token.val = TOK_SAR;- }- } else {- goto def_token;- }- break;- case '=':- if (p[1] == '=') {- if (p[2] == '=') {- p += 3;- s->token.val = TOK_STRICT_EQ;- } else {- p += 2;- s->token.val = TOK_EQ;- }- } else if (p[1] == '>') {- p += 2;- s->token.val = TOK_ARROW;- } else {- goto def_token;- }- break;- case '!':- if (p[1] == '=') {- if (p[2] == '=') {- p += 3;- s->token.val = TOK_STRICT_NEQ;- } else {- p += 2;- s->token.val = TOK_NEQ;- }- } else {- goto def_token;- }- break;- case '&':- if (p[1] == '=') {- p += 2;- s->token.val = TOK_AND_ASSIGN;- } else if (p[1] == '&') {- if (p[2] == '=') {- p += 3;- s->token.val = TOK_LAND_ASSIGN;- } else {- p += 2;- s->token.val = TOK_LAND;- }- } else {- goto def_token;- }- break;-#ifdef CONFIG_BIGNUM- /* in math mode, '^' is the power operator. '^^' is always the- xor operator and '**' is always the power operator */- case '^':- if (p[1] == '=') {- p += 2;- if (s->cur_func->js_mode & JS_MODE_MATH)- s->token.val = TOK_MATH_POW_ASSIGN;- else- s->token.val = TOK_XOR_ASSIGN;- } else if (p[1] == '^') {- if (p[2] == '=') {- p += 3;- s->token.val = TOK_XOR_ASSIGN;- } else {- p += 2;- s->token.val = '^';- }- } else {- p++;- if (s->cur_func->js_mode & JS_MODE_MATH)- s->token.val = TOK_MATH_POW;- else- s->token.val = '^';- }- break;-#else- case '^':- if (p[1] == '=') {- p += 2;- s->token.val = TOK_XOR_ASSIGN;- } else {- goto def_token;- }- break;-#endif- case '|':- if (p[1] == '=') {- p += 2;- s->token.val = TOK_OR_ASSIGN;- } else if (p[1] == '|') {- if (p[2] == '=') {- p += 3;- s->token.val = TOK_LOR_ASSIGN;- } else {- p += 2;- s->token.val = TOK_LOR;- }- } else {- goto def_token;- }- break;- case '?':- if (p[1] == '?') {- if (p[2] == '=') {- p += 3;- s->token.val = TOK_DOUBLE_QUESTION_MARK_ASSIGN;- } else {- p += 2;- s->token.val = TOK_DOUBLE_QUESTION_MARK;- }- } else if (p[1] == '.' && !(p[2] >= '0' && p[2] <= '9')) {- p += 2;- s->token.val = TOK_QUESTION_MARK_DOT;- } else {- goto def_token;- }- break;- default:- if (c >= 128) {- /* unicode value */- c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);- switch(c) {- case CP_PS:- case CP_LS:- /* XXX: should avoid incrementing line_number, but- needed to handle HTML comments */- goto line_terminator; - default:- if (lre_is_space(c)) {- goto redo;- } else if (lre_js_is_ident_first(c)) {- ident_has_escape = FALSE;- goto has_ident;- } else {- js_parse_error(s, "unexpected character");- goto fail;- }- }- }- def_token:- s->token.val = c;- p++;- break;- }- s->buf_ptr = p;-- // dump_token(s, &s->token);- return 0;-- fail:- s->token.val = TOK_ERROR;- return -1;-}--/* 'c' is the first character. Return JS_ATOM_NULL in case of error */-static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c)-{- const uint8_t *p;- char ident_buf[128], *buf;- size_t ident_size, ident_pos;- JSAtom atom;- - p = *pp;- buf = ident_buf;- ident_size = sizeof(ident_buf);- ident_pos = 0;- for(;;) {- buf[ident_pos++] = c;- c = *p;- if (c >= 128 ||- !((lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1))- break;- p++;- if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) {- if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) {- atom = JS_ATOM_NULL;- goto done;- }- }- }- atom = JS_NewAtomLen(s->ctx, buf, ident_pos);- done:- if (unlikely(buf != ident_buf))- js_free(s->ctx, buf);- *pp = p;- return atom;-}--static __exception int json_next_token(JSParseState *s)-{- const uint8_t *p;- int c;- JSAtom atom;- - if (js_check_stack_overflow(s->ctx->rt, 0)) {- return js_parse_error(s, "stack overflow");- }- - free_token(s, &s->token);-- p = s->last_ptr = s->buf_ptr;- s->last_line_num = s->token.line_num;- redo:- s->token.line_num = s->line_num;- s->token.ptr = p;- c = *p;- switch(c) {- case 0:- if (p >= s->buf_end) {- s->token.val = TOK_EOF;- } else {- goto def_token;- }- break;- case '\'':- if (!s->ext_json) {- /* JSON does not accept single quoted strings */- goto def_token;- }- /* fall through */- case '\"':- if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p))- goto fail;- break;- case '\r': /* accept DOS and MAC newline sequences */- if (p[1] == '\n') {- p++;- }- /* fall thru */- case '\n':- p++;- s->line_num++;- goto redo;- case '\f':- case '\v':- if (!s->ext_json) {- /* JSONWhitespace does not match <VT>, nor <FF> */- goto def_token;- }- /* fall through */- case ' ':- case '\t':- p++;- goto redo;- case '/':- if (!s->ext_json) {- /* JSON does not accept comments */- goto def_token;- }- if (p[1] == '*') {- /* comment */- p += 2;- for(;;) {- if (*p == '\0' && p >= s->buf_end) {- js_parse_error(s, "unexpected end of comment");- goto fail;- }- if (p[0] == '*' && p[1] == '/') {- p += 2;- break;- }- if (*p == '\n') {- s->line_num++;- p++;- } else if (*p == '\r') {- p++;- } else if (*p >= 0x80) {- c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);- if (c == -1) {- p++; /* skip invalid UTF-8 */- }- } else {- p++;- }- }- goto redo;- } else if (p[1] == '/') {- /* line comment */- p += 2;- for(;;) {- if (*p == '\0' && p >= s->buf_end)- break;- if (*p == '\r' || *p == '\n')- break;- if (*p >= 0x80) {- c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);- /* LS or PS are considered as line terminator */- if (c == CP_LS || c == CP_PS) {- break;- } else if (c == -1) {- p++; /* skip invalid UTF-8 */- }- } else {- p++;- }- }- goto redo;- } else {- goto def_token;- }- break;- case 'a': case 'b': case 'c': case 'd':- case 'e': case 'f': case 'g': case 'h':- case 'i': case 'j': case 'k': case 'l':- case 'm': case 'n': case 'o': case 'p':- case 'q': case 'r': case 's': case 't':- case 'u': case 'v': case 'w': case 'x':- case 'y': case 'z': - case 'A': case 'B': case 'C': case 'D':- case 'E': case 'F': case 'G': case 'H':- case 'I': case 'J': case 'K': case 'L':- case 'M': case 'N': case 'O': case 'P':- case 'Q': case 'R': case 'S': case 'T':- case 'U': case 'V': case 'W': case 'X':- case 'Y': case 'Z': - case '_':- case '$':- /* identifier : only pure ascii characters are accepted */- p++;- atom = json_parse_ident(s, &p, c);- if (atom == JS_ATOM_NULL)- goto fail;- s->token.u.ident.atom = atom;- s->token.u.ident.has_escape = FALSE;- s->token.u.ident.is_reserved = FALSE;- s->token.val = TOK_IDENT;- break;- case '+':- if (!s->ext_json || !is_digit(p[1]))- goto def_token;- goto parse_number;- case '0':- if (is_digit(p[1]))- goto def_token;- goto parse_number;- case '-':- if (!is_digit(p[1]))- goto def_token;- goto parse_number;- case '1': case '2': case '3': case '4':- case '5': case '6': case '7': case '8':- case '9': - /* number */- parse_number:- {- JSValue ret;- int flags, radix;- if (!s->ext_json) {- flags = 0;- radix = 10;- } else {- flags = ATOD_ACCEPT_BIN_OCT;- radix = 0;- }- ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix,- flags);- if (JS_IsException(ret))- goto fail;- s->token.val = TOK_NUMBER;- s->token.u.num.val = ret;- }- break;- default:- if (c >= 128) {- js_parse_error(s, "unexpected character");- goto fail;- }- def_token:- s->token.val = c;- p++;- break;- }- s->buf_ptr = p;-- // dump_token(s, &s->token);- return 0;-- fail:- s->token.val = TOK_ERROR;- return -1;-}--/* only used for ':' and '=>', 'let' or 'function' look-ahead. *pp is- only set if TOK_IMPORT is returned */-/* XXX: handle all unicode cases */-static int simple_next_token(const uint8_t **pp, BOOL no_line_terminator)-{- const uint8_t *p;- uint32_t c;- - /* skip spaces and comments */- p = *pp;- for (;;) {- switch(c = *p++) {- case '\r':- case '\n':- if (no_line_terminator)- return '\n';- continue;- case ' ':- case '\t':- case '\v':- case '\f':- continue;- case '/':- if (*p == '/') {- if (no_line_terminator)- return '\n';- while (*p && *p != '\r' && *p != '\n')- p++;- continue;- }- if (*p == '*') {- while (*++p) {- if ((*p == '\r' || *p == '\n') && no_line_terminator)- return '\n';- if (*p == '*' && p[1] == '/') {- p += 2;- break;- }- }- continue;- }- break;- case '=':- if (*p == '>')- return TOK_ARROW;- break;- default:- if (lre_js_is_ident_first(c)) {- if (c == 'i') {- if (p[0] == 'n' && !lre_js_is_ident_next(p[1])) {- return TOK_IN;- }- if (p[0] == 'm' && p[1] == 'p' && p[2] == 'o' &&- p[3] == 'r' && p[4] == 't' &&- !lre_js_is_ident_next(p[5])) {- *pp = p + 5;- return TOK_IMPORT;- }- } else if (c == 'o' && *p == 'f' && !lre_js_is_ident_next(p[1])) {- return TOK_OF;- } else if (c == 'e' &&- p[0] == 'x' && p[1] == 'p' && p[2] == 'o' &&- p[3] == 'r' && p[4] == 't' &&- !lre_js_is_ident_next(p[5])) {- *pp = p + 5;- return TOK_EXPORT;- } else if (c == 'f' && p[0] == 'u' && p[1] == 'n' &&- p[2] == 'c' && p[3] == 't' && p[4] == 'i' &&- p[5] == 'o' && p[6] == 'n' && !lre_js_is_ident_next(p[7])) {- return TOK_FUNCTION;- }- return TOK_IDENT;- }- break;- }- return c;- }-}--static int peek_token(JSParseState *s, BOOL no_line_terminator)-{- const uint8_t *p = s->buf_ptr;- return simple_next_token(&p, no_line_terminator);-}--/* return true if 'input' contains the source of a module- (heuristic). 'input' must be a zero terminated.-- Heuristic: skip comments and expect 'import' keyword not followed- by '(' or '.' or export keyword.-*/-BOOL JS_DetectModule(const char *input, size_t input_len)-{- const uint8_t *p = (const uint8_t *)input;- int tok;- switch(simple_next_token(&p, FALSE)) {- case TOK_IMPORT:- tok = simple_next_token(&p, FALSE);- return (tok != '.' && tok != '(');- case TOK_EXPORT:- return TRUE;- default:- return FALSE;- }-}--static inline int get_prev_opcode(JSFunctionDef *fd) {- if (fd->last_opcode_pos < 0)- return OP_invalid;- else- return fd->byte_code.buf[fd->last_opcode_pos];-}--static BOOL js_is_live_code(JSParseState *s) {- switch (get_prev_opcode(s->cur_func)) {- case OP_tail_call:- case OP_tail_call_method:- case OP_return:- case OP_return_undef:- case OP_return_async:- case OP_throw:- case OP_throw_var:- case OP_goto:-#if SHORT_OPCODES- case OP_goto8:- case OP_goto16:-#endif- case OP_ret:- return FALSE;- default:- return TRUE;- }-}--static void emit_u8(JSParseState *s, uint8_t val)-{- dbuf_putc(&s->cur_func->byte_code, val);-}--static void emit_u16(JSParseState *s, uint16_t val)-{- dbuf_put_u16(&s->cur_func->byte_code, val);-}--static void emit_u32(JSParseState *s, uint32_t val)-{- dbuf_put_u32(&s->cur_func->byte_code, val);-}--static void emit_op(JSParseState *s, uint8_t val)-{- JSFunctionDef *fd = s->cur_func;- DynBuf *bc = &fd->byte_code;-- /* Use the line number of the last token used, not the next token,- nor the current offset in the source file.- */- if (unlikely(fd->last_opcode_line_num != s->last_line_num)) {- dbuf_putc(bc, OP_line_num);- dbuf_put_u32(bc, s->last_line_num);- fd->last_opcode_line_num = s->last_line_num;- }- fd->last_opcode_pos = bc->size;- dbuf_putc(bc, val);-}--static void emit_atom(JSParseState *s, JSAtom name)-{- emit_u32(s, JS_DupAtom(s->ctx, name));-}--static int update_label(JSFunctionDef *s, int label, int delta)-{- LabelSlot *ls;-- assert(label >= 0 && label < s->label_count);- ls = &s->label_slots[label];- ls->ref_count += delta;- assert(ls->ref_count >= 0);- return ls->ref_count;-}--static int new_label_fd(JSFunctionDef *fd, int label)-{- LabelSlot *ls;-- if (label < 0) {- if (js_resize_array(fd->ctx, (void *)&fd->label_slots,- sizeof(fd->label_slots[0]),- &fd->label_size, fd->label_count + 1))- return -1;- label = fd->label_count++;- ls = &fd->label_slots[label];- ls->ref_count = 0;- ls->pos = -1;- ls->pos2 = -1;- ls->addr = -1;- ls->first_reloc = NULL;- }- return label;-}--static int new_label(JSParseState *s)-{- return new_label_fd(s->cur_func, -1);-}--/* return the label ID offset */-static int emit_label(JSParseState *s, int label)-{- if (label >= 0) {- emit_op(s, OP_label);- emit_u32(s, label);- s->cur_func->label_slots[label].pos = s->cur_func->byte_code.size;- return s->cur_func->byte_code.size - 4;- } else {- return -1;- }-}--/* return label or -1 if dead code */-static int emit_goto(JSParseState *s, int opcode, int label)-{- if (js_is_live_code(s)) {- if (label < 0)- label = new_label(s);- emit_op(s, opcode);- emit_u32(s, label);- s->cur_func->label_slots[label].ref_count++;- return label;- }- return -1;-}--/* return the constant pool index. 'val' is not duplicated. */-static int cpool_add(JSParseState *s, JSValue val)-{- JSFunctionDef *fd = s->cur_func;- - if (js_resize_array(s->ctx, (void *)&fd->cpool, sizeof(fd->cpool[0]),- &fd->cpool_size, fd->cpool_count + 1))- return -1;- fd->cpool[fd->cpool_count++] = val;- return fd->cpool_count - 1;-}--static __exception int emit_push_const(JSParseState *s, JSValueConst val,- BOOL as_atom)-{- int idx;-- if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING && as_atom) {- JSAtom atom;- /* warning: JS_NewAtomStr frees the string value */- JS_DupValue(s->ctx, val);- atom = JS_NewAtomStr(s->ctx, JS_VALUE_GET_STRING(val));- if (atom != JS_ATOM_NULL && !__JS_AtomIsTaggedInt(atom)) {- emit_op(s, OP_push_atom_value);- emit_u32(s, atom);- return 0;- }- }-- idx = cpool_add(s, JS_DupValue(s->ctx, val));- if (idx < 0)- return -1;- emit_op(s, OP_push_const);- emit_u32(s, idx);- return 0;-}--/* return the variable index or -1 if not found,- add ARGUMENT_VAR_OFFSET for argument variables */-static int find_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name)-{- int i;- for(i = fd->arg_count; i-- > 0;) {- if (fd->args[i].var_name == name)- return i | ARGUMENT_VAR_OFFSET;- }- return -1;-}--static int find_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)-{- int i;- for(i = fd->var_count; i-- > 0;) {- if (fd->vars[i].var_name == name && fd->vars[i].scope_level == 0)- return i;- }- return find_arg(ctx, fd, name);-}--/* return true if scope == parent_scope or if scope is a child of- parent_scope */-static BOOL is_child_scope(JSContext *ctx, JSFunctionDef *fd,- int scope, int parent_scope)-{- while (scope >= 0) {- if (scope == parent_scope)- return TRUE;- scope = fd->scopes[scope].parent;- }- return FALSE;-}--/* find a 'var' declaration in the same scope or a child scope */-static int find_var_in_child_scope(JSContext *ctx, JSFunctionDef *fd,- JSAtom name, int scope_level)-{- int i;- for(i = 0; i < fd->var_count; i++) {- JSVarDef *vd = &fd->vars[i];- if (vd->var_name == name && vd->scope_level == 0) {- if (is_child_scope(ctx, fd, vd->func_pool_or_scope_idx,- scope_level))- return i;- }- }- return -1;-}---static JSHoistedDef *find_hoisted_def(JSFunctionDef *fd, JSAtom name)-{- int i;- for(i = 0; i < fd->hoisted_def_count; i++) {- JSHoistedDef *hf = &fd->hoisted_def[i];- if (hf->var_name == name)- return hf;- }- return NULL;--}--static JSHoistedDef *find_lexical_hoisted_def(JSFunctionDef *fd, JSAtom name)-{- JSHoistedDef *hf = find_hoisted_def(fd, name);- if (hf && hf->is_lexical)- return hf;- else- return NULL;-}--static int find_lexical_decl(JSContext *ctx, JSFunctionDef *fd, JSAtom name,- int scope_idx, BOOL check_catch_var)-{- while (scope_idx >= 0) {- JSVarDef *vd = &fd->vars[scope_idx];- if (vd->var_name == name &&- (vd->is_lexical || (vd->var_kind == JS_VAR_CATCH &&- check_catch_var)))- return scope_idx;- scope_idx = vd->scope_next;- }-- if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_GLOBAL) {- if (find_lexical_hoisted_def(fd, name))- return GLOBAL_VAR_OFFSET;- }- return -1;-}--static int push_scope(JSParseState *s) {- if (s->cur_func) {- JSFunctionDef *fd = s->cur_func;- int scope = fd->scope_count;- /* XXX: should check for scope overflow */- if ((fd->scope_count + 1) > fd->scope_size) {- int new_size;- size_t slack;- JSVarScope *new_buf;- /* XXX: potential arithmetic overflow */- new_size = max_int(fd->scope_count + 1, fd->scope_size * 3 / 2);- if (fd->scopes == fd->def_scope_array) {- new_buf = js_realloc2(s->ctx, NULL, new_size * sizeof(*fd->scopes), &slack);- if (!new_buf)- return -1;- memcpy(new_buf, fd->scopes, fd->scope_count * sizeof(*fd->scopes));- } else {- new_buf = js_realloc2(s->ctx, fd->scopes, new_size * sizeof(*fd->scopes), &slack);- if (!new_buf)- return -1;- }- new_size += slack / sizeof(*new_buf);- fd->scopes = new_buf;- fd->scope_size = new_size;- }- fd->scope_count++;- fd->scopes[scope].parent = fd->scope_level;- fd->scopes[scope].first = fd->scope_first;- emit_op(s, OP_enter_scope);- emit_u16(s, scope);- return fd->scope_level = scope;- }- return 0;-}--static int get_first_lexical_var(JSFunctionDef *fd, int scope)-{- while (scope >= 0) {- int scope_idx = fd->scopes[scope].first;- if (scope_idx >= 0)- return scope_idx;- scope = fd->scopes[scope].parent;- }- return -1;-}--static void pop_scope(JSParseState *s) {- if (s->cur_func) {- /* disable scoped variables */- JSFunctionDef *fd = s->cur_func;- int scope = fd->scope_level;- emit_op(s, OP_leave_scope);- emit_u16(s, scope);- fd->scope_level = fd->scopes[scope].parent;- fd->scope_first = get_first_lexical_var(fd, fd->scope_level);- }-}--static void close_scopes(JSParseState *s, int scope, int scope_stop)-{- while (scope > scope_stop) {- emit_op(s, OP_leave_scope);- emit_u16(s, scope);- scope = s->cur_func->scopes[scope].parent;- }-}--/* return the variable index or -1 if error */-static int add_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)-{- JSVarDef *vd;-- /* the local variable indexes are currently stored on 16 bits */- if (fd->var_count >= JS_MAX_LOCAL_VARS) {- JS_ThrowInternalError(ctx, "too many local variables");- return -1;- }- if (js_resize_array(ctx, (void **)&fd->vars, sizeof(fd->vars[0]),- &fd->var_size, fd->var_count + 1))- return -1;- vd = &fd->vars[fd->var_count++];- memset(vd, 0, sizeof(*vd));- vd->var_name = JS_DupAtom(ctx, name);- return fd->var_count - 1;-}--static int add_scope_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name,- JSVarKindEnum var_kind)-{- int idx = add_var(ctx, fd, name);- if (idx >= 0) {- JSVarDef *vd = &fd->vars[idx];- vd->var_kind = var_kind;- vd->scope_level = fd->scope_level;- vd->scope_next = fd->scope_first;- fd->scopes[fd->scope_level].first = idx;- fd->scope_first = idx;- }- return idx;-}--static int add_func_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)-{- int idx = fd->func_var_idx;- if (idx < 0 && (idx = add_var(ctx, fd, name)) >= 0) {- fd->func_var_idx = idx;- fd->vars[idx].is_func_var = TRUE;- if (fd->js_mode & JS_MODE_STRICT)- fd->vars[idx].is_const = TRUE;- }- return idx;-}--static int add_arguments_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)-{- int idx = fd->arguments_var_idx;- if (idx < 0 && (idx = add_var(ctx, fd, name)) >= 0) {- fd->arguments_var_idx = idx;- }- return idx;-}--static int add_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name)-{- JSVarDef *vd;-- /* the local variable indexes are currently stored on 16 bits */- if (fd->arg_count >= JS_MAX_LOCAL_VARS) {- JS_ThrowInternalError(ctx, "too many arguments");- return -1;- }- if (js_resize_array(ctx, (void **)&fd->args, sizeof(fd->args[0]),- &fd->arg_size, fd->arg_count + 1))- return -1;- vd = &fd->args[fd->arg_count++];- memset(vd, 0, sizeof(*vd));- vd->var_name = JS_DupAtom(ctx, name);- return fd->arg_count - 1;-}--/* add a Hoisted definition for a function (cpool_idx >= 0) or a- global variable (cpool_idx = -1) */-static JSHoistedDef *add_hoisted_def(JSContext *ctx,- JSFunctionDef *s, int cpool_idx,- JSAtom name, int var_idx, BOOL is_lexical)-{- JSHoistedDef *hf;-- if (js_resize_array(ctx, (void **)&s->hoisted_def,- sizeof(s->hoisted_def[0]),- &s->hoisted_def_size, s->hoisted_def_count + 1))- return NULL;- hf = &s->hoisted_def[s->hoisted_def_count++];- hf->cpool_idx = cpool_idx;- hf->force_init = 0;- hf->is_lexical = is_lexical;- hf->is_const = FALSE;- hf->var_idx = var_idx;- hf->scope_level = s->scope_level;- hf->var_name = JS_ATOM_NULL;- if (name != JS_ATOM_NULL) {- hf->var_name = JS_DupAtom(ctx, name);- }- return hf;-}--typedef enum {- JS_VAR_DEF_WITH,- JS_VAR_DEF_LET,- JS_VAR_DEF_CONST,- JS_VAR_DEF_FUNCTION_DECL, /* function declaration */- JS_VAR_DEF_NEW_FUNCTION_DECL, /* async/generator function declaration */- JS_VAR_DEF_CATCH,- JS_VAR_DEF_VAR,-} JSVarDefEnum;--static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name,- JSVarDefEnum var_def_type)-{- JSContext *ctx = s->ctx;- JSVarDef *vd;- int idx;-- switch (var_def_type) {- case JS_VAR_DEF_WITH:- idx = add_scope_var(ctx, fd, name, JS_VAR_NORMAL);- break;-- case JS_VAR_DEF_LET:- case JS_VAR_DEF_CONST:- case JS_VAR_DEF_FUNCTION_DECL:- case JS_VAR_DEF_NEW_FUNCTION_DECL:- idx = find_lexical_decl(ctx, fd, name, fd->scope_first, TRUE);- if (idx >= 0) {- if (idx < GLOBAL_VAR_OFFSET) {- if (fd->vars[idx].scope_level == fd->scope_level) {- /* same scope: in non strict mode, functions- can be redefined (annex B.3.3.4). */- if (!(!(fd->js_mode & JS_MODE_STRICT) &&- var_def_type == JS_VAR_DEF_FUNCTION_DECL &&- fd->vars[idx].var_kind == JS_VAR_FUNCTION_DECL)) {- goto redef_lex_error;- }- } else if (fd->vars[idx].var_kind == JS_VAR_CATCH && (fd->vars[idx].scope_level + 2) == fd->scope_level) {- goto redef_lex_error;- }- } else {- if (fd->scope_level == 1) {- redef_lex_error:- /* redefining a scoped var in the same scope: error */- return js_parse_error(s, "invalid redefinition of lexical identifier");- }- }- }- if (var_def_type != JS_VAR_DEF_FUNCTION_DECL &&- var_def_type != JS_VAR_DEF_NEW_FUNCTION_DECL &&- fd->scope_level == 1 &&- find_arg(ctx, fd, name) >= 0) {- /* lexical variable redefines a parameter name */- return js_parse_error(s, "invalid redefinition of parameter name");- }-- if (find_var_in_child_scope(ctx, fd, name, fd->scope_level) >= 0) {- return js_parse_error(s, "invalid redefinition of a variable");- }- - if (fd->is_global_var) {- JSHoistedDef *hf;- hf = find_hoisted_def(fd, name);- if (hf && is_child_scope(ctx, fd, hf->scope_level,- fd->scope_level)) {- return js_parse_error(s, "invalid redefinition of global identifier");- }- }- - if (fd->is_eval &&- (fd->eval_type == JS_EVAL_TYPE_GLOBAL ||- fd->eval_type == JS_EVAL_TYPE_MODULE) &&- fd->scope_level == 1) {- JSHoistedDef *hf;- hf = add_hoisted_def(s->ctx, fd, -1, name, -1, TRUE);- if (!hf)- return -1;- hf->is_const = (var_def_type == JS_VAR_DEF_CONST);- idx = GLOBAL_VAR_OFFSET;- } else {- JSVarKindEnum var_kind;- if (var_def_type == JS_VAR_DEF_FUNCTION_DECL)- var_kind = JS_VAR_FUNCTION_DECL;- else if (var_def_type == JS_VAR_DEF_NEW_FUNCTION_DECL)- var_kind = JS_VAR_NEW_FUNCTION_DECL;- else- var_kind = JS_VAR_NORMAL;- idx = add_scope_var(ctx, fd, name, var_kind);- if (idx >= 0) {- vd = &fd->vars[idx];- vd->is_lexical = 1;- vd->is_const = (var_def_type == JS_VAR_DEF_CONST);- }- }- break;-- case JS_VAR_DEF_CATCH:- idx = add_scope_var(ctx, fd, name, JS_VAR_CATCH);- break;-- case JS_VAR_DEF_VAR:- if (find_lexical_decl(ctx, fd, name, fd->scope_first,- FALSE) >= 0) {- invalid_lexical_redefinition:- /* error to redefine a var that inside a lexical scope */- return js_parse_error(s, "invalid redefinition of lexical identifier");- }- if (fd->is_global_var) {- JSHoistedDef *hf;- hf = find_hoisted_def(fd, name);- if (hf && hf->is_lexical && hf->scope_level == fd->scope_level &&- fd->eval_type == JS_EVAL_TYPE_MODULE) {- goto invalid_lexical_redefinition;- }- hf = add_hoisted_def(s->ctx, fd, -1, name, -1, FALSE);- if (!hf)- return -1;- idx = GLOBAL_VAR_OFFSET;- } else {- /* if the variable already exists, don't add it again */- idx = find_var(ctx, fd, name);- if (idx >= 0)- break;- idx = add_var(ctx, fd, name);- if (idx >= 0) {- if (name == JS_ATOM_arguments && fd->has_arguments_binding)- fd->arguments_var_idx = idx;- fd->vars[idx].func_pool_or_scope_idx = fd->scope_level;- }- }- break;- default:- abort();- }- return idx;-}--/* add a private field variable in the current scope */-static int add_private_class_field(JSParseState *s, JSFunctionDef *fd,- JSAtom name, JSVarKindEnum var_kind)-{- JSContext *ctx = s->ctx;- JSVarDef *vd;- int idx;-- idx = add_scope_var(ctx, fd, name, var_kind);- if (idx < 0)- return idx;- vd = &fd->vars[idx];- vd->is_lexical = 1;- vd->is_const = 1;- return idx;-}--static __exception int js_parse_expr(JSParseState *s);-static __exception int js_parse_function_decl(JSParseState *s,- JSParseFunctionEnum func_type,- JSFunctionKindEnum func_kind,- JSAtom func_name, const uint8_t *ptr,- int start_line);-static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s);-static __exception int js_parse_function_decl2(JSParseState *s,- JSParseFunctionEnum func_type,- JSFunctionKindEnum func_kind,- JSAtom func_name,- const uint8_t *ptr,- int function_line_num,- JSParseExportEnum export_flag,- JSFunctionDef **pfd);-static __exception int js_parse_assign_expr(JSParseState *s, int in_accepted);-static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag);-static void push_break_entry(JSFunctionDef *fd, BlockEnv *be,- JSAtom label_name,- int label_break, int label_cont,- int drop_count);-static void pop_break_entry(JSFunctionDef *fd);-static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m,- JSAtom local_name, JSAtom export_name,- JSExportTypeEnum export_type);--/* Note: all the fields are already sealed except length */-static int seal_template_obj(JSContext *ctx, JSValueConst obj)-{- JSObject *p;- JSShapeProperty *prs;-- p = JS_VALUE_GET_OBJ(obj);- prs = find_own_property1(p, JS_ATOM_length);- if (prs) {- if (js_update_property_flags(ctx, p, &prs,- prs->flags & ~(JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)))- return -1;- }- p->extensible = FALSE;- return 0;-}--static __exception int js_parse_template(JSParseState *s, int call, int *argc)-{- JSContext *ctx = s->ctx;- JSValue raw_array, template_object;- JSToken cooked;- int depth, ret;-- raw_array = JS_UNDEFINED; /* avoid warning */- template_object = JS_UNDEFINED; /* avoid warning */- if (call) {- /* Create a template object: an array of cooked strings */- /* Create an array of raw strings and store it to the raw property */- template_object = JS_NewArray(ctx);- if (JS_IsException(template_object))- return -1;- // pool_idx = s->cur_func->cpool_count;- ret = emit_push_const(s, template_object, 0);- JS_FreeValue(ctx, template_object);- if (ret)- return -1;- raw_array = JS_NewArray(ctx);- if (JS_IsException(raw_array))- return -1;- if (JS_DefinePropertyValue(ctx, template_object, JS_ATOM_raw,- raw_array, JS_PROP_THROW) < 0) {- return -1;- }- }-- depth = 0;- while (s->token.val == TOK_TEMPLATE) {- const uint8_t *p = s->token.ptr + 1;- cooked = s->token;- if (call) {- if (JS_DefinePropertyValueUint32(ctx, raw_array, depth,- JS_DupValue(ctx, s->token.u.str.str),- JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) {- return -1;- }- /* re-parse the string with escape sequences but do not throw a- syntax error if it contains invalid sequences- */- if (js_parse_string(s, '`', FALSE, p, &cooked, &p)) {- cooked.u.str.str = JS_UNDEFINED;- }- if (JS_DefinePropertyValueUint32(ctx, template_object, depth,- cooked.u.str.str,- JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) {- return -1;- }- } else {- JSString *str;- /* re-parse the string with escape sequences and throw a- syntax error if it contains invalid sequences- */- JS_FreeValue(ctx, s->token.u.str.str);- s->token.u.str.str = JS_UNDEFINED;- if (js_parse_string(s, '`', TRUE, p, &cooked, &p))- return -1;- str = JS_VALUE_GET_STRING(cooked.u.str.str);- if (str->len != 0 || depth == 0) {- ret = emit_push_const(s, cooked.u.str.str, 1);- JS_FreeValue(s->ctx, cooked.u.str.str);- if (ret)- return -1;- if (depth == 0) {- if (s->token.u.str.sep == '`')- goto done1;- emit_op(s, OP_get_field2);- emit_atom(s, JS_ATOM_concat);- }- depth++;- } else {- JS_FreeValue(s->ctx, cooked.u.str.str);- }- }- if (s->token.u.str.sep == '`')- goto done;- if (next_token(s))- return -1;- if (js_parse_expr(s))- return -1;- depth++;- if (s->token.val != '}') {- return js_parse_error(s, "expected '}' after template expression");- }- /* XXX: should convert to string at this stage? */- free_token(s, &s->token);- /* Resume TOK_TEMPLATE parsing (s->token.line_num and- * s->token.ptr are OK) */- s->got_lf = FALSE;- s->last_line_num = s->token.line_num;- if (js_parse_template_part(s, s->buf_ptr))- return -1;- }- return js_parse_expect(s, TOK_TEMPLATE);-- done:- if (call) {- /* Seal the objects */- seal_template_obj(ctx, raw_array);- seal_template_obj(ctx, template_object);- *argc = depth + 1;- } else {- emit_op(s, OP_call_method);- emit_u16(s, depth - 1);- }- done1:- return next_token(s);-}---#define PROP_TYPE_IDENT 0-#define PROP_TYPE_VAR 1-#define PROP_TYPE_GET 2-#define PROP_TYPE_SET 3-#define PROP_TYPE_STAR 4-#define PROP_TYPE_ASYNC 5-#define PROP_TYPE_ASYNC_STAR 6--#define PROP_TYPE_PRIVATE (1 << 4)--static BOOL token_is_ident(int tok)-{- /* Accept keywords and reserved words as property names */- return (tok == TOK_IDENT ||- (tok >= TOK_FIRST_KEYWORD &&- tok <= TOK_LAST_KEYWORD));-}--/* if the property is an expression, name = JS_ATOM_NULL */-static int __exception js_parse_property_name(JSParseState *s,- JSAtom *pname,- BOOL allow_method, BOOL allow_var,- BOOL allow_private)-{- int is_private = 0;- BOOL is_non_reserved_ident;- JSAtom name;- int prop_type;- - prop_type = PROP_TYPE_IDENT;- if (allow_method) {- if (token_is_pseudo_keyword(s, JS_ATOM_get)- || token_is_pseudo_keyword(s, JS_ATOM_set)) {- /* get x(), set x() */- name = JS_DupAtom(s->ctx, s->token.u.ident.atom);- if (next_token(s))- goto fail1;- if (s->token.val == ':' || s->token.val == ',' ||- s->token.val == '}' || s->token.val == '(') {- is_non_reserved_ident = TRUE;- goto ident_found;- }- prop_type = PROP_TYPE_GET + (name == JS_ATOM_set);- JS_FreeAtom(s->ctx, name);- } else if (s->token.val == '*') {- if (next_token(s))- goto fail;- prop_type = PROP_TYPE_STAR;- } else if (token_is_pseudo_keyword(s, JS_ATOM_async) &&- peek_token(s, TRUE) != '\n') {- name = JS_DupAtom(s->ctx, s->token.u.ident.atom);- if (next_token(s))- goto fail1;- if (s->token.val == ':' || s->token.val == ',' ||- s->token.val == '}' || s->token.val == '(') {- is_non_reserved_ident = TRUE;- goto ident_found;- }- JS_FreeAtom(s->ctx, name);- if (s->token.val == '*') {- if (next_token(s))- goto fail;- prop_type = PROP_TYPE_ASYNC_STAR;- } else {- prop_type = PROP_TYPE_ASYNC;- }- }- }-- if (token_is_ident(s->token.val)) {- /* variable can only be a non-reserved identifier */- is_non_reserved_ident =- (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved);- /* keywords and reserved words have a valid atom */- name = JS_DupAtom(s->ctx, s->token.u.ident.atom);- if (next_token(s))- goto fail1;- ident_found:- if (is_non_reserved_ident &&- prop_type == PROP_TYPE_IDENT && allow_var) {- if (!(s->token.val == ':' ||- (s->token.val == '(' && allow_method))) {- prop_type = PROP_TYPE_VAR;- }- }- } else if (s->token.val == TOK_STRING) {- name = JS_ValueToAtom(s->ctx, s->token.u.str.str);- if (name == JS_ATOM_NULL)- goto fail;- if (next_token(s))- goto fail1;- } else if (s->token.val == TOK_NUMBER) {- JSValue val;- val = s->token.u.num.val;-#ifdef CONFIG_BIGNUM- if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {- JSBigFloat *p = JS_VALUE_GET_PTR(val);- val = s->ctx->rt->bigfloat_ops.- mul_pow10_to_float64(s->ctx, &p->num,- s->token.u.num.exponent);- if (JS_IsException(val))- goto fail;- name = JS_ValueToAtom(s->ctx, val);- JS_FreeValue(s->ctx, val);- } else-#endif- {- name = JS_ValueToAtom(s->ctx, val);- }- if (name == JS_ATOM_NULL)- goto fail;- if (next_token(s))- goto fail1;- } else if (s->token.val == '[') {- if (next_token(s))- goto fail;- if (js_parse_expr(s))- goto fail;- if (js_parse_expect(s, ']'))- goto fail;- name = JS_ATOM_NULL;- } else if (s->token.val == TOK_PRIVATE_NAME && allow_private) {- name = JS_DupAtom(s->ctx, s->token.u.ident.atom);- if (next_token(s))- goto fail1;- is_private = PROP_TYPE_PRIVATE;- } else {- goto invalid_prop;- }- if (prop_type != PROP_TYPE_IDENT && prop_type != PROP_TYPE_VAR &&- s->token.val != '(') {- JS_FreeAtom(s->ctx, name);- invalid_prop:- js_parse_error(s, "invalid property name");- goto fail;- }- *pname = name;- return prop_type | is_private;- fail1:- JS_FreeAtom(s->ctx, name);- fail:- *pname = JS_ATOM_NULL;- return -1;-}--typedef struct JSParsePos {- int last_line_num;- int line_num;- BOOL got_lf;- const uint8_t *ptr;-} JSParsePos;--static int js_parse_get_pos(JSParseState *s, JSParsePos *sp)-{- sp->last_line_num = s->last_line_num;- sp->line_num = s->token.line_num;- sp->ptr = s->token.ptr;- sp->got_lf = s->got_lf;- return 0;-}--static __exception int js_parse_seek_token(JSParseState *s, const JSParsePos *sp)-{- s->token.line_num = sp->last_line_num;- s->line_num = sp->line_num;- s->buf_ptr = sp->ptr;- s->got_lf = sp->got_lf;- return next_token(s);-}--/* return TRUE if a regexp literal is allowed after this token */-static BOOL is_regexp_allowed(int tok)-{- switch (tok) {- case TOK_NUMBER:- case TOK_STRING:- case TOK_REGEXP:- case TOK_DEC:- case TOK_INC:- case TOK_NULL:- case TOK_FALSE:- case TOK_TRUE:- case TOK_THIS:- case ')':- case ']':- case '}': /* XXX: regexp may occur after */- case TOK_IDENT:- return FALSE;- default:- return TRUE;- }-}--#define SKIP_HAS_SEMI (1 << 0)-#define SKIP_HAS_ELLIPSIS (1 << 1)--/* XXX: improve speed with early bailout */-/* XXX: no longer works if regexps are present. Could use previous- regexp parsing heuristics to handle most cases */-static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_terminator)-{- char state[256];- size_t level = 0;- JSParsePos pos;- int last_tok, tok = TOK_EOF;- int c, tok_len, bits = 0;-- /* protect from underflow */- state[level++] = 0;-- js_parse_get_pos(s, &pos);- last_tok = 0;- for (;;) {- switch(s->token.val) {- case '(':- case '[':- case '{':- if (level >= sizeof(state))- goto done;- state[level++] = s->token.val;- break;- case ')':- if (state[--level] != '(')- goto done;- break;- case ']':- if (state[--level] != '[')- goto done;- break;- case '}':- c = state[--level];- if (c == '`') {- /* continue the parsing of the template */- free_token(s, &s->token);- /* Resume TOK_TEMPLATE parsing (s->token.line_num and- * s->token.ptr are OK) */- s->got_lf = FALSE;- s->last_line_num = s->token.line_num;- if (js_parse_template_part(s, s->buf_ptr))- goto done;- goto handle_template;- } else if (c != '{') {- goto done;- }- break;- case TOK_TEMPLATE:- handle_template:- if (s->token.u.str.sep != '`') {- /* '${' inside the template : closing '}' and continue- parsing the template */- if (level >= sizeof(state))- goto done;- state[level++] = '`';- } - break;- case TOK_EOF:- goto done;- case ';':- if (level == 2) {- bits |= SKIP_HAS_SEMI;- }- break;- case TOK_ELLIPSIS:- if (level == 2) {- bits |= SKIP_HAS_ELLIPSIS;- }- break;-- case TOK_DIV_ASSIGN:- tok_len = 2;- goto parse_regexp;- case '/':- tok_len = 1;- parse_regexp:- if (is_regexp_allowed(last_tok)) {- s->buf_ptr -= tok_len;- if (js_parse_regexp(s)) {- /* XXX: should clear the exception */- goto done;- }- }- break;- }- /* last_tok is only used to recognize regexps */- if (s->token.val == TOK_IDENT &&- (token_is_pseudo_keyword(s, JS_ATOM_of) ||- token_is_pseudo_keyword(s, JS_ATOM_yield))) {- last_tok = TOK_OF;- } else {- last_tok = s->token.val;- }- if (next_token(s)) {- /* XXX: should clear the exception generated by next_token() */- break;- }- if (level <= 1) {- tok = s->token.val;- if (token_is_pseudo_keyword(s, JS_ATOM_of))- tok = TOK_OF;- if (no_line_terminator && s->last_line_num != s->token.line_num)- tok = '\n';- break;- }- }- done:- if (pbits) {- *pbits = bits;- }- if (js_parse_seek_token(s, &pos))- return -1;- return tok;-}--static void set_object_name(JSParseState *s, JSAtom name)-{- JSFunctionDef *fd = s->cur_func;- int opcode;-- opcode = get_prev_opcode(fd);- if (opcode == OP_set_name) {- /* XXX: should free atom after OP_set_name? */- fd->byte_code.size = fd->last_opcode_pos;- fd->last_opcode_pos = -1;- emit_op(s, OP_set_name);- emit_atom(s, name);- } else if (opcode == OP_set_class_name) {- int define_class_pos;- JSAtom atom;- define_class_pos = fd->last_opcode_pos + 1 -- get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);- assert(fd->byte_code.buf[define_class_pos] == OP_define_class);- /* for consistency we free the previous atom which is- JS_ATOM_empty_string */- atom = get_u32(fd->byte_code.buf + define_class_pos + 1);- JS_FreeAtom(s->ctx, atom);- put_u32(fd->byte_code.buf + define_class_pos + 1,- JS_DupAtom(s->ctx, name));- fd->last_opcode_pos = -1;- }-}--static void set_object_name_computed(JSParseState *s)-{- JSFunctionDef *fd = s->cur_func;- int opcode;-- opcode = get_prev_opcode(fd);- if (opcode == OP_set_name) {- /* XXX: should free atom after OP_set_name? */- fd->byte_code.size = fd->last_opcode_pos;- fd->last_opcode_pos = -1;- emit_op(s, OP_set_name_computed);- } else if (opcode == OP_set_class_name) {- int define_class_pos;- define_class_pos = fd->last_opcode_pos + 1 -- get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);- assert(fd->byte_code.buf[define_class_pos] == OP_define_class);- fd->byte_code.buf[define_class_pos] = OP_define_class_computed;- fd->last_opcode_pos = -1;- }-}--static __exception int js_parse_object_literal(JSParseState *s)-{- JSAtom name = JS_ATOM_NULL;- const uint8_t *start_ptr;- int start_line, prop_type;- BOOL has_proto;-- if (next_token(s))- goto fail;- /* XXX: add an initial length that will be patched back */- emit_op(s, OP_object);- has_proto = FALSE;- while (s->token.val != '}') {- /* specific case for getter/setter */- start_ptr = s->token.ptr;- start_line = s->token.line_num;-- if (s->token.val == TOK_ELLIPSIS) {- if (next_token(s))- return -1;- if (js_parse_assign_expr(s, TRUE))- return -1;- emit_op(s, OP_null); /* dummy excludeList */- emit_op(s, OP_copy_data_properties);- emit_u8(s, 2 | (1 << 2) | (0 << 5));- emit_op(s, OP_drop); /* pop excludeList */- emit_op(s, OP_drop); /* pop src object */- goto next;- }-- prop_type = js_parse_property_name(s, &name, TRUE, TRUE, FALSE);- if (prop_type < 0)- goto fail;-- if (prop_type == PROP_TYPE_VAR) {- /* shortcut for x: x */- emit_op(s, OP_scope_get_var);- emit_atom(s, name);- emit_u16(s, s->cur_func->scope_level);- emit_op(s, OP_define_field);- emit_atom(s, name);- } else if (s->token.val == '(') {- BOOL is_getset = (prop_type == PROP_TYPE_GET ||- prop_type == PROP_TYPE_SET);- JSParseFunctionEnum func_type;- JSFunctionKindEnum func_kind;- int op_flags;-- func_kind = JS_FUNC_NORMAL;- if (is_getset) {- func_type = JS_PARSE_FUNC_GETTER + prop_type - PROP_TYPE_GET;- } else {- func_type = JS_PARSE_FUNC_METHOD;- if (prop_type == PROP_TYPE_STAR)- func_kind = JS_FUNC_GENERATOR;- else if (prop_type == PROP_TYPE_ASYNC)- func_kind = JS_FUNC_ASYNC;- else if (prop_type == PROP_TYPE_ASYNC_STAR)- func_kind = JS_FUNC_ASYNC_GENERATOR;- }- if (js_parse_function_decl(s, func_type, func_kind, JS_ATOM_NULL,- start_ptr, start_line))- goto fail;- if (name == JS_ATOM_NULL) {- emit_op(s, OP_define_method_computed);- } else {- emit_op(s, OP_define_method);- emit_atom(s, name);- }- if (is_getset) {- op_flags = OP_DEFINE_METHOD_GETTER +- prop_type - PROP_TYPE_GET;- } else {- op_flags = OP_DEFINE_METHOD_METHOD;- }- emit_u8(s, op_flags | OP_DEFINE_METHOD_ENUMERABLE);- } else {- if (js_parse_expect(s, ':'))- goto fail;- if (js_parse_assign_expr(s, TRUE))- goto fail;- if (name == JS_ATOM_NULL) {- set_object_name_computed(s);- emit_op(s, OP_define_array_el);- emit_op(s, OP_drop);- } else if (name == JS_ATOM___proto__) {- if (has_proto) {- js_parse_error(s, "duplicate __proto__ property name");- goto fail;- }- emit_op(s, OP_set_proto);- has_proto = TRUE;- } else {- set_object_name(s, name);- emit_op(s, OP_define_field);- emit_atom(s, name);- }- }- JS_FreeAtom(s->ctx, name);- next:- name = JS_ATOM_NULL;- if (s->token.val != ',')- break;- if (next_token(s))- goto fail;- }- if (js_parse_expect(s, '}'))- goto fail;- return 0;- fail:- JS_FreeAtom(s->ctx, name);- return -1;-}--static __exception int js_parse_postfix_expr(JSParseState *s,- BOOL accept_lparen);--/* XXX: is there is nicer solution ? */-static __exception int js_parse_class_default_ctor(JSParseState *s,- BOOL has_super,- JSFunctionDef **pfd)-{- JSParsePos pos;- const char *str;- int ret, line_num;- JSParseFunctionEnum func_type;- const uint8_t *saved_buf_end;- - js_parse_get_pos(s, &pos);- if (has_super) {- str = "(...a){super(...a);}";- func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR;- } else {- str = "(){}";- func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR;- }- line_num = s->token.line_num;- saved_buf_end = s->buf_end;- s->buf_ptr = (uint8_t *)str;- s->buf_end = (uint8_t *)(str + strlen(str));- ret = next_token(s);- if (!ret) {- ret = js_parse_function_decl2(s, func_type, JS_FUNC_NORMAL,- JS_ATOM_NULL, (uint8_t *)str,- line_num, JS_PARSE_EXPORT_NONE, pfd);- }- s->buf_end = saved_buf_end;- ret |= js_parse_seek_token(s, &pos);- return ret;-}--/* find field in the current scope */-static int find_private_class_field(JSContext *ctx, JSFunctionDef *fd,- JSAtom name, int scope_level)-{- int idx;- idx = fd->scopes[scope_level].first;- while (idx != -1) {- if (fd->vars[idx].scope_level != scope_level)- break;- if (fd->vars[idx].var_name == name)- return idx;- idx = fd->vars[idx].scope_next;- }- return -1;-}--/* initialize the class fields, called by the constructor. Note:- super() can be called in an arrow function, so <this> and- <class_fields_init> can be variable references */-static void emit_class_field_init(JSParseState *s)-{- int label_next;- - emit_op(s, OP_scope_get_var);- emit_atom(s, JS_ATOM_class_fields_init);- emit_u16(s, s->cur_func->scope_level);-- /* no need to call the class field initializer if not defined */- emit_op(s, OP_dup);- label_next = emit_goto(s, OP_if_false, -1);- - emit_op(s, OP_scope_get_var);- emit_atom(s, JS_ATOM_this);- emit_u16(s, 0);- - emit_op(s, OP_swap);- - emit_op(s, OP_call_method);- emit_u16(s, 0);-- emit_label(s, label_next);- emit_op(s, OP_drop);-}--/* build a private setter function name from the private getter name */-static JSAtom get_private_setter_name(JSContext *ctx, JSAtom name)-{- return js_atom_concat_str(ctx, name, "<set>");-}--typedef struct {- JSFunctionDef *fields_init_fd;- int computed_fields_count;- BOOL has_brand;- int brand_push_pos;-} ClassFieldsDef;--static __exception int emit_class_init_start(JSParseState *s,- ClassFieldsDef *cf)-{- int label_add_brand;- - cf->fields_init_fd = js_parse_function_class_fields_init(s);- if (!cf->fields_init_fd)- return -1;-- s->cur_func = cf->fields_init_fd;- - /* XXX: would be better to add the code only if needed, maybe in a- later pass */- emit_op(s, OP_push_false); /* will be patched later */- cf->brand_push_pos = cf->fields_init_fd->last_opcode_pos;- label_add_brand = emit_goto(s, OP_if_false, -1);- - emit_op(s, OP_scope_get_var);- emit_atom(s, JS_ATOM_this);- emit_u16(s, 0);- - emit_op(s, OP_scope_get_var);- emit_atom(s, JS_ATOM_home_object);- emit_u16(s, 0);- - emit_op(s, OP_add_brand);- - emit_label(s, label_add_brand);-- s->cur_func = s->cur_func->parent;- return 0;-}--static __exception int add_brand(JSParseState *s, ClassFieldsDef *cf)-{- if (!cf->has_brand) {- /* define the brand field in 'this' of the initializer */- if (!cf->fields_init_fd) {- if (emit_class_init_start(s, cf))- return -1;- }- /* patch the start of the function to enable the OP_add_brand code */- cf->fields_init_fd->byte_code.buf[cf->brand_push_pos] = OP_push_true;- - cf->has_brand = TRUE;- }- return 0;-}--static void emit_class_init_end(JSParseState *s, ClassFieldsDef *cf)-{- int cpool_idx;- - s->cur_func = cf->fields_init_fd;- emit_op(s, OP_return_undef);- s->cur_func = s->cur_func->parent;- - cpool_idx = cpool_add(s, JS_NULL);- cf->fields_init_fd->parent_cpool_idx = cpool_idx;- emit_op(s, OP_fclosure);- emit_u32(s, cpool_idx);- emit_op(s, OP_set_home_object);-}---static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,- JSParseExportEnum export_flag)-{- JSContext *ctx = s->ctx;- JSFunctionDef *fd = s->cur_func;- JSAtom name = JS_ATOM_NULL, class_name = JS_ATOM_NULL, class_name1;- JSAtom class_var_name = JS_ATOM_NULL;- JSFunctionDef *method_fd, *ctor_fd;- int saved_js_mode, class_name_var_idx, prop_type, ctor_cpool_offset;- int class_flags = 0, i, define_class_offset;- BOOL is_static, is_private;- const uint8_t *class_start_ptr = s->token.ptr;- const uint8_t *start_ptr;- ClassFieldsDef class_fields[2];- - /* classes are parsed and executed in strict mode */- saved_js_mode = fd->js_mode;- fd->js_mode |= JS_MODE_STRICT;- if (next_token(s))- goto fail;- if (s->token.val == TOK_IDENT) {- if (s->token.u.ident.is_reserved) {- js_parse_error_reserved_identifier(s);- goto fail;- }- class_name = JS_DupAtom(ctx, s->token.u.ident.atom);- if (next_token(s))- goto fail;- } else if (!is_class_expr && export_flag != JS_PARSE_EXPORT_DEFAULT) {- js_parse_error(s, "class statement requires a name");- goto fail;- }- if (!is_class_expr) {- if (class_name == JS_ATOM_NULL)- class_var_name = JS_ATOM__default_; /* export default */- else- class_var_name = class_name;- class_var_name = JS_DupAtom(ctx, class_var_name);- }-- push_scope(s);-- if (s->token.val == TOK_EXTENDS) {- class_flags = JS_DEFINE_CLASS_HAS_HERITAGE;- if (next_token(s))- goto fail;- /* XXX: the grammar only allows LeftHandSideExpression */- if (js_parse_postfix_expr(s, TRUE))- goto fail;- } else {- emit_op(s, OP_undefined);- }-- /* add a 'const' definition for the class name */- if (class_name != JS_ATOM_NULL) {- class_name_var_idx = define_var(s, fd, class_name, JS_VAR_DEF_CONST);- if (class_name_var_idx < 0)- goto fail;- }-- if (js_parse_expect(s, '{'))- goto fail;-- /* this scope contains the private fields */- push_scope(s);-- emit_op(s, OP_push_const);- ctor_cpool_offset = fd->byte_code.size;- emit_u32(s, 0); /* will be patched at the end of the class parsing */-- if (class_name == JS_ATOM_NULL) {- if (class_var_name != JS_ATOM_NULL)- class_name1 = JS_ATOM_default;- else- class_name1 = JS_ATOM_empty_string;- } else {- class_name1 = class_name;- }- - emit_op(s, OP_define_class);- emit_atom(s, class_name1);- emit_u8(s, class_flags);- define_class_offset = fd->last_opcode_pos;-- for(i = 0; i < 2; i++) {- ClassFieldsDef *cf = &class_fields[i];- cf->fields_init_fd = NULL;- cf->computed_fields_count = 0;- cf->has_brand = FALSE;- }- - ctor_fd = NULL;- while (s->token.val != '}') {- if (s->token.val == ';') {- if (next_token(s))- goto fail;- continue;- }- is_static = (s->token.val == TOK_STATIC);- prop_type = -1;- if (is_static) {- if (next_token(s))- goto fail;- /* allow "static" field name */- if (s->token.val == ';' || s->token.val == '=') {- is_static = FALSE;- name = JS_DupAtom(ctx, JS_ATOM_static);- prop_type = PROP_TYPE_IDENT;- }- }- if (is_static)- emit_op(s, OP_swap);- start_ptr = s->token.ptr;- if (prop_type < 0) {- prop_type = js_parse_property_name(s, &name, TRUE, FALSE, TRUE);- if (prop_type < 0)- goto fail;- }- is_private = prop_type & PROP_TYPE_PRIVATE;- prop_type &= ~PROP_TYPE_PRIVATE;- - if ((name == JS_ATOM_constructor && !is_static &&- prop_type != PROP_TYPE_IDENT) ||- (name == JS_ATOM_prototype && is_static) ||- name == JS_ATOM_hash_constructor) {- js_parse_error(s, "invalid method name");- goto fail;- }- if (prop_type == PROP_TYPE_GET || prop_type == PROP_TYPE_SET) {- BOOL is_set = prop_type - PROP_TYPE_GET;- JSFunctionDef *method_fd;-- if (is_private) {- int idx, var_kind;- idx = find_private_class_field(ctx, fd, name, fd->scope_level);- if (idx >= 0) {- var_kind = fd->vars[idx].var_kind;- if (var_kind == JS_VAR_PRIVATE_FIELD ||- var_kind == JS_VAR_PRIVATE_METHOD ||- var_kind == JS_VAR_PRIVATE_GETTER_SETTER ||- var_kind == (JS_VAR_PRIVATE_GETTER + is_set)) {- goto private_field_already_defined;- }- fd->vars[idx].var_kind = JS_VAR_PRIVATE_GETTER_SETTER;- } else {- if (add_private_class_field(s, fd, name,- JS_VAR_PRIVATE_GETTER + is_set) < 0)- goto fail;- }- if (add_brand(s, &class_fields[is_static]) < 0)- goto fail;- }-- if (js_parse_function_decl2(s, JS_PARSE_FUNC_GETTER + is_set,- JS_FUNC_NORMAL, JS_ATOM_NULL,- start_ptr, s->token.line_num,- JS_PARSE_EXPORT_NONE, &method_fd))- goto fail;- if (is_private) {- method_fd->need_home_object = TRUE; /* needed for brand check */- emit_op(s, OP_set_home_object);- /* XXX: missing function name */- emit_op(s, OP_scope_put_var_init);- if (is_set) {- JSAtom setter_name;- int ret;- - setter_name = get_private_setter_name(ctx, name);- if (setter_name == JS_ATOM_NULL)- goto fail;- emit_atom(s, setter_name);- ret = add_private_class_field(s, fd, setter_name,- JS_VAR_PRIVATE_SETTER);- JS_FreeAtom(ctx, setter_name);- if (ret < 0)- goto fail;- } else {- emit_atom(s, name);- }- emit_u16(s, s->cur_func->scope_level);- } else {- if (name == JS_ATOM_NULL) {- emit_op(s, OP_define_method_computed);- } else {- emit_op(s, OP_define_method);- emit_atom(s, name);- }- emit_u8(s, OP_DEFINE_METHOD_GETTER + is_set);- }- } else if (prop_type == PROP_TYPE_IDENT && s->token.val != '(') {- ClassFieldsDef *cf = &class_fields[is_static];- JSAtom field_var_name = JS_ATOM_NULL;- - /* class field */-- /* XXX: spec: not consistent with method name checks */- if (name == JS_ATOM_constructor || name == JS_ATOM_prototype) {- js_parse_error(s, "invalid field name");- goto fail;- }- - if (is_private) {- if (find_private_class_field(ctx, fd, name,- fd->scope_level) >= 0) {- goto private_field_already_defined;- }- if (add_private_class_field(s, fd, name,- JS_VAR_PRIVATE_FIELD) < 0)- goto fail;- emit_op(s, OP_private_symbol);- emit_atom(s, name);- emit_op(s, OP_scope_put_var_init);- emit_atom(s, name);- emit_u16(s, s->cur_func->scope_level);- }-- if (!cf->fields_init_fd) {- if (emit_class_init_start(s, cf))- goto fail;- }- if (name == JS_ATOM_NULL ) {- /* save the computed field name into a variable */- field_var_name = js_atom_concat_num(ctx, JS_ATOM_computed_field + is_static, cf->computed_fields_count);- if (field_var_name == JS_ATOM_NULL)- goto fail;- if (define_var(s, fd, field_var_name, JS_VAR_DEF_CONST) < 0) {- JS_FreeAtom(ctx, field_var_name);- goto fail;- }- emit_op(s, OP_to_propkey);- emit_op(s, OP_scope_put_var_init);- emit_atom(s, field_var_name);- emit_u16(s, s->cur_func->scope_level);- }- s->cur_func = cf->fields_init_fd;- emit_op(s, OP_scope_get_var);- emit_atom(s, JS_ATOM_this);- emit_u16(s, 0);-- if (name == JS_ATOM_NULL) {- emit_op(s, OP_scope_get_var);- emit_atom(s, field_var_name);- emit_u16(s, s->cur_func->scope_level);- cf->computed_fields_count++;- JS_FreeAtom(ctx, field_var_name);- } else if (is_private) {- emit_op(s, OP_scope_get_var);- emit_atom(s, name);- emit_u16(s, s->cur_func->scope_level);- }- - if (s->token.val == '=') {- if (next_token(s))- goto fail;- if (js_parse_assign_expr(s, TRUE))- goto fail;- } else {- emit_op(s, OP_undefined);- }- if (is_private) {- set_object_name_computed(s);- emit_op(s, OP_define_private_field);- } else if (name == JS_ATOM_NULL) {- set_object_name_computed(s);- emit_op(s, OP_define_array_el);- emit_op(s, OP_drop);- } else {- set_object_name(s, name);- emit_op(s, OP_define_field);- emit_atom(s, name);- }- s->cur_func = s->cur_func->parent;- if (js_parse_expect_semi(s))- goto fail;- } else {- JSParseFunctionEnum func_type;- JSFunctionKindEnum func_kind;- - func_type = JS_PARSE_FUNC_METHOD;- func_kind = JS_FUNC_NORMAL;- if (prop_type == PROP_TYPE_STAR) {- func_kind = JS_FUNC_GENERATOR;- } else if (prop_type == PROP_TYPE_ASYNC) {- func_kind = JS_FUNC_ASYNC;- } else if (prop_type == PROP_TYPE_ASYNC_STAR) {- func_kind = JS_FUNC_ASYNC_GENERATOR;- } else if (name == JS_ATOM_constructor && !is_static) {- if (ctor_fd) {- js_parse_error(s, "property constructor appears more than once");- goto fail;- }- if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE)- func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR;- else- func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR;- }- if (is_private) {- if (add_brand(s, &class_fields[is_static]) < 0)- goto fail;- }- 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))- goto fail;- if (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR ||- func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) {- ctor_fd = method_fd;- } else if (is_private) {- method_fd->need_home_object = TRUE; /* needed for brand check */- if (find_private_class_field(ctx, fd, name,- fd->scope_level) >= 0) {- private_field_already_defined:- js_parse_error(s, "private class field is already defined");- goto fail;- }- if (add_private_class_field(s, fd, name,- JS_VAR_PRIVATE_METHOD) < 0)- goto fail;- emit_op(s, OP_set_home_object);- emit_op(s, OP_set_name);- emit_atom(s, name);- emit_op(s, OP_scope_put_var_init);- emit_atom(s, name);- emit_u16(s, s->cur_func->scope_level);- } else {- if (name == JS_ATOM_NULL) {- emit_op(s, OP_define_method_computed);- } else {- emit_op(s, OP_define_method);- emit_atom(s, name);- }- emit_u8(s, OP_DEFINE_METHOD_METHOD);- }- }- if (is_static)- emit_op(s, OP_swap);- JS_FreeAtom(ctx, name);- name = JS_ATOM_NULL;- }-- if (s->token.val != '}') {- js_parse_error(s, "expecting '%c'", '}');- goto fail;- }-- if (!ctor_fd) {- if (js_parse_class_default_ctor(s, class_flags & JS_DEFINE_CLASS_HAS_HERITAGE, &ctor_fd))- goto fail;- }- /* patch the constant pool index for the constructor */- put_u32(fd->byte_code.buf + ctor_cpool_offset, ctor_fd->parent_cpool_idx);-- /* store the class source code in the constructor. */- if (!(fd->js_mode & JS_MODE_STRIP)) {- js_free(ctx, ctor_fd->source);- ctor_fd->source_len = s->buf_ptr - class_start_ptr;- ctor_fd->source = js_strndup(ctx, (const char *)class_start_ptr,- ctor_fd->source_len);- if (!ctor_fd->source)- goto fail;- }-- /* consume the '}' */- if (next_token(s))- goto fail;-- /* store the function to initialize the fields to that it can be- referenced by the constructor */- {- ClassFieldsDef *cf = &class_fields[0];- int var_idx;- - var_idx = define_var(s, fd, JS_ATOM_class_fields_init,- JS_VAR_DEF_CONST);- if (var_idx < 0)- goto fail;- if (cf->fields_init_fd) {- emit_class_init_end(s, cf);- } else {- emit_op(s, OP_undefined);- }- emit_op(s, OP_scope_put_var_init);- emit_atom(s, JS_ATOM_class_fields_init);- emit_u16(s, s->cur_func->scope_level);- }-- /* drop the prototype */- emit_op(s, OP_drop);-- /* initialize the static fields */- if (class_fields[1].fields_init_fd != NULL) {- ClassFieldsDef *cf = &class_fields[1];- emit_op(s, OP_dup);- emit_class_init_end(s, cf);- emit_op(s, OP_call_method);- emit_u16(s, 0);- emit_op(s, OP_drop);- }- - if (class_name != JS_ATOM_NULL) {- /* store the class name in the scoped class name variable (it- is independent from the class statement variable- definition) */- emit_op(s, OP_dup);- emit_op(s, OP_scope_put_var_init);- emit_atom(s, class_name);- emit_u16(s, fd->scope_level);- }- pop_scope(s);- pop_scope(s);-- /* the class statements have a block level scope */- if (class_var_name != JS_ATOM_NULL) {- if (define_var(s, fd, class_var_name, JS_VAR_DEF_LET) < 0)- goto fail;- emit_op(s, OP_scope_put_var_init);- emit_atom(s, class_var_name);- emit_u16(s, fd->scope_level);- } else {- if (class_name == JS_ATOM_NULL) {- /* cannot use OP_set_name because the name of the class- must be defined before the static initializers are- executed */- emit_op(s, OP_set_class_name);- emit_u32(s, fd->last_opcode_pos + 1 - define_class_offset);- }- }-- if (export_flag != JS_PARSE_EXPORT_NONE) {- if (!add_export_entry(s, fd->module,- class_var_name,- export_flag == JS_PARSE_EXPORT_NAMED ? class_var_name : JS_ATOM_default,- JS_EXPORT_TYPE_LOCAL))- goto fail;- }-- JS_FreeAtom(ctx, class_name);- JS_FreeAtom(ctx, class_var_name);- fd->js_mode = saved_js_mode;- return 0;- fail:- JS_FreeAtom(ctx, name);- JS_FreeAtom(ctx, class_name);- JS_FreeAtom(ctx, class_var_name);- fd->js_mode = saved_js_mode;- return -1;-}--static __exception int js_parse_array_literal(JSParseState *s)-{- uint32_t idx;- BOOL need_length;-- if (next_token(s))- return -1;- /* small regular arrays are created on the stack */- idx = 0;- while (s->token.val != ']' && idx < 32) {- if (s->token.val == ',' || s->token.val == TOK_ELLIPSIS)- break;- if (js_parse_assign_expr(s, TRUE))- return -1;- idx++;- /* accept trailing comma */- if (s->token.val == ',') {- if (next_token(s))- return -1;- } else- if (s->token.val != ']')- goto done;- }- emit_op(s, OP_array_from);- emit_u16(s, idx);-- /* larger arrays and holes are handled with explicit indices */- need_length = FALSE;- while (s->token.val != ']' && idx < 0x7fffffff) {- if (s->token.val == TOK_ELLIPSIS)- break;- need_length = TRUE;- if (s->token.val != ',') {- if (js_parse_assign_expr(s, TRUE))- return -1;- emit_op(s, OP_define_field);- emit_u32(s, __JS_AtomFromUInt32(idx));- need_length = FALSE;- }- idx++;- /* accept trailing comma */- if (s->token.val == ',') {- if (next_token(s))- return -1;- }- }- if (s->token.val == ']') {- if (need_length) {- /* Set the length: Cannot use OP_define_field because- length is not configurable */- emit_op(s, OP_dup);- emit_op(s, OP_push_i32);- emit_u32(s, idx);- emit_op(s, OP_put_field);- emit_atom(s, JS_ATOM_length);- }- goto done;- }-- /* huge arrays and spread elements require a dynamic index on the stack */- emit_op(s, OP_push_i32);- emit_u32(s, idx);-- /* stack has array, index */- while (s->token.val != ']') {- if (s->token.val == TOK_ELLIPSIS) {- if (next_token(s))- return -1;- if (js_parse_assign_expr(s, TRUE))- return -1;-#if 1- emit_op(s, OP_append);-#else- int label_next, label_done;- label_next = new_label(s);- label_done = new_label(s);- /* enumerate object */- emit_op(s, OP_for_of_start);- emit_op(s, OP_rot5l);- emit_op(s, OP_rot5l);- emit_label(s, label_next);- /* on stack: enum_rec array idx */- emit_op(s, OP_for_of_next);- emit_u8(s, 2);- emit_goto(s, OP_if_true, label_done);- /* append element */- /* enum_rec array idx val -> enum_rec array new_idx */- emit_op(s, OP_define_array_el);- emit_op(s, OP_inc);- emit_goto(s, OP_goto, label_next);- emit_label(s, label_done);- /* close enumeration */- emit_op(s, OP_drop); /* drop undef val */- emit_op(s, OP_nip1); /* drop enum_rec */- emit_op(s, OP_nip1);- emit_op(s, OP_nip1);-#endif- } else {- need_length = TRUE;- if (s->token.val != ',') {- if (js_parse_assign_expr(s, TRUE))- return -1;- /* a idx val */- emit_op(s, OP_define_array_el);- need_length = FALSE;- }- emit_op(s, OP_inc);- }- if (s->token.val != ',')- break;- if (next_token(s))- return -1;- }- if (need_length) {- /* Set the length: cannot use OP_define_field because- length is not configurable */- emit_op(s, OP_dup1); /* array length - array array length */- emit_op(s, OP_put_field);- emit_atom(s, JS_ATOM_length);- } else {- emit_op(s, OP_drop); /* array length - array */- }-done:- return js_parse_expect(s, ']');-}--/* XXX: remove */-static BOOL has_with_scope(JSFunctionDef *s, int scope_level)-{- /* check if scope chain contains a with statement */- while (s) {- int scope_idx = s->scopes[scope_level].first;- while (scope_idx >= 0) {- JSVarDef *vd = &s->vars[scope_idx];-- if (vd->var_name == JS_ATOM__with_)- return TRUE;- scope_idx = vd->scope_next;- }- /* check parent scopes */- scope_level = s->parent_scope_level;- s = s->parent;- }- return FALSE;-}--static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope,- JSAtom *pname, int *plabel, int *pdepth, BOOL keep,- int tok)-{- JSFunctionDef *fd;- int opcode, scope, label, depth;- JSAtom name;-- /* we check the last opcode to get the lvalue type */- fd = s->cur_func;- scope = 0;- name = JS_ATOM_NULL;- label = -1;- depth = 0;- switch(opcode = get_prev_opcode(fd)) {- case OP_scope_get_var:- name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);- scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);- if ((name == JS_ATOM_arguments || name == JS_ATOM_eval) &&- (fd->js_mode & JS_MODE_STRICT)) {- return js_parse_error(s, "invalid lvalue in strict mode");- }- if (name == JS_ATOM_this || name == JS_ATOM_new_target)- goto invalid_lvalue;- depth = 2; /* will generate OP_get_ref_value */- break;- case OP_get_field:- name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);- depth = 1;- break;- case OP_scope_get_private_field:- name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);- scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);- depth = 1;- break;- case OP_get_array_el:- depth = 2;- break;- case OP_get_super_value:- depth = 3;- break;- default:- invalid_lvalue:- if (tok == TOK_FOR) {- return js_parse_error(s, "invalid for in/of left hand-side");- } else if (tok == TOK_INC || tok == TOK_DEC) {- return js_parse_error(s, "invalid increment/decrement operand");- } else if (tok == '[' || tok == '{') {- return js_parse_error(s, "invalid destructuring target");- } else {- return js_parse_error(s, "invalid assignment left-hand side");- }- }- /* remove the last opcode */- fd->byte_code.size = fd->last_opcode_pos;- fd->last_opcode_pos = -1;-- if (keep) {- /* get the value but keep the object/fields on the stack */- switch(opcode) {- case OP_scope_get_var:- label = new_label(s);- emit_op(s, OP_scope_make_ref);- emit_atom(s, name);- emit_u32(s, label);- emit_u16(s, scope);- update_label(fd, label, 1);- emit_op(s, OP_get_ref_value);- opcode = OP_get_ref_value;- break;- case OP_get_field:- emit_op(s, OP_get_field2);- emit_atom(s, name);- break;- case OP_scope_get_private_field:- emit_op(s, OP_scope_get_private_field2);- emit_atom(s, name);- emit_u16(s, scope);- break;- case OP_get_array_el:- /* XXX: replace by a single opcode ? */- emit_op(s, OP_to_propkey2);- emit_op(s, OP_dup2);- emit_op(s, OP_get_array_el);- break;- case OP_get_super_value:- emit_op(s, OP_to_propkey);- emit_op(s, OP_dup3);- emit_op(s, OP_get_super_value);- break;- default:- abort();- }- } else {- switch(opcode) {- case OP_scope_get_var:- label = new_label(s);- emit_op(s, OP_scope_make_ref);- emit_atom(s, name);- emit_u32(s, label);- emit_u16(s, scope);- update_label(fd, label, 1);- opcode = OP_get_ref_value;- break;- case OP_get_array_el:- emit_op(s, OP_to_propkey2);- break;- case OP_get_super_value:- emit_op(s, OP_to_propkey);- break;- }- }-- *popcode = opcode;- *pscope = scope;- /* name has refcount for OP_get_field and OP_get_ref_value,- and JS_ATOM_NULL for other opcodes */- *pname = name;- *plabel = label;- if (pdepth)- *pdepth = depth;- return 0;-}--typedef enum {- PUT_LVALUE_NOKEEP, /* [depth] v -> */- PUT_LVALUE_NOKEEP_DEPTH, /* [depth] v -> , keep depth (currently- just disable optimizations) */- PUT_LVALUE_KEEP_TOP, /* [depth] v -> v */- PUT_LVALUE_KEEP_SECOND, /* [depth] v0 v -> v0 */- PUT_LVALUE_NOKEEP_BOTTOM, /* v [depth] -> */-} PutLValueEnum;--/* name has a live reference. 'is_let' is only used with opcode =- OP_scope_get_var which is never generated by get_lvalue(). */-static void put_lvalue(JSParseState *s, int opcode, int scope,- JSAtom name, int label, PutLValueEnum special,- BOOL is_let)-{- switch(opcode) {- case OP_get_field:- case OP_scope_get_private_field:- /* depth = 1 */- switch(special) {- case PUT_LVALUE_NOKEEP:- case PUT_LVALUE_NOKEEP_DEPTH:- break;- case PUT_LVALUE_KEEP_TOP:- emit_op(s, OP_insert2); /* obj v -> v obj v */- break;- case PUT_LVALUE_KEEP_SECOND:- emit_op(s, OP_perm3); /* obj v0 v -> v0 obj v */- break;- case PUT_LVALUE_NOKEEP_BOTTOM:- emit_op(s, OP_swap);- break;- default:- abort();- }- break;- case OP_get_array_el:- case OP_get_ref_value:- /* depth = 2 */- if (opcode == OP_get_ref_value) {- JS_FreeAtom(s->ctx, name);- emit_label(s, label);- }- switch(special) {- case PUT_LVALUE_NOKEEP:- emit_op(s, OP_nop); /* will trigger optimization */- break;- case PUT_LVALUE_NOKEEP_DEPTH:- break;- case PUT_LVALUE_KEEP_TOP:- emit_op(s, OP_insert3); /* obj prop v -> v obj prop v */- break;- case PUT_LVALUE_KEEP_SECOND:- emit_op(s, OP_perm4); /* obj prop v0 v -> v0 obj prop v */- break;- case PUT_LVALUE_NOKEEP_BOTTOM:- emit_op(s, OP_rot3l);- break;- default:- abort();- }- break;- case OP_get_super_value:- /* depth = 3 */- switch(special) {- case PUT_LVALUE_NOKEEP:- case PUT_LVALUE_NOKEEP_DEPTH:- break;- case PUT_LVALUE_KEEP_TOP:- emit_op(s, OP_insert4); /* this obj prop v -> v this obj prop v */- break;- case PUT_LVALUE_KEEP_SECOND:- emit_op(s, OP_perm5); /* this obj prop v0 v -> v0 this obj prop v */- break;- case PUT_LVALUE_NOKEEP_BOTTOM:- emit_op(s, OP_rot4l);- break;- default:- abort();- }- break;- default:- break;- }- - switch(opcode) {- case OP_scope_get_var: /* val -- */- assert(special == PUT_LVALUE_NOKEEP ||- special == PUT_LVALUE_NOKEEP_DEPTH);- emit_op(s, is_let ? OP_scope_put_var_init : OP_scope_put_var);- emit_u32(s, name); /* has refcount */- emit_u16(s, scope);- break;- case OP_get_field:- emit_op(s, OP_put_field);- emit_u32(s, name); /* name has refcount */- break;- case OP_scope_get_private_field:- emit_op(s, OP_scope_put_private_field);- emit_u32(s, name); /* name has refcount */- emit_u16(s, scope);- break;- case OP_get_array_el:- emit_op(s, OP_put_array_el);- break;- case OP_get_ref_value:- emit_op(s, OP_put_ref_value);- break;- case OP_get_super_value:- emit_op(s, OP_put_super_value);- break;- default:- abort();- }-}--static __exception int js_parse_expr_paren(JSParseState *s)-{- if (js_parse_expect(s, '('))- return -1;- if (js_parse_expr(s))- return -1;- if (js_parse_expect(s, ')'))- return -1;- return 0;-}--static int js_unsupported_keyword(JSParseState *s, JSAtom atom)-{- char buf[ATOM_GET_STR_BUF_SIZE];- return js_parse_error(s, "unsupported keyword: %s",- JS_AtomGetStr(s->ctx, buf, sizeof(buf), atom));-}--static __exception int js_define_var(JSParseState *s, JSAtom name, int tok)-{- JSFunctionDef *fd = s->cur_func;- JSVarDefEnum var_def_type;- - if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) {- return js_parse_error(s, "yield is a reserved identifier");- }- if ((name == JS_ATOM_arguments || name == JS_ATOM_eval)- && (fd->js_mode & JS_MODE_STRICT)) {- return js_parse_error(s, "invalid variable name in strict mode");- }- if ((name == JS_ATOM_let || name == JS_ATOM_undefined)- && (tok == TOK_LET || tok == TOK_CONST)) {- return js_parse_error(s, "invalid lexical variable name");- }- switch(tok) {- case TOK_LET:- var_def_type = JS_VAR_DEF_LET;- break;- case TOK_CONST:- var_def_type = JS_VAR_DEF_CONST;- break;- case TOK_VAR:- var_def_type = JS_VAR_DEF_VAR;- break;- case TOK_CATCH:- var_def_type = JS_VAR_DEF_CATCH;- break;- default:- abort();- }- if (define_var(s, fd, name, var_def_type) < 0)- return -1;- return 0;-}--static void js_emit_spread_code(JSParseState *s, int depth)-{- int label_rest_next, label_rest_done;-- /* XXX: could check if enum object is an actual array and optimize- slice extraction. enumeration record and target array are in a- different order from OP_append case. */- /* enum_rec xxx -- enum_rec xxx array 0 */- emit_op(s, OP_array_from);- emit_u16(s, 0);- emit_op(s, OP_push_i32);- emit_u32(s, 0);- emit_label(s, label_rest_next = new_label(s));- emit_op(s, OP_for_of_next);- emit_u8(s, 2 + depth);- label_rest_done = emit_goto(s, OP_if_true, -1);- /* array idx val -- array idx */- emit_op(s, OP_define_array_el);- emit_op(s, OP_inc);- emit_goto(s, OP_goto, label_rest_next);- emit_label(s, label_rest_done);- /* enum_rec xxx array idx undef -- enum_rec xxx array */- emit_op(s, OP_drop);- emit_op(s, OP_drop);-}--static int js_parse_check_duplicate_parameter(JSParseState *s, JSAtom name)-{- /* Check for duplicate parameter names */- JSFunctionDef *fd = s->cur_func;- int i;- for (i = 0; i < fd->arg_count; i++) {- if (fd->args[i].var_name == name)- goto duplicate;- }- for (i = 0; i < fd->var_count; i++) {- if (fd->vars[i].var_name == name)- goto duplicate;- }- return 0;--duplicate:- return js_parse_error(s, "duplicate parameter names not allowed in this context");-}--static JSAtom js_parse_destructuring_var(JSParseState *s, int tok, int is_arg)-{- JSAtom name;-- if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)- || ((s->cur_func->js_mode & JS_MODE_STRICT) &&- (s->token.u.ident.atom == JS_ATOM_eval || s->token.u.ident.atom == JS_ATOM_arguments))) {- js_parse_error(s, "invalid destructuring target");- return JS_ATOM_NULL;- }- name = JS_DupAtom(s->ctx, s->token.u.ident.atom);- if (is_arg && js_parse_check_duplicate_parameter(s, name))- goto fail;- if (next_token(s))- goto fail;-- return name;-fail:- JS_FreeAtom(s->ctx, name);- return JS_ATOM_NULL;-}--static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg,- int hasval, int has_ellipsis,- BOOL allow_initializer)-{- int label_parse, label_assign, label_done, label_lvalue, depth_lvalue;- int start_addr, assign_addr;- JSAtom prop_name, var_name;- int opcode, scope, tok1, skip_bits;-- if (has_ellipsis < 0) {- /* pre-parse destructuration target for spread detection */- js_parse_skip_parens_token(s, &skip_bits, FALSE);- has_ellipsis = skip_bits & SKIP_HAS_ELLIPSIS;- }-- label_parse = new_label(s);- label_assign = new_label(s);-- start_addr = s->cur_func->byte_code.size;- if (hasval) {- /* consume value from the stack */- emit_op(s, OP_dup);- emit_op(s, OP_undefined);- emit_op(s, OP_strict_eq);- emit_goto(s, OP_if_true, label_parse);- emit_label(s, label_assign);- } else {- emit_goto(s, OP_goto, label_parse);- emit_label(s, label_assign);- /* leave value on the stack */- emit_op(s, OP_dup);- }- assign_addr = s->cur_func->byte_code.size;- if (s->token.val == '{') {- if (next_token(s))- return -1;- /* throw an exception if the value cannot be converted to an object */- emit_op(s, OP_to_object);- if (has_ellipsis) {- /* add excludeList on stack just below src object */- emit_op(s, OP_object);- emit_op(s, OP_swap);- }- while (s->token.val != '}') {- int prop_type;- if (s->token.val == TOK_ELLIPSIS) {- if (!has_ellipsis) {- JS_ThrowInternalError(s->ctx, "unexpected ellipsis token");- return -1;- }- if (next_token(s))- return -1;- if (tok) {- var_name = js_parse_destructuring_var(s, tok, is_arg);- if (var_name == JS_ATOM_NULL)- return -1;- opcode = OP_scope_get_var;- scope = s->cur_func->scope_level;- label_lvalue = -1;- depth_lvalue = 0;- } else {- if (js_parse_postfix_expr(s, TRUE))- return -1;-- if (get_lvalue(s, &opcode, &scope, &var_name,- &label_lvalue, &depth_lvalue, FALSE, '{'))- return -1;- }- if (s->token.val != '}') {- js_parse_error(s, "assignment rest property must be last");- goto var_error;- }- emit_op(s, OP_object); /* target */- emit_op(s, OP_copy_data_properties);- emit_u8(s, 0 | ((depth_lvalue + 1) << 2) | ((depth_lvalue + 2) << 5));- goto set_val;- }- prop_type = js_parse_property_name(s, &prop_name, FALSE, TRUE, FALSE);- if (prop_type < 0)- return -1;- var_name = JS_ATOM_NULL;- opcode = OP_scope_get_var;- scope = s->cur_func->scope_level;- label_lvalue = -1;- depth_lvalue = 0;- if (prop_type == PROP_TYPE_IDENT) {- if (next_token(s))- goto prop_error;- if ((s->token.val == '[' || s->token.val == '{')- && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' ||- tok1 == '=' || tok1 == '}')) {- if (prop_name == JS_ATOM_NULL) {- /* computed property name on stack */- if (has_ellipsis) {- /* define the property in excludeList */- emit_op(s, OP_to_propkey); /* avoid calling ToString twice */- emit_op(s, OP_perm3); /* TOS: src excludeList prop */- emit_op(s, OP_null); /* TOS: src excludeList prop null */- emit_op(s, OP_define_array_el); /* TOS: src excludeList prop */- emit_op(s, OP_perm3); /* TOS: excludeList src prop */- }- /* get the computed property from the source object */- emit_op(s, OP_get_array_el2);- } else {- /* named property */- if (has_ellipsis) {- /* define the property in excludeList */- emit_op(s, OP_swap); /* TOS: src excludeList */- emit_op(s, OP_null); /* TOS: src excludeList null */- emit_op(s, OP_define_field); /* TOS: src excludeList */- emit_atom(s, prop_name);- emit_op(s, OP_swap); /* TOS: excludeList src */- }- /* get the named property from the source object */- emit_op(s, OP_get_field2);- emit_u32(s, prop_name);- }- if (js_parse_destructuring_element(s, tok, is_arg, TRUE, -1, TRUE))- return -1;- if (s->token.val == '}')- break;- /* accept a trailing comma before the '}' */- if (js_parse_expect(s, ','))- return -1;- continue;- }- if (prop_name == JS_ATOM_NULL) {- emit_op(s, OP_to_propkey2);- if (has_ellipsis) {- /* define the property in excludeList */- emit_op(s, OP_perm3);- emit_op(s, OP_null);- emit_op(s, OP_define_array_el);- emit_op(s, OP_perm3);- }- /* source prop -- source source prop */- emit_op(s, OP_dup1);- } else {- if (has_ellipsis) {- /* define the property in excludeList */- emit_op(s, OP_swap);- emit_op(s, OP_null);- emit_op(s, OP_define_field);- emit_atom(s, prop_name);- emit_op(s, OP_swap);- }- /* source -- source source */- emit_op(s, OP_dup);- }- if (tok) {- var_name = js_parse_destructuring_var(s, tok, is_arg);- if (var_name == JS_ATOM_NULL)- goto prop_error;- } else {- if (js_parse_postfix_expr(s, TRUE))- goto prop_error;- lvalue:- if (get_lvalue(s, &opcode, &scope, &var_name,- &label_lvalue, &depth_lvalue, FALSE, '{'))- goto prop_error;- /* swap ref and lvalue object if any */- if (prop_name == JS_ATOM_NULL) {- switch(depth_lvalue) {- case 1:- /* source prop x -> x source prop */- emit_op(s, OP_rot3r);- break;- case 2:- /* source prop x y -> x y source prop */- emit_op(s, OP_swap2); /* t p2 s p1 */- break;- case 3:- /* source prop x y z -> x y z source prop */- emit_op(s, OP_rot5l);- emit_op(s, OP_rot5l);- break;- }- } else {- switch(depth_lvalue) {- case 1:- /* source x -> x source */- emit_op(s, OP_swap);- break;- case 2:- /* source x y -> x y source */- emit_op(s, OP_rot3l);- break;- case 3:- /* source x y z -> x y z source */- emit_op(s, OP_rot4l);- break;- }- }- }- if (prop_name == JS_ATOM_NULL) {- /* computed property name on stack */- /* XXX: should have OP_get_array_el2x with depth */- /* source prop -- val */- emit_op(s, OP_get_array_el);- } else {- /* named property */- /* XXX: should have OP_get_field2x with depth */- /* source -- val */- emit_op(s, OP_get_field);- emit_u32(s, prop_name);- }- } else {- /* prop_type = PROP_TYPE_VAR, cannot be a computed property */- if (is_arg && js_parse_check_duplicate_parameter(s, prop_name))- goto prop_error;- if ((s->cur_func->js_mode & JS_MODE_STRICT) &&- (prop_name == JS_ATOM_eval || prop_name == JS_ATOM_arguments)) {- js_parse_error(s, "invalid destructuring target");- goto prop_error;- }- if (has_ellipsis) {- /* define the property in excludeList */- emit_op(s, OP_swap);- emit_op(s, OP_null);- emit_op(s, OP_define_field);- emit_atom(s, prop_name);- emit_op(s, OP_swap);- }- if (!tok || tok == TOK_VAR) {- /* generate reference */- /* source -- source source */- emit_op(s, OP_dup);- emit_op(s, OP_scope_get_var);- emit_atom(s, prop_name);- emit_u16(s, s->cur_func->scope_level);- goto lvalue;- }- var_name = JS_DupAtom(s->ctx, prop_name);- /* source -- source val */- emit_op(s, OP_get_field2);- emit_u32(s, prop_name);- }- set_val:- if (tok) {- if (js_define_var(s, var_name, tok))- goto var_error;- scope = s->cur_func->scope_level;- }- if (s->token.val == '=') { /* handle optional default value */- int label_hasval;- emit_op(s, OP_dup);- emit_op(s, OP_undefined);- emit_op(s, OP_strict_eq);- label_hasval = emit_goto(s, OP_if_false, -1);- if (next_token(s))- goto var_error;- emit_op(s, OP_drop);- if (js_parse_assign_expr(s, TRUE))- goto var_error;- if (opcode == OP_scope_get_var || opcode == OP_get_ref_value)- set_object_name(s, var_name);- emit_label(s, label_hasval);- }- /* store value into lvalue object */- put_lvalue(s, opcode, scope, var_name, label_lvalue,- PUT_LVALUE_NOKEEP_DEPTH,- (tok == TOK_CONST || tok == TOK_LET));- if (s->token.val == '}')- break;- /* accept a trailing comma before the '}' */- if (js_parse_expect(s, ','))- return -1;- }- /* drop the source object */- emit_op(s, OP_drop);- if (has_ellipsis) {- emit_op(s, OP_drop); /* pop excludeList */- }- if (next_token(s))- return -1;- } else if (s->token.val == '[') {- BOOL has_spread;- int enum_depth;- BlockEnv block_env;-- if (next_token(s))- return -1;- /* the block environment is only needed in generators in case- 'yield' triggers a 'return' */- push_break_entry(s->cur_func, &block_env,- JS_ATOM_NULL, -1, -1, 2);- block_env.has_iterator = TRUE;- emit_op(s, OP_for_of_start);- has_spread = FALSE;- while (s->token.val != ']') {- /* get the next value */- if (s->token.val == TOK_ELLIPSIS) {- if (next_token(s))- return -1;- if (s->token.val == ',' || s->token.val == ']')- return js_parse_error(s, "missing binding pattern...");- has_spread = TRUE;- }- if (s->token.val == ',') {- /* do nothing, skip the value, has_spread is false */- emit_op(s, OP_for_of_next);- emit_u8(s, 0);- emit_op(s, OP_drop);- emit_op(s, OP_drop);- } else if ((s->token.val == '[' || s->token.val == '{')- && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' ||- tok1 == '=' || tok1 == ']')) {- if (has_spread) {- if (tok1 == '=')- return js_parse_error(s, "rest element cannot have a default value");- js_emit_spread_code(s, 0);- } else {- emit_op(s, OP_for_of_next);- emit_u8(s, 0);- emit_op(s, OP_drop);- }- if (js_parse_destructuring_element(s, tok, is_arg, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE))- return -1;- } else {- var_name = JS_ATOM_NULL;- enum_depth = 0;- if (tok) {- var_name = js_parse_destructuring_var(s, tok, is_arg);- if (var_name == JS_ATOM_NULL)- goto var_error;- if (js_define_var(s, var_name, tok))- goto var_error;- opcode = OP_scope_get_var;- scope = s->cur_func->scope_level;- } else {- if (js_parse_postfix_expr(s, TRUE))- return -1;- if (get_lvalue(s, &opcode, &scope, &var_name,- &label_lvalue, &enum_depth, FALSE, '[')) {- return -1;- }- }- if (has_spread) {- js_emit_spread_code(s, enum_depth);- } else {- emit_op(s, OP_for_of_next);- emit_u8(s, enum_depth);- emit_op(s, OP_drop);- }- if (s->token.val == '=' && !has_spread) {- /* handle optional default value */- int label_hasval;- emit_op(s, OP_dup);- emit_op(s, OP_undefined);- emit_op(s, OP_strict_eq);- label_hasval = emit_goto(s, OP_if_false, -1);- if (next_token(s))- goto var_error;- emit_op(s, OP_drop);- if (js_parse_assign_expr(s, TRUE))- goto var_error;- if (opcode == OP_scope_get_var || opcode == OP_get_ref_value)- set_object_name(s, var_name);- emit_label(s, label_hasval);- }- /* store value into lvalue object */- put_lvalue(s, opcode, scope, var_name,- label_lvalue, PUT_LVALUE_NOKEEP_DEPTH,- (tok == TOK_CONST || tok == TOK_LET));- }- if (s->token.val == ']')- break;- if (has_spread)- return js_parse_error(s, "rest element must be the last one");- /* accept a trailing comma before the ']' */- if (js_parse_expect(s, ','))- return -1;- }- /* close iterator object:- if completed, enum_obj has been replaced by undefined */- emit_op(s, OP_iterator_close);- pop_break_entry(s->cur_func);- if (next_token(s))- return -1;- } else {- return js_parse_error(s, "invalid assignment syntax");- }- if (s->token.val == '=' && allow_initializer) {- label_done = emit_goto(s, OP_goto, -1);- if (next_token(s))- return -1;- emit_label(s, label_parse);- if (hasval)- emit_op(s, OP_drop);- if (js_parse_assign_expr(s, TRUE))- return -1;- emit_goto(s, OP_goto, label_assign);- emit_label(s, label_done);- } else {- /* normally hasval is true except if- js_parse_skip_parens_token() was wrong in the parsing */- // assert(hasval);- if (!hasval) {- js_parse_error(s, "too complicated destructuring expression");- return -1;- }- /* remove test and decrement label ref count */- memset(s->cur_func->byte_code.buf + start_addr, OP_nop,- assign_addr - start_addr);- s->cur_func->label_slots[label_parse].ref_count--;- }- return 0;-- prop_error:- JS_FreeAtom(s->ctx, prop_name);- var_error:- JS_FreeAtom(s->ctx, var_name);- return -1;-}--typedef enum FuncCallType {- FUNC_CALL_NORMAL,- FUNC_CALL_NEW,- FUNC_CALL_SUPER_CTOR,- FUNC_CALL_TEMPLATE,-} FuncCallType;--static void optional_chain_test(JSParseState *s, int *poptional_chaining_label,- int drop_count)-{- int label_next, i;- if (*poptional_chaining_label < 0)- *poptional_chaining_label = new_label(s);- /* XXX: could be more efficient with a specific opcode */- emit_op(s, OP_dup);- emit_op(s, OP_is_undefined_or_null);- label_next = emit_goto(s, OP_if_false, -1);- for(i = 0; i < drop_count; i++)- emit_op(s, OP_drop);- emit_op(s, OP_undefined);- emit_goto(s, OP_goto, *poptional_chaining_label);- emit_label(s, label_next);-}--static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen)-{- FuncCallType call_type;- int optional_chaining_label;- - call_type = FUNC_CALL_NORMAL;- switch(s->token.val) {- case TOK_NUMBER:- {- JSValue val;- val = s->token.u.num.val;-- if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) {- emit_op(s, OP_push_i32);- emit_u32(s, JS_VALUE_GET_INT(val));- } else-#ifdef CONFIG_BIGNUM- if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {- slimb_t e;- int ret;-- /* need a runtime conversion */- /* XXX: could add a cache and/or do it once at- the start of the function */- if (emit_push_const(s, val, 0) < 0)- return -1;- e = s->token.u.num.exponent;- if (e == (int32_t)e) {- emit_op(s, OP_push_i32);- emit_u32(s, e);- } else {- val = JS_NewBigInt64_1(s->ctx, e);- if (JS_IsException(val))- return -1;- ret = emit_push_const(s, val, 0);- JS_FreeValue(s->ctx, val);- if (ret < 0)- return -1;- }- emit_op(s, OP_mul_pow10);- } else-#endif- {- if (emit_push_const(s, val, 0) < 0)- return -1;- }- }- if (next_token(s))- return -1;- break;- case TOK_TEMPLATE:- if (js_parse_template(s, 0, NULL))- return -1;- break;- case TOK_STRING:- if (emit_push_const(s, s->token.u.str.str, 1))- return -1;- if (next_token(s))- return -1;- break;- - case TOK_DIV_ASSIGN:- s->buf_ptr -= 2;- goto parse_regexp;- case '/':- s->buf_ptr--;- parse_regexp:- {- JSValue str;- int ret, backtrace_flags;- if (!s->ctx->compile_regexp)- return js_parse_error(s, "RegExp are not supported");- /* the previous token is '/' or '/=', so no need to free */- if (js_parse_regexp(s))- return -1;- ret = emit_push_const(s, s->token.u.regexp.body, 0);- str = s->ctx->compile_regexp(s->ctx, s->token.u.regexp.body,- s->token.u.regexp.flags);- if (JS_IsException(str)) {- /* add the line number info */- backtrace_flags = 0;- if (s->cur_func && s->cur_func->backtrace_barrier)- backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL;- build_backtrace(s->ctx, s->ctx->rt->current_exception,- s->filename, s->token.line_num,- backtrace_flags);- return -1;- }- ret = emit_push_const(s, str, 0);- JS_FreeValue(s->ctx, str);- if (ret)- return -1;- /* we use a specific opcode to be sure the correct- function is called (otherwise the bytecode would have- to be verified by the RegExp constructor) */- emit_op(s, OP_regexp);- if (next_token(s))- return -1;- }- break;- case '(':- if (js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) {- if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,- JS_FUNC_NORMAL, JS_ATOM_NULL,- s->token.ptr, s->token.line_num))- return -1;- } else {- if (js_parse_expr_paren(s))- return -1;- }- break;- case TOK_FUNCTION:- if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR,- JS_FUNC_NORMAL, JS_ATOM_NULL,- s->token.ptr, s->token.line_num))- return -1;- break;- case TOK_CLASS:- if (js_parse_class(s, TRUE, JS_PARSE_EXPORT_NONE))- return -1;- break;- case TOK_NULL:- if (next_token(s))- return -1;- emit_op(s, OP_null);- break;- case TOK_THIS:- if (next_token(s))- return -1;- emit_op(s, OP_scope_get_var);- emit_atom(s, JS_ATOM_this);- emit_u16(s, 0);- break;- case TOK_FALSE:- if (next_token(s))- return -1;- emit_op(s, OP_push_false);- break;- case TOK_TRUE:- if (next_token(s))- return -1;- emit_op(s, OP_push_true);- break;- case TOK_IDENT:- {- JSAtom name;- if (s->token.u.ident.is_reserved) {- return js_parse_error_reserved_identifier(s);- }- if (peek_token(s, TRUE) == TOK_ARROW) {- if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,- JS_FUNC_NORMAL, JS_ATOM_NULL,- s->token.ptr, s->token.line_num))- return -1;- } else if (token_is_pseudo_keyword(s, JS_ATOM_async) &&- peek_token(s, TRUE) != '\n') {- const uint8_t *source_ptr;- int source_line_num;-- source_ptr = s->token.ptr;- source_line_num = s->token.line_num;- if (next_token(s))- return -1;- if (s->token.val == TOK_FUNCTION) {- if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR,- JS_FUNC_ASYNC, JS_ATOM_NULL,- source_ptr, source_line_num))- return -1;- } else if ((s->token.val == '(' &&- js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) ||- (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved &&- peek_token(s, TRUE) == TOK_ARROW)) {- if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,- JS_FUNC_ASYNC, JS_ATOM_NULL,- source_ptr, source_line_num))- return -1;- } else {- name = JS_DupAtom(s->ctx, JS_ATOM_async);- goto do_get_var;- }- } else {- if (s->token.u.ident.atom == JS_ATOM_arguments &&- !s->cur_func->arguments_allowed) {- js_parse_error(s, "'arguments' identifier is not allowed in class field initializer");- return -1;- }- name = JS_DupAtom(s->ctx, s->token.u.ident.atom);- if (next_token(s)) /* update line number before emitting code */- return -1;- do_get_var:- emit_op(s, OP_scope_get_var);- emit_u32(s, name);- emit_u16(s, s->cur_func->scope_level);- }- }- break;- case '{':- case '[':- {- int skip_bits;- if (js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') {- if (js_parse_destructuring_element(s, 0, 0, FALSE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE))- return -1;- } else {- if (s->token.val == '{') {- if (js_parse_object_literal(s))- return -1;- } else {- if (js_parse_array_literal(s))- return -1;- }- }- }- break;- case TOK_NEW:- if (next_token(s))- return -1;- if (s->token.val == '.') {- if (next_token(s))- return -1;- if (!token_is_pseudo_keyword(s, JS_ATOM_target))- return js_parse_error(s, "expecting target");- if (!s->cur_func->new_target_allowed)- return js_parse_error(s, "new.target only allowed within functions");- if (next_token(s))- return -1;- emit_op(s, OP_scope_get_var);- emit_atom(s, JS_ATOM_new_target);- emit_u16(s, 0);- } else {- if (js_parse_postfix_expr(s, FALSE))- return -1;- accept_lparen = TRUE;- if (s->token.val != '(') {- /* new operator on an object */- emit_op(s, OP_dup);- emit_op(s, OP_call_constructor);- emit_u16(s, 0);- } else {- call_type = FUNC_CALL_NEW;- }- }- break;- case TOK_SUPER:- if (next_token(s))- return -1;- if (s->token.val == '(') {- if (!s->cur_func->super_call_allowed)- return js_parse_error(s, "super() is only valid in a derived class constructor");- call_type = FUNC_CALL_SUPER_CTOR;- } else if (s->token.val == '.' || s->token.val == '[') {- if (!s->cur_func->super_allowed)- return js_parse_error(s, "'super' is only valid in a method");- emit_op(s, OP_scope_get_var);- emit_atom(s, JS_ATOM_this);- emit_u16(s, 0);- emit_op(s, OP_scope_get_var);- emit_atom(s, JS_ATOM_home_object);- emit_u16(s, 0);- emit_op(s, OP_get_super);- } else {- return js_parse_error(s, "invalid use of 'super'");- }- break;- case TOK_IMPORT:- if (next_token(s))- return -1;- if (s->token.val == '.') {- if (next_token(s))- return -1;- if (!token_is_pseudo_keyword(s, JS_ATOM_meta))- return js_parse_error(s, "meta expected");- if (!s->is_module)- return js_parse_error(s, "import.meta only valid in module code");- if (next_token(s))- return -1;- emit_op(s, OP_special_object);- emit_u8(s, OP_SPECIAL_OBJECT_IMPORT_META);- } else {- if (js_parse_expect(s, '('))- return -1;- if (!accept_lparen)- return js_parse_error(s, "invalid use of 'import()'");- if (js_parse_assign_expr(s, TRUE))- return -1;- if (js_parse_expect(s, ')'))- return -1;- emit_op(s, OP_import);- }- break;- default:- return js_parse_error(s, "unexpected token in expression: '%.*s'",- (int)(s->buf_ptr - s->token.ptr), s->token.ptr);- }-- optional_chaining_label = -1;- for(;;) {- JSFunctionDef *fd = s->cur_func;- BOOL has_optional_chain = FALSE;- - if (s->token.val == TOK_QUESTION_MARK_DOT) {- /* optional chaining */- if (next_token(s))- return -1;- has_optional_chain = TRUE;- if (s->token.val == '(' && accept_lparen) {- goto parse_func_call;- } else if (s->token.val == '[') {- goto parse_array_access;- } else {- goto parse_property;- }- } else if (s->token.val == TOK_TEMPLATE &&- call_type == FUNC_CALL_NORMAL) {- if (optional_chaining_label >= 0) {- return js_parse_error(s, "template literal cannot appear in an optional chain");- }- call_type = FUNC_CALL_TEMPLATE;- goto parse_func_call2;- } else if (s->token.val == '(' && accept_lparen) {- int opcode, arg_count, drop_count;-- /* function call */- parse_func_call:- if (next_token(s))- return -1;-- if (call_type == FUNC_CALL_NORMAL) {- parse_func_call2:- switch(opcode = get_prev_opcode(fd)) {- case OP_get_field:- /* keep the object on the stack */- fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2;- drop_count = 2;- break;- case OP_scope_get_private_field:- /* keep the object on the stack */- fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_private_field2;- drop_count = 2;- break;- case OP_get_array_el:- /* keep the object on the stack */- fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2;- drop_count = 2;- break;- case OP_scope_get_var:- {- JSAtom name;- int scope;- name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);- scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);- if (name == JS_ATOM_eval && call_type == FUNC_CALL_NORMAL && !has_optional_chain) {- /* direct 'eval' */- opcode = OP_eval;- } else {- /* verify if function name resolves to a simple- get_loc/get_arg: a function call inside a `with`- statement can resolve to a method call of the- `with` context object- */- /* XXX: always generate the OP_scope_get_ref- and remove it in variable resolution- pass ? */- if (has_with_scope(fd, scope)) {- opcode = OP_scope_get_ref;- fd->byte_code.buf[fd->last_opcode_pos] = opcode;- }- }- drop_count = 1;- }- break;- case OP_get_super_value:- fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el;- /* on stack: this func_obj */- opcode = OP_get_array_el;- drop_count = 2;- break;- default:- opcode = OP_invalid;- drop_count = 1;- break;- }- if (has_optional_chain) {- optional_chain_test(s, &optional_chaining_label,- drop_count);- }- } else {- opcode = OP_invalid;- }-- if (call_type == FUNC_CALL_TEMPLATE) {- if (js_parse_template(s, 1, &arg_count))- return -1;- goto emit_func_call;- } else if (call_type == FUNC_CALL_SUPER_CTOR) {- emit_op(s, OP_scope_get_var);- emit_atom(s, JS_ATOM_this_active_func);- emit_u16(s, 0);-- emit_op(s, OP_get_super);-- emit_op(s, OP_scope_get_var);- emit_atom(s, JS_ATOM_new_target);- emit_u16(s, 0);- } else if (call_type == FUNC_CALL_NEW) {- emit_op(s, OP_dup); /* new.target = function */- }-- /* parse arguments */- arg_count = 0;- while (s->token.val != ')') {- if (arg_count >= 65535) {- return js_parse_error(s, "Too many call arguments");- }- if (s->token.val == TOK_ELLIPSIS)- break;- if (js_parse_assign_expr(s, TRUE))- return -1;- arg_count++;- if (s->token.val == ')')- break;- /* accept a trailing comma before the ')' */- if (js_parse_expect(s, ','))- return -1;- }- if (s->token.val == TOK_ELLIPSIS) {- emit_op(s, OP_array_from);- emit_u16(s, arg_count);- emit_op(s, OP_push_i32);- emit_u32(s, arg_count);-- /* on stack: array idx */- while (s->token.val != ')') {- if (s->token.val == TOK_ELLIPSIS) {- if (next_token(s))- return -1;- if (js_parse_assign_expr(s, TRUE))- return -1;-#if 1- /* XXX: could pass is_last indicator? */- emit_op(s, OP_append);-#else- int label_next, label_done;- label_next = new_label(s);- label_done = new_label(s);- /* push enumerate object below array/idx pair */- emit_op(s, OP_for_of_start);- emit_op(s, OP_rot5l);- emit_op(s, OP_rot5l);- emit_label(s, label_next);- /* on stack: enum_rec array idx */- emit_op(s, OP_for_of_next);- emit_u8(s, 2);- emit_goto(s, OP_if_true, label_done);- /* append element */- /* enum_rec array idx val -> enum_rec array new_idx */- emit_op(s, OP_define_array_el);- emit_op(s, OP_inc);- emit_goto(s, OP_goto, label_next);- emit_label(s, label_done);- /* close enumeration, drop enum_rec and idx */- emit_op(s, OP_drop); /* drop undef */- emit_op(s, OP_nip1); /* drop enum_rec */- emit_op(s, OP_nip1);- emit_op(s, OP_nip1);-#endif- } else {- if (js_parse_assign_expr(s, TRUE))- return -1;- /* array idx val */- emit_op(s, OP_define_array_el);- emit_op(s, OP_inc);- }- if (s->token.val == ')')- break;- /* accept a trailing comma before the ')' */- if (js_parse_expect(s, ','))- return -1;- }- if (next_token(s))- return -1;- /* drop the index */- emit_op(s, OP_drop);-- /* apply function call */- switch(opcode) {- case OP_get_field:- case OP_scope_get_private_field:- case OP_get_array_el:- case OP_scope_get_ref:- /* obj func array -> func obj array */- emit_op(s, OP_perm3);- emit_op(s, OP_apply);- emit_u16(s, call_type == FUNC_CALL_NEW);- break;- case OP_eval:- emit_op(s, OP_apply_eval);- emit_u16(s, fd->scope_level);- fd->has_eval_call = TRUE;- break;- default:- if (call_type == FUNC_CALL_SUPER_CTOR) {- emit_op(s, OP_apply);- emit_u16(s, 1);- /* set the 'this' value */- emit_op(s, OP_dup);- emit_op(s, OP_scope_put_var_init);- emit_atom(s, JS_ATOM_this);- emit_u16(s, 0);-- emit_class_field_init(s);- } else if (call_type == FUNC_CALL_NEW) {- /* obj func array -> func obj array */- emit_op(s, OP_perm3);- emit_op(s, OP_apply);- emit_u16(s, 1);- } else {- /* func array -> func undef array */- emit_op(s, OP_undefined);- emit_op(s, OP_swap);- emit_op(s, OP_apply);- emit_u16(s, 0);- }- break;- }- } else {- if (next_token(s))- return -1;- emit_func_call:- switch(opcode) {- case OP_get_field:- case OP_scope_get_private_field:- case OP_get_array_el:- case OP_scope_get_ref:- emit_op(s, OP_call_method);- emit_u16(s, arg_count);- break;- case OP_eval:- emit_op(s, OP_eval);- emit_u16(s, arg_count);- emit_u16(s, fd->scope_level);- fd->has_eval_call = TRUE;- break;- default:- if (call_type == FUNC_CALL_SUPER_CTOR) {- emit_op(s, OP_call_constructor);- emit_u16(s, arg_count);-- /* set the 'this' value */- emit_op(s, OP_dup);- emit_op(s, OP_scope_put_var_init);- emit_atom(s, JS_ATOM_this);- emit_u16(s, 0);-- emit_class_field_init(s);- } else if (call_type == FUNC_CALL_NEW) {- emit_op(s, OP_call_constructor);- emit_u16(s, arg_count);- } else {- emit_op(s, OP_call);- emit_u16(s, arg_count);- }- break;- }- }- call_type = FUNC_CALL_NORMAL;- } else if (s->token.val == '.') {- if (next_token(s))- return -1;- if (s->token.val == TOK_PRIVATE_NAME) {- /* private class field */- if (get_prev_opcode(fd) == OP_get_super) {- return js_parse_error(s, "private class field forbidden after super");- }- emit_op(s, OP_scope_get_private_field);- emit_atom(s, s->token.u.ident.atom);- emit_u16(s, s->cur_func->scope_level);- } else {- parse_property:- if (!token_is_ident(s->token.val)) {- return js_parse_error(s, "expecting field name");- }- if (get_prev_opcode(fd) == OP_get_super) {- JSValue val;- int ret;- val = JS_AtomToValue(s->ctx, s->token.u.ident.atom);- ret = emit_push_const(s, val, 1);- JS_FreeValue(s->ctx, val);- if (ret)- return -1;- emit_op(s, OP_get_super_value);- } else {- if (has_optional_chain) {- optional_chain_test(s, &optional_chaining_label, 1);- }- emit_op(s, OP_get_field);- emit_atom(s, s->token.u.ident.atom);- }- }- if (next_token(s))- return -1;- } else if (s->token.val == '[') {- int prev_op;-- parse_array_access:- prev_op = get_prev_opcode(fd);- if (has_optional_chain) {- optional_chain_test(s, &optional_chaining_label, 1);- }- if (next_token(s))- return -1;- if (js_parse_expr(s))- return -1;- if (js_parse_expect(s, ']'))- return -1;- if (prev_op == OP_get_super) {- emit_op(s, OP_get_super_value);- } else {- emit_op(s, OP_get_array_el);- }- } else {- break;- }- }- if (optional_chaining_label >= 0)- emit_label(s, optional_chaining_label);- return 0;-}--static __exception int js_parse_delete(JSParseState *s)-{- JSFunctionDef *fd = s->cur_func;- JSAtom name;- int opcode;-- if (next_token(s))- return -1;- if (js_parse_unary(s, -1))- return -1;- switch(opcode = get_prev_opcode(fd)) {- case OP_get_field:- {- JSValue val;- int ret;-- name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);- fd->byte_code.size = fd->last_opcode_pos;- fd->last_opcode_pos = -1;- val = JS_AtomToValue(s->ctx, name);- ret = emit_push_const(s, val, 1);- JS_FreeValue(s->ctx, val);- JS_FreeAtom(s->ctx, name);- if (ret)- return ret;- }- goto do_delete;- case OP_get_array_el:- fd->byte_code.size = fd->last_opcode_pos;- fd->last_opcode_pos = -1;- do_delete:- emit_op(s, OP_delete);- break;- case OP_scope_get_var:- /* 'delete this': this is not a reference */- name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);- if (name == JS_ATOM_this || name == JS_ATOM_new_target)- goto ret_true;- if (fd->js_mode & JS_MODE_STRICT) {- return js_parse_error(s, "cannot delete a direct reference in strict mode");- } else {- fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_delete_var;- }- break;- case OP_scope_get_private_field:- return js_parse_error(s, "cannot delete a private class field");- case OP_get_super_value:- emit_op(s, OP_throw_var);- emit_atom(s, JS_ATOM_NULL);- emit_u8(s, JS_THROW_VAR_DELETE_SUPER);- break;- default:- ret_true:- emit_op(s, OP_drop);- emit_op(s, OP_push_true);- break;- }- return 0;-}--static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag)-{- int op;-- switch(s->token.val) {- case '+':- case '-':- case '!':- case '~':- case TOK_VOID:- op = s->token.val;- if (next_token(s))- return -1;- if (js_parse_unary(s, -1))- return -1;- switch(op) {- case '-':- emit_op(s, OP_neg);- break;- case '+':- emit_op(s, OP_plus);- break;- case '!':- emit_op(s, OP_lnot);- break;- case '~':- emit_op(s, OP_not);- break;- case TOK_VOID:- emit_op(s, OP_drop);- emit_op(s, OP_undefined);- break;- default:- abort();- }- exponentiation_flag = 0;- break;- case TOK_DEC:- case TOK_INC:- {- int opcode, op, scope, label;- JSAtom name;- op = s->token.val;- if (next_token(s))- return -1;- /* XXX: should parse LeftHandSideExpression */- if (js_parse_unary(s, 0))- return -1;- if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op))- return -1;- emit_op(s, OP_dec + op - TOK_DEC);- put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP,- FALSE);- }- break;- case TOK_TYPEOF:- {- JSFunctionDef *fd;- if (next_token(s))- return -1;- if (js_parse_unary(s, -1))- return -1;- /* reference access should not return an exception, so we- patch the get_var */- fd = s->cur_func;- if (get_prev_opcode(fd) == OP_scope_get_var) {- fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_var_undef;- }- emit_op(s, OP_typeof);- exponentiation_flag = 0;- }- break;- case TOK_DELETE:- if (js_parse_delete(s))- return -1;- exponentiation_flag = 0;- break;- case TOK_AWAIT:- if (!(s->cur_func->func_kind & JS_FUNC_ASYNC))- return js_parse_error(s, "unexpected 'await' keyword");- if (!s->cur_func->in_function_body)- return js_parse_error(s, "await in default expression");- if (next_token(s))- return -1;- if (js_parse_unary(s, -1))- return -1;- emit_op(s, OP_await);- exponentiation_flag = 0;- break;- default:- if (js_parse_postfix_expr(s, TRUE))- return -1;- if (!s->got_lf &&- (s->token.val == TOK_DEC || s->token.val == TOK_INC)) {- int opcode, op, scope, label;- JSAtom name;- op = s->token.val;- if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op))- return -1;- emit_op(s, OP_post_dec + op - TOK_DEC);- put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_SECOND,- FALSE);- if (next_token(s))- return -1; - }- break;- }- if (exponentiation_flag) {-#ifdef CONFIG_BIGNUM- if (s->token.val == TOK_POW || s->token.val == TOK_MATH_POW) {- /* Extended exponentiation syntax rules: we extend the ES7- grammar in order to have more intuitive semantics:- -2**2 evaluates to -4. */- if (!(s->cur_func->js_mode & JS_MODE_MATH)) {- if (exponentiation_flag < 0) {- JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'");- return -1;- }- }- if (next_token(s))- return -1;- if (js_parse_unary(s, 1))- return -1;- emit_op(s, OP_pow);- }-#else- if (s->token.val == TOK_POW) {- /* Strict ES7 exponentiation syntax rules: To solve- conficting semantics between different implementations- regarding the precedence of prefix operators and the- postifx exponential, ES7 specifies that -2**2 is a- syntax error. */- if (exponentiation_flag < 0) {- JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'");- return -1;- }- if (next_token(s))- return -1;- if (js_parse_unary(s, 1))- return -1;- emit_op(s, OP_pow);- }-#endif- }- return 0;-}--static __exception int js_parse_expr_binary(JSParseState *s, int level,- BOOL in_accepted)-{- int op, opcode;-- if (level == 0) {- return js_parse_unary(s, 1);- }- if (js_parse_expr_binary(s, level - 1, in_accepted))- return -1;- for(;;) {- op = s->token.val;- switch(level) {- case 1:- switch(op) {- case '*':- opcode = OP_mul;- break;- case '/':- opcode = OP_div;- break;- case '%':-#ifdef CONFIG_BIGNUM- if (s->cur_func->js_mode & JS_MODE_MATH)- opcode = OP_math_mod;- else-#endif- opcode = OP_mod;- break;- default:- return 0;- }- break;- case 2:- switch(op) {- case '+':- opcode = OP_add;- break;- case '-':- opcode = OP_sub;- break;- default:- return 0;- }- break;- case 3:- switch(op) {- case TOK_SHL:- opcode = OP_shl;- break;- case TOK_SAR:- opcode = OP_sar;- break;- case TOK_SHR:- opcode = OP_shr;- break;- default:- return 0;- }- break;- case 4:- switch(op) {- case '<':- opcode = OP_lt;- break;- case '>':- opcode = OP_gt;- break;- case TOK_LTE:- opcode = OP_lte;- break;- case TOK_GTE:- opcode = OP_gte;- break;- case TOK_INSTANCEOF:- opcode = OP_instanceof;- break;- case TOK_IN:- if (in_accepted) {- opcode = OP_in;- } else {- return 0;- }- break;- default:- return 0;- }- break;- case 5:- switch(op) {- case TOK_EQ:- opcode = OP_eq;- break;- case TOK_NEQ:- opcode = OP_neq;- break;- case TOK_STRICT_EQ:- opcode = OP_strict_eq;- break;- case TOK_STRICT_NEQ:- opcode = OP_strict_neq;- break;- default:- return 0;- }- break;- case 6:- switch(op) {- case '&':- opcode = OP_and;- break;- default:- return 0;- }- break;- case 7:- switch(op) {- case '^':- opcode = OP_xor;- break;- default:- return 0;- }- break;- case 8:- switch(op) {- case '|':- opcode = OP_or;- break;- default:- return 0;- }- break;- default:- abort();- }- if (next_token(s))- return -1;- if (js_parse_expr_binary(s, level - 1, in_accepted))- return -1;- emit_op(s, opcode);- }- return 0;-}--static __exception int js_parse_logical_and_or(JSParseState *s, int op,- BOOL in_accepted)-{- int label1;-- if (op == TOK_LAND) {- if (js_parse_expr_binary(s, 8, in_accepted))- return -1;- } else {- if (js_parse_logical_and_or(s, TOK_LAND, in_accepted))- return -1;- }- if (s->token.val == op) {- label1 = new_label(s);-- for(;;) {- if (next_token(s))- return -1;- emit_op(s, OP_dup);- emit_goto(s, op == TOK_LAND ? OP_if_false : OP_if_true, label1);- emit_op(s, OP_drop);-- if (op == TOK_LAND) {- if (js_parse_expr_binary(s, 8, in_accepted))- return -1;- } else {- if (js_parse_logical_and_or(s, TOK_LAND, in_accepted))- return -1;- }- if (s->token.val != op) {- if (s->token.val == TOK_DOUBLE_QUESTION_MARK)- return js_parse_error(s, "cannot mix ?? with && or ||");- break;- }- }-- emit_label(s, label1);- }- return 0;-}--static __exception int js_parse_coalesce_expr(JSParseState *s, BOOL in_accepted)-{- int label1;- - if (js_parse_logical_and_or(s, TOK_LOR, in_accepted))- return -1;- if (s->token.val == TOK_DOUBLE_QUESTION_MARK) {- label1 = new_label(s);- for(;;) {- if (next_token(s))- return -1;- - emit_op(s, OP_dup);- emit_op(s, OP_is_undefined_or_null);- emit_goto(s, OP_if_false, label1);- emit_op(s, OP_drop);- - if (js_parse_expr_binary(s, 8, in_accepted))- return -1;- if (s->token.val != TOK_DOUBLE_QUESTION_MARK)- break;- }- emit_label(s, label1);- }- return 0;-}--static __exception int js_parse_cond_expr(JSParseState *s, BOOL in_accepted)-{- int label1, label2;-- if (js_parse_coalesce_expr(s, in_accepted))- return -1;- if (s->token.val == '?') {- if (next_token(s))- return -1;- label1 = emit_goto(s, OP_if_false, -1);-- if (js_parse_assign_expr(s, TRUE))- return -1;- if (js_parse_expect(s, ':'))- return -1;-- label2 = emit_goto(s, OP_goto, -1);-- emit_label(s, label1);-- if (js_parse_assign_expr(s, in_accepted))- return -1;-- emit_label(s, label2);- }- return 0;-}--static void emit_return(JSParseState *s, BOOL hasval);--static __exception int js_parse_assign_expr(JSParseState *s, BOOL in_accepted)-{- int opcode, op, scope;- JSAtom name0 = JS_ATOM_NULL;- JSAtom name;-- if (s->token.val == TOK_YIELD) {- BOOL is_star = FALSE;- if (!(s->cur_func->func_kind & JS_FUNC_GENERATOR))- return js_parse_error(s, "unexpected 'yield' keyword");- if (!s->cur_func->in_function_body)- return js_parse_error(s, "yield in default expression");- if (next_token(s))- return -1;- /* XXX: is there a better method to detect 'yield' without- parameters ? */- if (s->token.val != ';' && s->token.val != ')' &&- s->token.val != ']' && s->token.val != '}' &&- s->token.val != ',' && s->token.val != ':' && !s->got_lf) {- if (s->token.val == '*') {- is_star = TRUE;- if (next_token(s))- return -1;- }- if (js_parse_assign_expr(s, in_accepted))- return -1;- } else {- emit_op(s, OP_undefined);- }- if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {- int label_loop, label_return, label_next;- int label_return1, label_yield, label_throw, label_throw1;- int label_throw2;-- if (is_star) {- label_loop = new_label(s);- label_yield = new_label(s);-- emit_op(s, OP_for_await_of_start);-- /* remove the catch offset (XXX: could avoid pushing back- undefined) */- emit_op(s, OP_drop);- emit_op(s, OP_undefined);-- emit_op(s, OP_undefined); /* initial value */-- emit_label(s, label_loop);- emit_op(s, OP_async_iterator_next);- emit_op(s, OP_await);- emit_op(s, OP_iterator_get_value_done);- label_next = emit_goto(s, OP_if_true, -1); /* end of loop */- emit_op(s, OP_await);- emit_label(s, label_yield);- emit_op(s, OP_async_yield_star);- emit_op(s, OP_dup);- label_return = emit_goto(s, OP_if_true, -1);- emit_op(s, OP_drop);- emit_goto(s, OP_goto, label_loop);-- emit_label(s, label_return);- emit_op(s, OP_push_i32);- emit_u32(s, 2);- emit_op(s, OP_strict_eq);- label_throw = emit_goto(s, OP_if_true, -1);-- /* return handling */- emit_op(s, OP_await);- emit_op(s, OP_async_iterator_get);- emit_u8(s, 0);- label_return1 = emit_goto(s, OP_if_true, -1);- emit_op(s, OP_await);- emit_op(s, OP_iterator_get_value_done);- /* XXX: the spec does not indicate that an await should be- performed in case done = true, but the tests assume it */- emit_goto(s, OP_if_false, label_yield);-- emit_label(s, label_return1);- emit_op(s, OP_nip);- emit_op(s, OP_nip);- emit_op(s, OP_nip);- emit_return(s, TRUE);-- /* throw handling */- emit_label(s, label_throw);- emit_op(s, OP_async_iterator_get);- emit_u8(s, 1);- label_throw1 = emit_goto(s, OP_if_true, -1);- emit_op(s, OP_await);- emit_op(s, OP_iterator_get_value_done);- emit_goto(s, OP_if_false, label_yield);- /* XXX: the spec does not indicate that an await should be- performed in case done = true, but the tests assume it */- emit_op(s, OP_await);- emit_goto(s, OP_goto, label_next);- /* close the iterator and throw a type error exception */- emit_label(s, label_throw1);- emit_op(s, OP_async_iterator_get);- emit_u8(s, 0);- label_throw2 = emit_goto(s, OP_if_true, -1);- emit_op(s, OP_await);- emit_label(s, label_throw2);- emit_op(s, OP_async_iterator_get);- emit_u8(s, 2); /* throw the type error exception */- emit_op(s, OP_drop); /* never reached */-- emit_label(s, label_next);- emit_op(s, OP_nip); /* keep the value associated with- done = true */- emit_op(s, OP_nip);- emit_op(s, OP_nip);- } else {- emit_op(s, OP_await);- emit_op(s, OP_yield);- label_next = emit_goto(s, OP_if_false, -1);- emit_return(s, TRUE);- emit_label(s, label_next);- }- } else {- int label_next;- if (is_star) {- emit_op(s, OP_for_of_start);- emit_op(s, OP_drop); /* drop the catch offset */- emit_op(s, OP_yield_star);- } else {- emit_op(s, OP_yield);- }- label_next = emit_goto(s, OP_if_false, -1);- emit_return(s, TRUE);- emit_label(s, label_next);- }- return 0;- }- if (s->token.val == TOK_IDENT) {- /* name0 is used to check for OP_set_name pattern, not duplicated */- name0 = s->token.u.ident.atom;- }- if (js_parse_cond_expr(s, in_accepted))- return -1;-- op = s->token.val;- if (op == '=' || (op >= TOK_MUL_ASSIGN && op <= TOK_POW_ASSIGN)) {- int label;- if (next_token(s))- return -1;- if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, (op != '='), op) < 0)- return -1;-- if (js_parse_assign_expr(s, in_accepted)) {- JS_FreeAtom(s->ctx, name);- return -1;- }-- if (op == '=') {- if (opcode == OP_get_ref_value && name == name0) {- set_object_name(s, name);- }- } else {- static const uint8_t assign_opcodes[] = {- OP_mul, OP_div, OP_mod, OP_add, OP_sub,- OP_shl, OP_sar, OP_shr, OP_and, OP_xor, OP_or,-#ifdef CONFIG_BIGNUM- OP_pow,-#endif- OP_pow,- };- op = assign_opcodes[op - TOK_MUL_ASSIGN];-#ifdef CONFIG_BIGNUM- if (s->cur_func->js_mode & JS_MODE_MATH) {- if (op == OP_mod)- op = OP_math_mod;- }-#endif- emit_op(s, op);- }- put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, FALSE);- } else if (op >= TOK_LAND_ASSIGN && op <= TOK_DOUBLE_QUESTION_MARK_ASSIGN) {- int label, label1, depth_lvalue, label2;- - if (next_token(s))- return -1;- if (get_lvalue(s, &opcode, &scope, &name, &label,- &depth_lvalue, TRUE, op) < 0)- return -1;-- emit_op(s, OP_dup);- if (op == TOK_DOUBLE_QUESTION_MARK_ASSIGN)- emit_op(s, OP_is_undefined_or_null);- label1 = emit_goto(s, op == TOK_LOR_ASSIGN ? OP_if_true : OP_if_false,- -1);- emit_op(s, OP_drop);- - if (js_parse_assign_expr(s, in_accepted)) {- JS_FreeAtom(s->ctx, name);- return -1;- }-- if (opcode == OP_get_ref_value && name == name0) {- set_object_name(s, name);- }- - switch(depth_lvalue) {- case 1:- emit_op(s, OP_insert2);- break;- case 2:- emit_op(s, OP_insert3);- break;- case 3:- emit_op(s, OP_insert4);- break;- default:- abort();- }-- /* XXX: we disable the OP_put_ref_value optimization by not- using put_lvalue() otherwise depth_lvalue is not correct */- put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_NOKEEP_DEPTH,- FALSE);- label2 = emit_goto(s, OP_goto, -1);- - emit_label(s, label1);-- /* remove the lvalue stack entries */- while (depth_lvalue != 0) {- emit_op(s, OP_nip);- depth_lvalue--;- }-- emit_label(s, label2);- }- return 0;-}--static __exception int js_parse_expr2(JSParseState *s, BOOL in_accepted)-{- BOOL comma = FALSE;- for(;;) {- if (js_parse_assign_expr(s, in_accepted))- return -1;- if (comma) {- /* prevent get_lvalue from using the last expression- as an lvalue. This also prevents the conversion of- of get_var to get_ref for method lookup in function- call inside `with` statement.- */- s->cur_func->last_opcode_pos = -1;- }- if (s->token.val != ',')- break;- comma = TRUE;- if (next_token(s))- return -1;- emit_op(s, OP_drop);- }- return 0;-}--static __exception int js_parse_expr(JSParseState *s)-{- return js_parse_expr2(s, TRUE);-}--static void push_break_entry(JSFunctionDef *fd, BlockEnv *be,- JSAtom label_name,- int label_break, int label_cont,- int drop_count)-{- be->prev = fd->top_break;- fd->top_break = be;- be->label_name = label_name;- be->label_break = label_break;- be->label_cont = label_cont;- be->drop_count = drop_count;- be->label_finally = -1;- be->scope_level = fd->scope_level;- be->has_iterator = FALSE;-}--static void pop_break_entry(JSFunctionDef *fd)-{- BlockEnv *be;- be = fd->top_break;- fd->top_break = be->prev;-}--static __exception int emit_break(JSParseState *s, JSAtom name, int is_cont)-{- BlockEnv *top;- int i, scope_level;-- scope_level = s->cur_func->scope_level;- top = s->cur_func->top_break;- while (top != NULL) {- close_scopes(s, scope_level, top->scope_level);- scope_level = top->scope_level;- if (is_cont &&- top->label_cont != -1 &&- (name == JS_ATOM_NULL || top->label_name == name)) {- /* continue stays inside the same block */- emit_goto(s, OP_goto, top->label_cont);- return 0;- }- if (!is_cont &&- top->label_break != -1 &&- (name == JS_ATOM_NULL || top->label_name == name)) {- emit_goto(s, OP_goto, top->label_break);- return 0;- }- i = 0;- if (top->has_iterator) {- emit_op(s, OP_iterator_close);- i += 3;- }- for(; i < top->drop_count; i++)- emit_op(s, OP_drop);- if (top->label_finally != -1) {- /* must push dummy value to keep same stack depth */- emit_op(s, OP_undefined);- emit_goto(s, OP_gosub, top->label_finally);- emit_op(s, OP_drop);- }- top = top->prev;- }- if (name == JS_ATOM_NULL) {- if (is_cont)- return js_parse_error(s, "continue must be inside loop");- else- return js_parse_error(s, "break must be inside loop or switch");- } else {- return js_parse_error(s, "break/continue label not found");- }-}--/* execute the finally blocks before return */-static void emit_return(JSParseState *s, BOOL hasval)-{- BlockEnv *top;- int drop_count;-- drop_count = 0;- top = s->cur_func->top_break;- while (top != NULL) {- /* XXX: emit the appropriate OP_leave_scope opcodes? Probably not- required as all local variables will be closed upon returning- from JS_CallInternal, but not in the same order. */- if (top->has_iterator) {- /* with 'yield', the exact number of OP_drop to emit is- unknown, so we use a specific operation to look for- the catch offset */- if (!hasval) {- emit_op(s, OP_undefined);- hasval = TRUE;- }- emit_op(s, OP_iterator_close_return);- if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {- int label_next;- emit_op(s, OP_async_iterator_close);- label_next = emit_goto(s, OP_if_true, -1);- emit_op(s, OP_await);- emit_label(s, label_next);- emit_op(s, OP_drop);- } else {- emit_op(s, OP_iterator_close);- }- drop_count = -3;- }- drop_count += top->drop_count;- if (top->label_finally != -1) {- while(drop_count) {- /* must keep the stack top if hasval */- emit_op(s, hasval ? OP_nip : OP_drop);- drop_count--;- }- if (!hasval) {- /* must push return value to keep same stack size */- emit_op(s, OP_undefined);- hasval = TRUE;- }- emit_goto(s, OP_gosub, top->label_finally);- }- top = top->prev;- }- if (s->cur_func->is_derived_class_constructor) {- int label_return;-- /* 'this' can be uninitialized, so it may be accessed only if- the derived class constructor does not return an object */- if (hasval) {- emit_op(s, OP_check_ctor_return);- label_return = emit_goto(s, OP_if_false, -1);- emit_op(s, OP_drop);- } else {- label_return = -1;- }-- /* XXX: if this is not initialized, should throw the- ReferenceError in the caller realm */- emit_op(s, OP_scope_get_var);- emit_atom(s, JS_ATOM_this);- emit_u16(s, 0);-- emit_label(s, label_return);- emit_op(s, OP_return);- } else if (s->cur_func->func_kind != JS_FUNC_NORMAL) {- if (!hasval) {- emit_op(s, OP_undefined);- } else if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {- emit_op(s, OP_await);- }- emit_op(s, OP_return_async);- } else {- emit_op(s, hasval ? OP_return : OP_return_undef);- }-}--#define DECL_MASK_FUNC (1 << 0) /* allow normal function declaration */-/* ored with DECL_MASK_FUNC if function declarations are allowed with a label */-#define DECL_MASK_FUNC_WITH_LABEL (1 << 1)-#define DECL_MASK_OTHER (1 << 2) /* all other declarations */-#define DECL_MASK_ALL (DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL | DECL_MASK_OTHER)--static __exception int js_parse_statement_or_decl(JSParseState *s,- int decl_mask);--static __exception int js_parse_statement(JSParseState *s)-{- return js_parse_statement_or_decl(s, 0);-}--static __exception int js_parse_block(JSParseState *s)-{- if (js_parse_expect(s, '{'))- return -1;- if (s->token.val != '}') {- push_scope(s);- for(;;) {- if (js_parse_statement_or_decl(s, DECL_MASK_ALL))- return -1;- if (s->token.val == '}')- break;- }- pop_scope(s);- }- if (next_token(s))- return -1;- return 0;-}--static __exception int js_parse_var(JSParseState *s, BOOL in_accepted, int tok,- BOOL export_flag)-{- JSContext *ctx = s->ctx;- JSFunctionDef *fd = s->cur_func;- JSAtom name = JS_ATOM_NULL;-- for (;;) {- if (s->token.val == TOK_IDENT) {- if (s->token.u.ident.is_reserved) {- return js_parse_error_reserved_identifier(s);- }- name = JS_DupAtom(ctx, s->token.u.ident.atom);- if (name == JS_ATOM_let && (tok == TOK_LET || tok == TOK_CONST)) {- js_parse_error(s, "'let' is not a valid lexical identifier");- goto var_error;- }- if (next_token(s))- goto var_error;- if (js_define_var(s, name, tok))- goto var_error;- if (export_flag) {- if (!add_export_entry(s, s->cur_func->module, name, name,- JS_EXPORT_TYPE_LOCAL))- goto var_error;- }-- if (s->token.val == '=') {- if (next_token(s))- goto var_error;- if (tok == TOK_VAR) {- /* Must make a reference for proper `with` semantics */- int opcode, scope, label;- JSAtom name1;-- emit_op(s, OP_scope_get_var);- emit_atom(s, name);- emit_u16(s, fd->scope_level);- if (get_lvalue(s, &opcode, &scope, &name1, &label, NULL, FALSE, '=') < 0)- goto var_error;- if (js_parse_assign_expr(s, in_accepted)) {- JS_FreeAtom(ctx, name1);- goto var_error;- }- set_object_name(s, name);- put_lvalue(s, opcode, scope, name1, label,- PUT_LVALUE_NOKEEP, FALSE);- } else {- if (js_parse_assign_expr(s, in_accepted))- goto var_error;- set_object_name(s, name);- emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ?- OP_scope_put_var_init : OP_scope_put_var);- emit_atom(s, name);- emit_u16(s, fd->scope_level);- }- } else {- if (tok == TOK_CONST) {- js_parse_error(s, "missing initializer for const variable");- goto var_error;- }- if (tok == TOK_LET) {- /* initialize lexical variable upon entering its scope */- emit_op(s, OP_undefined);- emit_op(s, OP_scope_put_var_init);- emit_atom(s, name);- emit_u16(s, fd->scope_level);- }- }- JS_FreeAtom(ctx, name);- } else {- int skip_bits;- if ((s->token.val == '[' || s->token.val == '{')- && js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') {- emit_op(s, OP_undefined);- if (js_parse_destructuring_element(s, tok, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE))- return -1;- } else {- return js_parse_error(s, "variable name expected");- }- }- if (s->token.val != ',')- break;- if (next_token(s))- return -1;- }- return 0;-- var_error:- JS_FreeAtom(ctx, name);- return -1;-}--/* test if the current token is a label. Use simplistic look-ahead scanner */-static BOOL is_label(JSParseState *s)-{- return (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved &&- peek_token(s, FALSE) == ':');-}--/* test if the current token is a let keyword. Use simplistic look-ahead scanner */-static int is_let(JSParseState *s, int decl_mask)-{- int res = FALSE;-- if (token_is_pseudo_keyword(s, JS_ATOM_let)) {-#if 1- JSParsePos pos;- js_parse_get_pos(s, &pos);- for (;;) {- if (next_token(s)) {- res = -1;- break;- }- if (s->token.val == '[') {- /* let [ is a syntax restriction:- it never introduces an ExpressionStatement */- res = TRUE;- break;- }- if (s->token.val == '{' ||- (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved) ||- s->token.val == TOK_LET ||- s->token.val == TOK_YIELD ||- s->token.val == TOK_AWAIT) {- /* Check for possible ASI if not scanning for Declaration */- /* XXX: should also check that `{` introduces a BindingPattern,- but Firefox does not and rejects eval("let=1;let\n{if(1)2;}") */- if (s->last_line_num == s->token.line_num || (decl_mask & DECL_MASK_OTHER)) {- res = TRUE;- break;- }- break;- }- break;- }- if (js_parse_seek_token(s, &pos)) {- res = -1;- }-#else- int tok = peek_token(s, TRUE);- if (tok == '{' || tok == TOK_IDENT || peek_token(s, FALSE) == '[') {- res = TRUE;- }-#endif- }- return res;-}--/* XXX: handle IteratorClose when exiting the loop before the- enumeration is done */-static __exception int js_parse_for_in_of(JSParseState *s, int label_name,- BOOL is_async)-{- JSContext *ctx = s->ctx;- JSFunctionDef *fd = s->cur_func;- JSAtom var_name;- BOOL has_initializer, is_for_of, has_destructuring;- int tok, tok1, opcode, scope, block_scope_level;- int label_next, label_expr, label_cont, label_body, label_break;- int pos_next, pos_expr;- BlockEnv break_entry;-- has_initializer = FALSE;- has_destructuring = FALSE;- is_for_of = FALSE;- block_scope_level = fd->scope_level;- label_cont = new_label(s);- label_body = new_label(s);- label_break = new_label(s);- label_next = new_label(s);-- /* create scope for the lexical variables declared in the enumeration- expressions. XXX: Not completely correct because of weird capturing- semantics in `for (i of o) a.push(function(){return i})` */- push_scope(s);-- /* local for_in scope starts here so individual elements- can be closed in statement. */- push_break_entry(s->cur_func, &break_entry,- label_name, label_break, label_cont, 1);- break_entry.scope_level = block_scope_level;-- label_expr = emit_goto(s, OP_goto, -1);-- pos_next = s->cur_func->byte_code.size;- emit_label(s, label_next);-- tok = s->token.val;- switch (is_let(s, DECL_MASK_OTHER)) {- case TRUE:- tok = TOK_LET;- break;- case FALSE:- break;- default:- return -1;- }- if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) {- if (next_token(s))- return -1;-- if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) {- if (s->token.val == '[' || s->token.val == '{') {- if (js_parse_destructuring_element(s, tok, 0, TRUE, -1, FALSE))- return -1;- has_destructuring = TRUE;- } else {- return js_parse_error(s, "variable name expected");- }- var_name = JS_ATOM_NULL;- } else {- var_name = JS_DupAtom(ctx, s->token.u.ident.atom);- if (next_token(s)) {- JS_FreeAtom(s->ctx, var_name);- return -1;- }- if (js_define_var(s, var_name, tok)) {- JS_FreeAtom(s->ctx, var_name);- return -1;- }- emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ?- OP_scope_put_var_init : OP_scope_put_var);- emit_atom(s, var_name);- emit_u16(s, fd->scope_level);- }- } else {- int skip_bits;- if ((s->token.val == '[' || s->token.val == '{')- && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == TOK_IN || tok1 == TOK_OF)) {- if (js_parse_destructuring_element(s, 0, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE))- return -1;- } else {- int lvalue_label;- if (js_parse_postfix_expr(s, TRUE))- return -1;- if (get_lvalue(s, &opcode, &scope, &var_name, &lvalue_label,- NULL, FALSE, TOK_FOR))- return -1;- put_lvalue(s, opcode, scope, var_name, lvalue_label,- PUT_LVALUE_NOKEEP_BOTTOM, FALSE);- }- var_name = JS_ATOM_NULL;- }- emit_goto(s, OP_goto, label_body);-- pos_expr = s->cur_func->byte_code.size;- emit_label(s, label_expr);- if (s->token.val == '=') {- /* XXX: potential scoping issue if inside `with` statement */- has_initializer = TRUE;- /* parse and evaluate initializer prior to evaluating the- object (only used with "for in" with a non lexical variable- in non strict mode */- if (next_token(s) || js_parse_assign_expr(s, FALSE)) {- JS_FreeAtom(ctx, var_name);- return -1;- }- if (var_name != JS_ATOM_NULL) {- emit_op(s, OP_scope_put_var);- emit_atom(s, var_name);- emit_u16(s, fd->scope_level);- }- }- JS_FreeAtom(ctx, var_name);-- if (token_is_pseudo_keyword(s, JS_ATOM_of)) {- break_entry.has_iterator = is_for_of = TRUE;- break_entry.drop_count += 2;- if (has_initializer)- goto initializer_error;- } else if (s->token.val == TOK_IN) {- if (is_async)- return js_parse_error(s, "'for await' loop should be used with 'of'");- if (has_initializer &&- (tok != TOK_VAR || (fd->js_mode & JS_MODE_STRICT) ||- has_destructuring)) {- initializer_error:- return js_parse_error(s, "a declaration in the head of a for-%s loop can't have an initializer",- is_for_of ? "of" : "in");- }- } else {- return js_parse_error(s, "expected 'of' or 'in' in for control expression");- }- if (next_token(s))- return -1;- if (is_for_of) {- if (js_parse_assign_expr(s, TRUE))- return -1;- } else {- if (js_parse_expr(s))- return -1;- }- /* close the scope after having evaluated the expression so that- the TDZ values are in the closures */- close_scopes(s, s->cur_func->scope_level, block_scope_level);- if (is_for_of) {- if (is_async)- emit_op(s, OP_for_await_of_start);- else- emit_op(s, OP_for_of_start);- /* on stack: enum_rec */- } else {- emit_op(s, OP_for_in_start);- /* on stack: enum_obj */- }- emit_goto(s, OP_goto, label_cont);-- if (js_parse_expect(s, ')'))- return -1;-- if (OPTIMIZE) {- /* move the `next` code here */- DynBuf *bc = &s->cur_func->byte_code;- int chunk_size = pos_expr - pos_next;- int offset = bc->size - pos_next;- int i;- dbuf_realloc(bc, bc->size + chunk_size);- dbuf_put(bc, bc->buf + pos_next, chunk_size);- memset(bc->buf + pos_next, OP_nop, chunk_size);- /* `next` part ends with a goto */- s->cur_func->last_opcode_pos = bc->size - 5;- /* relocate labels */- for (i = label_cont; i < s->cur_func->label_count; i++) {- LabelSlot *ls = &s->cur_func->label_slots[i];- if (ls->pos >= pos_next && ls->pos < pos_expr)- ls->pos += offset;- }- }-- emit_label(s, label_body);- if (js_parse_statement(s))- return -1;-- close_scopes(s, s->cur_func->scope_level, block_scope_level);-- emit_label(s, label_cont);- if (is_for_of) {- if (is_async) {- /* call the next method */- emit_op(s, OP_for_await_of_next);- /* get the result of the promise */- emit_op(s, OP_await);- /* unwrap the value and done values */- emit_op(s, OP_iterator_get_value_done);- } else {- emit_op(s, OP_for_of_next);- emit_u8(s, 0);- }- } else {- emit_op(s, OP_for_in_next);- }- /* on stack: enum_rec / enum_obj value bool */- emit_goto(s, OP_if_false, label_next);- /* drop the undefined value from for_xx_next */- emit_op(s, OP_drop);-- emit_label(s, label_break);- if (is_for_of) {- /* close and drop enum_rec */- emit_op(s, OP_iterator_close);- } else {- emit_op(s, OP_drop);- }- pop_break_entry(s->cur_func);- pop_scope(s);- return 0;-}--static void set_eval_ret_undefined(JSParseState *s)-{- if (s->cur_func->eval_ret_idx >= 0) {- emit_op(s, OP_undefined);- emit_op(s, OP_put_loc);- emit_u16(s, s->cur_func->eval_ret_idx);- }-}--static __exception int js_parse_statement_or_decl(JSParseState *s,- int decl_mask)-{- JSContext *ctx = s->ctx;- JSAtom label_name;- int tok;-- /* specific label handling */- /* XXX: support multiple labels on loop statements */- label_name = JS_ATOM_NULL;- if (is_label(s)) {- BlockEnv *be;-- label_name = JS_DupAtom(ctx, s->token.u.ident.atom);-- for (be = s->cur_func->top_break; be; be = be->prev) {- if (be->label_name == label_name) {- js_parse_error(s, "duplicate label name");- goto fail;- }- }-- if (next_token(s))- goto fail;- if (js_parse_expect(s, ':'))- goto fail;- if (s->token.val != TOK_FOR- && s->token.val != TOK_DO- && s->token.val != TOK_WHILE) {- /* labelled regular statement */- int label_break, mask;- BlockEnv break_entry;-- label_break = new_label(s);- push_break_entry(s->cur_func, &break_entry,- label_name, label_break, -1, 0);- if (!(s->cur_func->js_mode & JS_MODE_STRICT) &&- (decl_mask & DECL_MASK_FUNC_WITH_LABEL)) {- mask = DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL;- } else {- mask = 0;- }- if (js_parse_statement_or_decl(s, mask))- goto fail;- emit_label(s, label_break);- pop_break_entry(s->cur_func);- goto done;- }- }-- switch(tok = s->token.val) {- case '{':- if (js_parse_block(s))- goto fail;- break;- case TOK_RETURN:- if (s->cur_func->is_eval) {- js_parse_error(s, "return not in a function");- goto fail;- }- if (next_token(s))- goto fail;- if (s->token.val != ';' && s->token.val != '}' && !s->got_lf) {- if (js_parse_expr(s))- goto fail;- emit_return(s, TRUE);- } else {- emit_return(s, FALSE);- }- if (js_parse_expect_semi(s))- goto fail;- break;- case TOK_THROW:- if (next_token(s))- goto fail;- if (s->got_lf) {- js_parse_error(s, "line terminator not allowed after throw");- goto fail;- }- if (js_parse_expr(s))- goto fail;- emit_op(s, OP_throw);- if (js_parse_expect_semi(s))- goto fail;- break;- case TOK_LET:- case TOK_CONST:- haslet:- if (!(decl_mask & DECL_MASK_OTHER)) {- js_parse_error(s, "lexical declarations can't appear in single-statement context");- goto fail;- }- /* fall thru */- case TOK_VAR:- if (next_token(s))- goto fail;- if (js_parse_var(s, TRUE, tok, FALSE))- goto fail;- if (js_parse_expect_semi(s))- goto fail;- break;- case TOK_IF:- {- int label1, label2, mask;- if (next_token(s))- goto fail;- /* create a new scope for `let f;if(1) function f(){}` */- push_scope(s);- set_eval_ret_undefined(s);- if (js_parse_expr_paren(s))- goto fail;- label1 = emit_goto(s, OP_if_false, -1);- if (s->cur_func->js_mode & JS_MODE_STRICT)- mask = 0;- else- mask = DECL_MASK_FUNC; /* Annex B.3.4 */-- if (js_parse_statement_or_decl(s, mask))- goto fail;-- if (s->token.val == TOK_ELSE) {- label2 = emit_goto(s, OP_goto, -1);- if (next_token(s))- goto fail;-- emit_label(s, label1);- if (js_parse_statement_or_decl(s, mask))- goto fail;-- label1 = label2;- }- emit_label(s, label1);- pop_scope(s);- }- break;- case TOK_WHILE:- {- int label_cont, label_break;- BlockEnv break_entry;-- label_cont = new_label(s);- label_break = new_label(s);-- push_break_entry(s->cur_func, &break_entry,- label_name, label_break, label_cont, 0);-- if (next_token(s))- goto fail;-- set_eval_ret_undefined(s);-- emit_label(s, label_cont);- if (js_parse_expr_paren(s))- goto fail;- emit_goto(s, OP_if_false, label_break);-- if (js_parse_statement(s))- goto fail;- emit_goto(s, OP_goto, label_cont);-- emit_label(s, label_break);-- pop_break_entry(s->cur_func);- }- break;- case TOK_DO:- {- int label_cont, label_break, label1;- BlockEnv break_entry;-- label_cont = new_label(s);- label_break = new_label(s);- label1 = new_label(s);-- push_break_entry(s->cur_func, &break_entry,- label_name, label_break, label_cont, 0);-- if (next_token(s))- goto fail;-- emit_label(s, label1);-- set_eval_ret_undefined(s);-- if (js_parse_statement(s))- goto fail;-- emit_label(s, label_cont);- if (js_parse_expect(s, TOK_WHILE))- goto fail;- if (js_parse_expr_paren(s))- goto fail;- /* Insert semicolon if missing */- if (s->token.val == ';') {- if (next_token(s))- goto fail;- }- emit_goto(s, OP_if_true, label1);-- emit_label(s, label_break);-- pop_break_entry(s->cur_func);- }- break;- case TOK_FOR:- {- int label_cont, label_break, label_body, label_test;- int pos_cont, pos_body, block_scope_level;- BlockEnv break_entry;- int tok, bits;- BOOL is_async;-- if (next_token(s))- goto fail;-- set_eval_ret_undefined(s);- bits = 0;- is_async = FALSE;- if (s->token.val == '(') {- js_parse_skip_parens_token(s, &bits, FALSE);- } else if (s->token.val == TOK_AWAIT) {- if (!(s->cur_func->func_kind & JS_FUNC_ASYNC)) {- js_parse_error(s, "for await is only valid in asynchronous functions");- goto fail;- }- is_async = TRUE;- if (next_token(s))- goto fail;- }- if (js_parse_expect(s, '('))- goto fail;-- if (!(bits & SKIP_HAS_SEMI)) {- /* parse for/in or for/of */- if (js_parse_for_in_of(s, label_name, is_async))- goto fail;- break;- }- block_scope_level = s->cur_func->scope_level;-- /* create scope for the lexical variables declared in the initial,- test and increment expressions */- push_scope(s);- /* initial expression */- tok = s->token.val;- if (tok != ';') {- switch (is_let(s, DECL_MASK_OTHER)) {- case TRUE:- tok = TOK_LET;- break;- case FALSE:- break;- default:- goto fail;- }- if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) {- if (next_token(s))- goto fail;- if (js_parse_var(s, FALSE, tok, FALSE))- goto fail;- } else {- if (js_parse_expr2(s, FALSE))- goto fail;- emit_op(s, OP_drop);- }-- /* close the closures before the first iteration */- close_scopes(s, s->cur_func->scope_level, block_scope_level);- }- if (js_parse_expect(s, ';'))- goto fail;-- label_test = new_label(s);- label_cont = new_label(s);- label_body = new_label(s);- label_break = new_label(s);-- push_break_entry(s->cur_func, &break_entry,- label_name, label_break, label_cont, 0);-- /* test expression */- if (s->token.val == ';') {- /* no test expression */- label_test = label_body;- } else {- emit_label(s, label_test);- if (js_parse_expr(s))- goto fail;- emit_goto(s, OP_if_false, label_break);- }- if (js_parse_expect(s, ';'))- goto fail;-- if (s->token.val == ')') {- /* no end expression */- break_entry.label_cont = label_cont = label_test;- pos_cont = 0; /* avoid warning */- } else {- /* skip the end expression */- emit_goto(s, OP_goto, label_body);-- pos_cont = s->cur_func->byte_code.size;- emit_label(s, label_cont);- if (js_parse_expr(s))- goto fail;- emit_op(s, OP_drop);- if (label_test != label_body)- emit_goto(s, OP_goto, label_test);- }- if (js_parse_expect(s, ')'))- goto fail;-- pos_body = s->cur_func->byte_code.size;- emit_label(s, label_body);- if (js_parse_statement(s))- goto fail;-- /* close the closures before the next iteration */- /* XXX: check continue case */- close_scopes(s, s->cur_func->scope_level, block_scope_level);-- if (OPTIMIZE && label_test != label_body && label_cont != label_test) {- /* move the increment code here */- DynBuf *bc = &s->cur_func->byte_code;- int chunk_size = pos_body - pos_cont;- int offset = bc->size - pos_cont;- int i;- dbuf_realloc(bc, bc->size + chunk_size);- dbuf_put(bc, bc->buf + pos_cont, chunk_size);- memset(bc->buf + pos_cont, OP_nop, chunk_size);- /* increment part ends with a goto */- s->cur_func->last_opcode_pos = bc->size - 5;- /* relocate labels */- for (i = label_cont; i < s->cur_func->label_count; i++) {- LabelSlot *ls = &s->cur_func->label_slots[i];- if (ls->pos >= pos_cont && ls->pos < pos_body)- ls->pos += offset;- }- } else {- emit_goto(s, OP_goto, label_cont);- }-- emit_label(s, label_break);-- pop_break_entry(s->cur_func);- pop_scope(s);- }- break;- case TOK_BREAK:- case TOK_CONTINUE:- {- int is_cont = s->token.val - TOK_BREAK;- int label;-- if (next_token(s))- goto fail;- if (!s->got_lf && s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)- label = s->token.u.ident.atom;- else- label = JS_ATOM_NULL;- if (emit_break(s, label, is_cont))- goto fail;- if (label != JS_ATOM_NULL) {- if (next_token(s))- goto fail;- }- if (js_parse_expect_semi(s))- goto fail;- }- break;- case TOK_SWITCH:- {- int label_case, label_break, label1;- int default_label_pos;- BlockEnv break_entry;-- if (next_token(s))- goto fail;-- set_eval_ret_undefined(s);- if (js_parse_expr_paren(s))- goto fail;-- push_scope(s);- label_break = new_label(s);- push_break_entry(s->cur_func, &break_entry,- label_name, label_break, -1, 1);-- if (js_parse_expect(s, '{'))- goto fail;-- default_label_pos = -1;- label_case = -1;- while (s->token.val != '}') {- if (s->token.val == TOK_CASE) {- label1 = -1;- if (label_case >= 0) {- /* skip the case if needed */- label1 = emit_goto(s, OP_goto, -1);- }- emit_label(s, label_case);- label_case = -1;- for (;;) {- /* parse a sequence of case clauses */- if (next_token(s))- goto fail;- emit_op(s, OP_dup);- if (js_parse_expr(s))- goto fail;- if (js_parse_expect(s, ':'))- goto fail;- emit_op(s, OP_strict_eq);- if (s->token.val == TOK_CASE) {- label1 = emit_goto(s, OP_if_true, label1);- } else {- label_case = emit_goto(s, OP_if_false, -1);- emit_label(s, label1);- break;- }- }- } else if (s->token.val == TOK_DEFAULT) {- if (next_token(s))- goto fail;- if (js_parse_expect(s, ':'))- goto fail;- if (default_label_pos >= 0) {- js_parse_error(s, "duplicate default");- goto fail;- }- if (label_case < 0) {- /* falling thru direct from switch expression */- label_case = emit_goto(s, OP_goto, -1);- }- /* Emit a dummy label opcode. Label will be patched after- the end of the switch body. Do not use emit_label(s, 0)- because it would clobber label 0 address, preventing- proper optimizer operation.- */- emit_op(s, OP_label);- emit_u32(s, 0);- default_label_pos = s->cur_func->byte_code.size - 4;- } else {- if (label_case < 0) {- /* falling thru direct from switch expression */- js_parse_error(s, "invalid switch statement");- goto fail;- }- if (js_parse_statement_or_decl(s, DECL_MASK_ALL))- goto fail;- }- }- if (js_parse_expect(s, '}'))- goto fail;- if (default_label_pos >= 0) {- /* Ugly patch for the the `default` label, shameful and risky */- put_u32(s->cur_func->byte_code.buf + default_label_pos,- label_case);- s->cur_func->label_slots[label_case].pos = default_label_pos + 4;- } else {- emit_label(s, label_case);- }- emit_label(s, label_break);- emit_op(s, OP_drop); /* drop the switch expression */-- pop_break_entry(s->cur_func);- pop_scope(s);- }- break;- case TOK_TRY:- {- int label_catch, label_catch2, label_finally, label_end;- JSAtom name;- BlockEnv block_env;-- set_eval_ret_undefined(s);- if (next_token(s))- goto fail;- label_catch = new_label(s);- label_catch2 = new_label(s);- label_finally = new_label(s);- label_end = new_label(s);-- emit_goto(s, OP_catch, label_catch);-- push_break_entry(s->cur_func, &block_env,- JS_ATOM_NULL, -1, -1, 1);- block_env.label_finally = label_finally;-- if (js_parse_block(s))- goto fail;-- pop_break_entry(s->cur_func);-- if (js_is_live_code(s)) {- /* drop the catch offset */- emit_op(s, OP_drop);- /* must push dummy value to keep same stack size */- emit_op(s, OP_undefined);- emit_goto(s, OP_gosub, label_finally);- emit_op(s, OP_drop);-- emit_goto(s, OP_goto, label_end);- }-- if (s->token.val == TOK_CATCH) {- if (next_token(s))- goto fail;-- push_scope(s); /* catch variable */- emit_label(s, label_catch);-- if (s->token.val == '{') {- /* support optional-catch-binding feature */- emit_op(s, OP_drop); /* pop the exception object */- } else {- if (js_parse_expect(s, '('))- goto fail;- if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) {- if (s->token.val == '[' || s->token.val == '{') {- /* XXX: TOK_LET is not completely correct */- if (js_parse_destructuring_element(s, TOK_LET, 0, TRUE, -1, TRUE))- goto fail;- } else {- js_parse_error(s, "identifier expected");- goto fail;- }- } else {- name = JS_DupAtom(ctx, s->token.u.ident.atom);- if (next_token(s)- || js_define_var(s, name, TOK_CATCH) < 0) {- JS_FreeAtom(ctx, name);- goto fail;- }- /* store the exception value in the catch variable */- emit_op(s, OP_scope_put_var);- emit_u32(s, name);- emit_u16(s, s->cur_func->scope_level);- }- if (js_parse_expect(s, ')'))- goto fail;- }- /* XXX: should keep the address to nop it out if there is no finally block */- emit_goto(s, OP_catch, label_catch2);-- push_scope(s); /* catch block */- push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL,- -1, -1, 1);- block_env.label_finally = label_finally;-- if (js_parse_block(s))- goto fail;-- pop_break_entry(s->cur_func);- pop_scope(s); /* catch block */- pop_scope(s); /* catch variable */-- if (js_is_live_code(s)) {- /* drop the catch2 offset */- emit_op(s, OP_drop);- /* XXX: should keep the address to nop it out if there is no finally block */- /* must push dummy value to keep same stack size */- emit_op(s, OP_undefined);- emit_goto(s, OP_gosub, label_finally);- emit_op(s, OP_drop);- emit_goto(s, OP_goto, label_end);- }- /* catch exceptions thrown in the catch block to execute the- * finally clause and rethrow the exception */- emit_label(s, label_catch2);- /* catch value is at TOS, no need to push undefined */- emit_goto(s, OP_gosub, label_finally);- emit_op(s, OP_throw);-- } else if (s->token.val == TOK_FINALLY) {- /* finally without catch : execute the finally clause- * and rethrow the exception */- emit_label(s, label_catch);- /* catch value is at TOS, no need to push undefined */- emit_goto(s, OP_gosub, label_finally);- emit_op(s, OP_throw);- } else {- js_parse_error(s, "expecting catch or finally");- goto fail;- }- emit_label(s, label_finally);- if (s->token.val == TOK_FINALLY) {- int saved_eval_ret_idx;- if (next_token(s))- goto fail;- /* on the stack: ret_value gosub_ret_value */- push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL,- -1, -1, 2);- saved_eval_ret_idx = s->cur_func->eval_ret_idx;- s->cur_func->eval_ret_idx = -1;- /* 'finally' does not update eval_ret */- if (js_parse_block(s))- goto fail;- s->cur_func->eval_ret_idx = saved_eval_ret_idx;- pop_break_entry(s->cur_func);- }- emit_op(s, OP_ret);- emit_label(s, label_end);- }- break;- case ';':- /* empty statement */- if (next_token(s))- goto fail;- break;- case TOK_WITH:- if (s->cur_func->js_mode & JS_MODE_STRICT) {- js_parse_error(s, "invalid keyword: with");- goto fail;- } else {- int with_idx;-- if (next_token(s))- goto fail;-- if (js_parse_expr_paren(s))- goto fail;-- push_scope(s);- with_idx = define_var(s, s->cur_func, JS_ATOM__with_,- JS_VAR_DEF_WITH);- if (with_idx < 0)- goto fail;- emit_op(s, OP_to_object);- emit_op(s, OP_put_loc);- emit_u16(s, with_idx);-- set_eval_ret_undefined(s);- if (js_parse_statement(s))- goto fail;-- /* Popping scope drops lexical context for the with object variable */- pop_scope(s);- }- break;- case TOK_FUNCTION:- /* ES6 Annex B.3.2 and B.3.3 semantics */- if (!(decl_mask & DECL_MASK_FUNC))- goto func_decl_error;- if (!(decl_mask & DECL_MASK_OTHER) && peek_token(s, FALSE) == '*')- goto func_decl_error;- goto parse_func_var;- case TOK_IDENT:- if (s->token.u.ident.is_reserved) {- js_parse_error_reserved_identifier(s);- goto fail;- }- /* Determine if `let` introduces a Declaration or an ExpressionStatement */- switch (is_let(s, decl_mask)) {- case TRUE:- tok = TOK_LET;- goto haslet;- case FALSE:- break;- default:- goto fail;- }- if (token_is_pseudo_keyword(s, JS_ATOM_async) &&- peek_token(s, TRUE) == TOK_FUNCTION) {- if (!(decl_mask & DECL_MASK_OTHER)) {- func_decl_error:- js_parse_error(s, "function declarations can't appear in single-statement context");- goto fail;- }- parse_func_var:- if (js_parse_function_decl(s, JS_PARSE_FUNC_VAR,- JS_FUNC_NORMAL, JS_ATOM_NULL,- s->token.ptr, s->token.line_num))- goto fail;- break;- }- goto hasexpr;-- case TOK_CLASS:- if (!(decl_mask & DECL_MASK_OTHER)) {- js_parse_error(s, "class declarations can't appear in single-statement context");- goto fail;- }- if (js_parse_class(s, FALSE, JS_PARSE_EXPORT_NONE))- return -1;- break;-- case TOK_DEBUGGER:- /* currently no debugger, so just skip the keyword */- if (next_token(s))- goto fail;- if (js_parse_expect_semi(s))- goto fail;- break;- - case TOK_ENUM:- case TOK_EXPORT:- case TOK_EXTENDS:- js_unsupported_keyword(s, s->token.u.ident.atom);- goto fail;-- default:- hasexpr:- if (js_parse_expr(s))- goto fail;- if (s->cur_func->eval_ret_idx >= 0) {- /* store the expression value so that it can be returned- by eval() */- emit_op(s, OP_put_loc);- emit_u16(s, s->cur_func->eval_ret_idx);- } else {- emit_op(s, OP_drop); /* drop the result */- }- if (js_parse_expect_semi(s))- goto fail;- break;- }-done:- JS_FreeAtom(ctx, label_name);- return 0;-fail:- JS_FreeAtom(ctx, label_name);- return -1;-}--/* 'name' is freed */-static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name)-{- JSModuleDef *m;- m = js_mallocz(ctx, sizeof(*m));- if (!m) {- JS_FreeAtom(ctx, name);- return NULL;- }- m->header.ref_count = 1;- m->module_name = name;- m->module_ns = JS_UNDEFINED;- m->func_obj = JS_UNDEFINED;- m->eval_exception = JS_UNDEFINED;- m->meta_obj = JS_UNDEFINED;- list_add_tail(&m->link, &ctx->loaded_modules);- return m;-}--static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,- JS_MarkFunc *mark_func)-{- int i;-- for(i = 0; i < m->export_entries_count; i++) {- JSExportEntry *me = &m->export_entries[i];- if (me->export_type == JS_EXPORT_TYPE_LOCAL &&- me->u.local.var_ref) {- mark_func(rt, &me->u.local.var_ref->header);- }- }-- JS_MarkValue(rt, m->module_ns, mark_func);- JS_MarkValue(rt, m->func_obj, mark_func);- JS_MarkValue(rt, m->eval_exception, mark_func);- JS_MarkValue(rt, m->meta_obj, mark_func);-}--static void js_free_module_def(JSContext *ctx, JSModuleDef *m)-{- int i;-- JS_FreeAtom(ctx, m->module_name);-- for(i = 0; i < m->req_module_entries_count; i++) {- JSReqModuleEntry *rme = &m->req_module_entries[i];- JS_FreeAtom(ctx, rme->module_name);- }- js_free(ctx, m->req_module_entries);-- for(i = 0; i < m->export_entries_count; i++) {- JSExportEntry *me = &m->export_entries[i];- if (me->export_type == JS_EXPORT_TYPE_LOCAL)- free_var_ref(ctx->rt, me->u.local.var_ref);- JS_FreeAtom(ctx, me->export_name);- JS_FreeAtom(ctx, me->local_name);- }- js_free(ctx, m->export_entries);-- js_free(ctx, m->star_export_entries);-- for(i = 0; i < m->import_entries_count; i++) {- JSImportEntry *mi = &m->import_entries[i];- JS_FreeAtom(ctx, mi->import_name);- }- js_free(ctx, m->import_entries);-- JS_FreeValue(ctx, m->module_ns);- JS_FreeValue(ctx, m->func_obj);- JS_FreeValue(ctx, m->eval_exception);- JS_FreeValue(ctx, m->meta_obj);- list_del(&m->link);- js_free(ctx, m);-}--static int add_req_module_entry(JSContext *ctx, JSModuleDef *m,- JSAtom module_name)-{- JSReqModuleEntry *rme;- int i;-- /* no need to add the module request if it is already present */- for(i = 0; i < m->req_module_entries_count; i++) {- rme = &m->req_module_entries[i];- if (rme->module_name == module_name)- return i;- }-- if (js_resize_array(ctx, (void **)&m->req_module_entries,- sizeof(JSReqModuleEntry),- &m->req_module_entries_size,- m->req_module_entries_count + 1))- return -1;- rme = &m->req_module_entries[m->req_module_entries_count++];- rme->module_name = JS_DupAtom(ctx, module_name);- rme->module = NULL;- return i;-}--static JSExportEntry *find_export_entry(JSContext *ctx, JSModuleDef *m,- JSAtom export_name)-{- JSExportEntry *me;- int i;- for(i = 0; i < m->export_entries_count; i++) {- me = &m->export_entries[i];- if (me->export_name == export_name)- return me;- }- return NULL;-}--static JSExportEntry *add_export_entry2(JSContext *ctx,- JSParseState *s, JSModuleDef *m,- JSAtom local_name, JSAtom export_name,- JSExportTypeEnum export_type)-{- JSExportEntry *me;-- if (find_export_entry(ctx, m, export_name)) {- char buf1[ATOM_GET_STR_BUF_SIZE];- if (s) {- js_parse_error(s, "duplicate exported name '%s'",- JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name));- } else {- JS_ThrowSyntaxErrorAtom(ctx, "duplicate exported name '%s'", export_name);- }- return NULL;- }-- if (js_resize_array(ctx, (void **)&m->export_entries,- sizeof(JSExportEntry),- &m->export_entries_size,- m->export_entries_count + 1))- return NULL;- me = &m->export_entries[m->export_entries_count++];- memset(me, 0, sizeof(*me));- me->local_name = JS_DupAtom(ctx, local_name);- me->export_name = JS_DupAtom(ctx, export_name);- me->export_type = export_type;- return me;-}--static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m,- JSAtom local_name, JSAtom export_name,- JSExportTypeEnum export_type)-{- return add_export_entry2(s->ctx, s, m, local_name, export_name,- export_type);-}--static int add_star_export_entry(JSContext *ctx, JSModuleDef *m,- int req_module_idx)-{- JSStarExportEntry *se;-- if (js_resize_array(ctx, (void **)&m->star_export_entries,- sizeof(JSStarExportEntry),- &m->star_export_entries_size,- m->star_export_entries_count + 1))- return -1;- se = &m->star_export_entries[m->star_export_entries_count++];- se->req_module_idx = req_module_idx;- return 0;-}--/* create a C module */-JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str,- JSModuleInitFunc *func)-{- JSModuleDef *m;- JSAtom name;- name = JS_NewAtom(ctx, name_str);- if (name == JS_ATOM_NULL)- return NULL;- m = js_new_module_def(ctx, name);- m->init_func = func;- return m;-}--int JS_AddModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name)-{- JSExportEntry *me;- JSAtom name;- name = JS_NewAtom(ctx, export_name);- if (name == JS_ATOM_NULL)- return -1;- me = add_export_entry2(ctx, NULL, m, JS_ATOM_NULL, name,- JS_EXPORT_TYPE_LOCAL);- JS_FreeAtom(ctx, name);- if (!me)- return -1;- else- return 0;-}--int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name,- JSValue val)-{- JSExportEntry *me;- JSAtom name;- name = JS_NewAtom(ctx, export_name);- if (name == JS_ATOM_NULL)- goto fail;- me = find_export_entry(ctx, m, name);- JS_FreeAtom(ctx, name);- if (!me)- goto fail;- set_value(ctx, me->u.local.var_ref->pvalue, val);- return 0;- fail:- JS_FreeValue(ctx, val);- return -1;-}--void JS_SetModuleLoaderFunc(JSRuntime *rt,- JSModuleNormalizeFunc *module_normalize,- JSModuleLoaderFunc *module_loader, void *opaque)-{- rt->module_normalize_func = module_normalize;- rt->module_loader_func = module_loader;- rt->module_loader_opaque = opaque;-}--/* default module filename normalizer */-static char *js_default_module_normalize_name(JSContext *ctx,- const char *base_name,- const char *name)-{- char *filename, *p;- const char *r;- int len;-- if (name[0] != '.') {- /* if no initial dot, the module name is not modified */- return js_strdup(ctx, name);- }-- p = strrchr(base_name, '/');- if (p)- len = p - base_name;- else- len = 0;-- filename = js_malloc(ctx, len + strlen(name) + 1 + 1);- if (!filename)- return NULL;- memcpy(filename, base_name, len);- filename[len] = '\0';-- /* we only normalize the leading '..' or '.' */- r = name;- for(;;) {- if (r[0] == '.' && r[1] == '/') {- r += 2;- } else if (r[0] == '.' && r[1] == '.' && r[2] == '/') {- /* remove the last path element of filename, except if "."- or ".." */- if (filename[0] == '\0')- break;- p = strrchr(filename, '/');- if (!p)- p = filename;- else- p++;- if (!strcmp(p, ".") || !strcmp(p, ".."))- break;- if (p > filename)- p--;- *p = '\0';- r += 3;- } else {- break;- }- }- if (filename[0] != '\0')- strcat(filename, "/");- strcat(filename, r);- // printf("normalize: %s %s -> %s\n", base_name, name, filename);- return filename;-}--static JSModuleDef *js_find_loaded_module(JSContext *ctx, JSAtom name)-{- struct list_head *el;- JSModuleDef *m;- - /* first look at the loaded modules */- list_for_each(el, &ctx->loaded_modules) {- m = list_entry(el, JSModuleDef, link);- if (m->module_name == name)- return m;- }- return NULL;-}--/* return NULL in case of exception (e.g. module could not be loaded) */-static JSModuleDef *js_host_resolve_imported_module(JSContext *ctx,- const char *base_cname,- const char *cname1)-{- JSRuntime *rt = ctx->rt;- JSModuleDef *m;- char *cname;- JSAtom module_name;-- if (!rt->module_normalize_func) {- cname = js_default_module_normalize_name(ctx, base_cname, cname1);- } else {- cname = rt->module_normalize_func(ctx, base_cname, cname1,- rt->module_loader_opaque);- }- if (!cname)- return NULL;-- module_name = JS_NewAtom(ctx, cname);- if (module_name == JS_ATOM_NULL) {- js_free(ctx, cname);- return NULL;- }-- /* first look at the loaded modules */- m = js_find_loaded_module(ctx, module_name);- if (m) {- js_free(ctx, cname);- JS_FreeAtom(ctx, module_name);- return m;- }-- JS_FreeAtom(ctx, module_name);-- /* load the module */- if (!rt->module_loader_func) {- /* XXX: use a syntax error ? */- JS_ThrowReferenceError(ctx, "could not load module '%s'",- cname);- js_free(ctx, cname);- return NULL;- }-- m = rt->module_loader_func(ctx, cname, rt->module_loader_opaque);- js_free(ctx, cname);- return m;-}--static JSModuleDef *js_host_resolve_imported_module_atom(JSContext *ctx,- JSAtom base_module_name,- JSAtom module_name1)-{- const char *base_cname, *cname;- JSModuleDef *m;- - base_cname = JS_AtomToCString(ctx, base_module_name);- if (!base_cname)- return NULL;- cname = JS_AtomToCString(ctx, module_name1);- if (!cname) {- JS_FreeCString(ctx, base_cname);- return NULL;- }- m = js_host_resolve_imported_module(ctx, base_cname, cname);- JS_FreeCString(ctx, base_cname);- JS_FreeCString(ctx, cname);- return m;-}--typedef struct JSResolveEntry {- JSModuleDef *module;- JSAtom name;-} JSResolveEntry;--typedef struct JSResolveState {- JSResolveEntry *array;- int size;- int count;-} JSResolveState;--static int find_resolve_entry(JSResolveState *s,- JSModuleDef *m, JSAtom name)-{- int i;- for(i = 0; i < s->count; i++) {- JSResolveEntry *re = &s->array[i];- if (re->module == m && re->name == name)- return i;- }- return -1;-}--static int add_resolve_entry(JSContext *ctx, JSResolveState *s,- JSModuleDef *m, JSAtom name)-{- JSResolveEntry *re;-- if (js_resize_array(ctx, (void **)&s->array,- sizeof(JSResolveEntry),- &s->size, s->count + 1))- return -1;- re = &s->array[s->count++];- re->module = m;- re->name = JS_DupAtom(ctx, name);- return 0;-}--typedef enum JSResolveResultEnum {- JS_RESOLVE_RES_EXCEPTION = -1, /* memory alloc error */- JS_RESOLVE_RES_FOUND = 0,- JS_RESOLVE_RES_NOT_FOUND,- JS_RESOLVE_RES_CIRCULAR,- JS_RESOLVE_RES_AMBIGUOUS,-} JSResolveResultEnum;--static JSResolveResultEnum js_resolve_export1(JSContext *ctx,- JSModuleDef **pmodule,- JSExportEntry **pme,- JSModuleDef *m,- JSAtom export_name,- JSResolveState *s)-{- JSExportEntry *me;-- *pmodule = NULL;- *pme = NULL;- if (find_resolve_entry(s, m, export_name) >= 0)- return JS_RESOLVE_RES_CIRCULAR;- if (add_resolve_entry(ctx, s, m, export_name) < 0)- return JS_RESOLVE_RES_EXCEPTION;- me = find_export_entry(ctx, m, export_name);- if (me) {- if (me->export_type == JS_EXPORT_TYPE_LOCAL) {- /* local export */- *pmodule = m;- *pme = me;- return JS_RESOLVE_RES_FOUND;- } else {- /* indirect export */- JSModuleDef *m1;- m1 = m->req_module_entries[me->u.req_module_idx].module;- if (me->local_name == JS_ATOM__star_) {- /* export ns from */- *pmodule = m;- *pme = me;- return JS_RESOLVE_RES_FOUND;- } else {- return js_resolve_export1(ctx, pmodule, pme, m1,- me->local_name, s);- }- }- } else {- if (export_name != JS_ATOM_default) {- /* not found in direct or indirect exports: try star exports */- int i;-- for(i = 0; i < m->star_export_entries_count; i++) {- JSStarExportEntry *se = &m->star_export_entries[i];- JSModuleDef *m1, *res_m;- JSExportEntry *res_me;- JSResolveResultEnum ret;-- m1 = m->req_module_entries[se->req_module_idx].module;- ret = js_resolve_export1(ctx, &res_m, &res_me, m1,- export_name, s);- if (ret == JS_RESOLVE_RES_AMBIGUOUS ||- ret == JS_RESOLVE_RES_EXCEPTION) {- return ret;- } else if (ret == JS_RESOLVE_RES_FOUND) {- if (*pme != NULL) {- if (*pmodule != res_m ||- res_me->local_name != (*pme)->local_name) {- *pmodule = NULL;- *pme = NULL;- return JS_RESOLVE_RES_AMBIGUOUS;- }- } else {- *pmodule = res_m;- *pme = res_me;- }- }- }- if (*pme != NULL)- return JS_RESOLVE_RES_FOUND;- }- return JS_RESOLVE_RES_NOT_FOUND;- }-}--/* If the return value is JS_RESOLVE_RES_FOUND, return the module- (*pmodule) and the corresponding local export entry- (*pme). Otherwise return (NULL, NULL) */-static JSResolveResultEnum js_resolve_export(JSContext *ctx,- JSModuleDef **pmodule,- JSExportEntry **pme,- JSModuleDef *m,- JSAtom export_name)-{- JSResolveState ss, *s = &ss;- int i;- JSResolveResultEnum ret;-- s->array = NULL;- s->size = 0;- s->count = 0;-- ret = js_resolve_export1(ctx, pmodule, pme, m, export_name, s);-- for(i = 0; i < s->count; i++)- JS_FreeAtom(ctx, s->array[i].name);- js_free(ctx, s->array);-- return ret;-}--static void js_resolve_export_throw_error(JSContext *ctx,- JSResolveResultEnum res,- JSModuleDef *m, JSAtom export_name)-{- char buf1[ATOM_GET_STR_BUF_SIZE];- char buf2[ATOM_GET_STR_BUF_SIZE];- switch(res) {- case JS_RESOLVE_RES_EXCEPTION:- break;- default:- case JS_RESOLVE_RES_NOT_FOUND:- JS_ThrowSyntaxError(ctx, "Could not find export '%s' in module '%s'",- JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),- JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));- break;- case JS_RESOLVE_RES_CIRCULAR:- JS_ThrowSyntaxError(ctx, "circular reference when looking for export '%s' in module '%s'",- JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),- JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));- break;- case JS_RESOLVE_RES_AMBIGUOUS:- JS_ThrowSyntaxError(ctx, "export '%s' in module '%s' is ambiguous",- JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),- JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));- break;- }-}---typedef enum {- EXPORTED_NAME_AMBIGUOUS,- EXPORTED_NAME_NORMAL,- EXPORTED_NAME_NS,-} ExportedNameEntryEnum;--typedef struct ExportedNameEntry {- JSAtom export_name;- ExportedNameEntryEnum export_type;- union {- JSExportEntry *me; /* using when the list is built */- JSVarRef *var_ref; /* EXPORTED_NAME_NORMAL */- JSModuleDef *module; /* for EXPORTED_NAME_NS */- } u;-} ExportedNameEntry;--typedef struct GetExportNamesState {- JSModuleDef **modules;- int modules_size;- int modules_count;-- ExportedNameEntry *exported_names;- int exported_names_size;- int exported_names_count;-} GetExportNamesState;--static int find_exported_name(GetExportNamesState *s, JSAtom name)-{- int i;- for(i = 0; i < s->exported_names_count; i++) {- if (s->exported_names[i].export_name == name)- return i;- }- return -1;-}--static __exception int get_exported_names(JSContext *ctx,- GetExportNamesState *s,- JSModuleDef *m, BOOL from_star)-{- ExportedNameEntry *en;- int i, j;-- /* check circular reference */- for(i = 0; i < s->modules_count; i++) {- if (s->modules[i] == m)- return 0;- }- if (js_resize_array(ctx, (void **)&s->modules, sizeof(s->modules[0]),- &s->modules_size, s->modules_count + 1))- return -1;- s->modules[s->modules_count++] = m;-- for(i = 0; i < m->export_entries_count; i++) {- JSExportEntry *me = &m->export_entries[i];- if (from_star && me->export_name == JS_ATOM_default)- continue;- j = find_exported_name(s, me->export_name);- if (j < 0) {- if (js_resize_array(ctx, (void **)&s->exported_names, sizeof(s->exported_names[0]),- &s->exported_names_size,- s->exported_names_count + 1))- return -1;- en = &s->exported_names[s->exported_names_count++];- en->export_name = me->export_name;- /* avoid a second lookup for simple module exports */- if (from_star || me->export_type != JS_EXPORT_TYPE_LOCAL)- en->u.me = NULL;- else- en->u.me = me;- } else {- en = &s->exported_names[j];- en->u.me = NULL;- }- }- for(i = 0; i < m->star_export_entries_count; i++) {- JSStarExportEntry *se = &m->star_export_entries[i];- JSModuleDef *m1;- m1 = m->req_module_entries[se->req_module_idx].module;- if (get_exported_names(ctx, s, m1, TRUE))- return -1;- }- return 0;-}--/* Unfortunately, the spec gives a different behavior from GetOwnProperty ! */-static int js_module_ns_has(JSContext *ctx, JSValueConst obj, JSAtom atom)-{- return (find_own_property1(JS_VALUE_GET_OBJ(obj), atom) != NULL);-}--static const JSClassExoticMethods js_module_ns_exotic_methods = {- .has_property = js_module_ns_has,-};--static int exported_names_cmp(const void *p1, const void *p2, void *opaque)-{- JSContext *ctx = opaque;- const ExportedNameEntry *me1 = p1;- const ExportedNameEntry *me2 = p2;- JSValue str1, str2;- int ret;-- /* XXX: should avoid allocation memory in atom comparison */- str1 = JS_AtomToString(ctx, me1->export_name);- str2 = JS_AtomToString(ctx, me2->export_name);- if (JS_IsException(str1) || JS_IsException(str2)) {- /* XXX: raise an error ? */- ret = 0;- } else {- ret = js_string_compare(ctx, JS_VALUE_GET_STRING(str1),- JS_VALUE_GET_STRING(str2));- }- JS_FreeValue(ctx, str1);- JS_FreeValue(ctx, str2);- return ret;-}--static JSValue js_get_module_ns(JSContext *ctx, JSModuleDef *m);--static int js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,- void *opaque)-{- JSModuleDef *m = opaque;- JSValue val, this_val = JS_MKPTR(JS_TAG_OBJECT, p);- - val = js_get_module_ns(ctx, m);- if (JS_IsException(val))- return -1;- if (JS_DefinePropertyValue(ctx, this_val, atom, val,- JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0)- return -1;- return 0;-}--static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m)-{- JSValue obj;- JSObject *p;- GetExportNamesState s_s, *s = &s_s;- int i, ret;- JSProperty *pr;-- obj = JS_NewObjectClass(ctx, JS_CLASS_MODULE_NS);- if (JS_IsException(obj))- return obj;- p = JS_VALUE_GET_OBJ(obj);-- memset(s, 0, sizeof(*s));- ret = get_exported_names(ctx, s, m, FALSE);- js_free(ctx, s->modules);- if (ret)- goto fail;-- /* Resolve the exported names. The ambiguous exports are removed */- for(i = 0; i < s->exported_names_count; i++) {- ExportedNameEntry *en = &s->exported_names[i];- JSResolveResultEnum res;- JSExportEntry *res_me;- JSModuleDef *res_m;-- if (en->u.me) {- res_me = en->u.me; /* fast case: no resolution needed */- res_m = m;- res = JS_RESOLVE_RES_FOUND;- } else {- res = js_resolve_export(ctx, &res_m, &res_me, m,- en->export_name);- }- if (res != JS_RESOLVE_RES_FOUND) {- if (res != JS_RESOLVE_RES_AMBIGUOUS) {- js_resolve_export_throw_error(ctx, res, m, en->export_name);- goto fail;- }- en->export_type = EXPORTED_NAME_AMBIGUOUS;- } else {- if (res_me->local_name == JS_ATOM__star_) {- en->export_type = EXPORTED_NAME_NS;- en->u.module = res_m->req_module_entries[res_me->u.req_module_idx].module;- } else {- en->export_type = EXPORTED_NAME_NORMAL;- if (res_me->u.local.var_ref) {- en->u.var_ref = res_me->u.local.var_ref;- } else {- JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj);- p1 = JS_VALUE_GET_OBJ(res_m->func_obj);- en->u.var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];- }- }- }- }-- /* sort the exported names */- rqsort(s->exported_names, s->exported_names_count,- sizeof(s->exported_names[0]), exported_names_cmp, ctx);-- for(i = 0; i < s->exported_names_count; i++) {- ExportedNameEntry *en = &s->exported_names[i];- switch(en->export_type) {- case EXPORTED_NAME_NORMAL:- {- JSVarRef *var_ref = en->u.var_ref;- pr = add_property(ctx, p, en->export_name,- JS_PROP_ENUMERABLE | JS_PROP_WRITABLE |- JS_PROP_VARREF);- if (!pr)- goto fail;- var_ref->header.ref_count++;- pr->u.var_ref = var_ref;- }- break;- case EXPORTED_NAME_NS:- /* the exported namespace must be created on demand */- if (JS_DefineAutoInitProperty(ctx, obj,- en->export_name,- JS_AUTOINIT_ID_MODULE_NS,- en->u.module, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0)- goto fail;- break;- default:- break;- }- }-- js_free(ctx, s->exported_names);-- JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_toStringTag,- JS_AtomToString(ctx, JS_ATOM_Module),- 0);-- p->extensible = FALSE;- return obj;- fail:- js_free(ctx, s->exported_names);- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--static JSValue js_get_module_ns(JSContext *ctx, JSModuleDef *m)-{- if (JS_IsUndefined(m->module_ns)) {- JSValue val;- val = js_build_module_ns(ctx, m);- if (JS_IsException(val))- return JS_EXCEPTION;- m->module_ns = val;- }- return JS_DupValue(ctx, m->module_ns);-}--/* Load all the required modules for module 'm' */-static int js_resolve_module(JSContext *ctx, JSModuleDef *m)-{- int i;- JSModuleDef *m1;-- if (m->resolved)- return 0;-#ifdef DUMP_MODULE_RESOLVE- {- char buf1[ATOM_GET_STR_BUF_SIZE];- printf("resolving module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));- }-#endif- m->resolved = TRUE;- /* resolve each requested module */- for(i = 0; i < m->req_module_entries_count; i++) {- JSReqModuleEntry *rme = &m->req_module_entries[i];- m1 = js_host_resolve_imported_module_atom(ctx, m->module_name,- rme->module_name);- if (!m1)- return -1;- rme->module = m1;- /* already done in js_host_resolve_imported_module() except if- the module was loaded with JS_EvalBinary() */- if (js_resolve_module(ctx, m1) < 0)- return -1;- }- return 0;-}--static JSVarRef *js_create_module_var(JSContext *ctx, BOOL is_lexical)-{- JSVarRef *var_ref;- var_ref = js_malloc(ctx, sizeof(JSVarRef));- if (!var_ref)- return NULL;- var_ref->header.ref_count = 1;- if (is_lexical)- var_ref->value = JS_UNINITIALIZED;- else- var_ref->value = JS_UNDEFINED;- var_ref->pvalue = &var_ref->value;- var_ref->is_detached = TRUE;- add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);- return var_ref;-}--/* Create the <eval> function associated with the module */-static int js_create_module_bytecode_function(JSContext *ctx, JSModuleDef *m)-{- JSFunctionBytecode *b;- int i;- JSVarRef **var_refs;- JSValue func_obj, bfunc;- JSObject *p;-- bfunc = m->func_obj;- func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,- JS_CLASS_BYTECODE_FUNCTION);-- if (JS_IsException(func_obj))- return -1;- b = JS_VALUE_GET_PTR(bfunc);-- p = JS_VALUE_GET_OBJ(func_obj);- p->u.func.function_bytecode = b;- b->header.ref_count++;- p->u.func.home_object = NULL;- p->u.func.var_refs = NULL;- if (b->closure_var_count) {- var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count);- if (!var_refs)- goto fail;- p->u.func.var_refs = var_refs;-- /* create the global variables. The other variables are- imported from other modules */- for(i = 0; i < b->closure_var_count; i++) {- JSClosureVar *cv = &b->closure_var[i];- JSVarRef *var_ref;- if (cv->is_local) {- var_ref = js_create_module_var(ctx, cv->is_lexical);- if (!var_ref)- goto fail;-#ifdef DUMP_MODULE_RESOLVE- printf("local %d: %p\n", i, var_ref);-#endif- var_refs[i] = var_ref;- }- }- }- m->func_obj = func_obj;- JS_FreeValue(ctx, bfunc);- return 0;- fail:- JS_FreeValue(ctx, func_obj);- return -1;-}--/* must be done before js_instantiate_module() because of cyclic references */-static int js_create_module_function(JSContext *ctx, JSModuleDef *m)-{- BOOL is_c_module;- int i;- JSVarRef *var_ref;- - if (m->func_created)- return 0;-- is_c_module = (m->init_func != NULL);-- if (is_c_module) {- /* initialize the exported variables */- for(i = 0; i < m->export_entries_count; i++) {- JSExportEntry *me = &m->export_entries[i];- if (me->export_type == JS_EXPORT_TYPE_LOCAL) {- var_ref = js_create_module_var(ctx, FALSE);- if (!var_ref)- return -1;- me->u.local.var_ref = var_ref;- }- }- } else {- if (js_create_module_bytecode_function(ctx, m))- return -1;- }- m->func_created = TRUE;-- /* do it on the dependencies */- - for(i = 0; i < m->req_module_entries_count; i++) {- JSReqModuleEntry *rme = &m->req_module_entries[i];- if (js_create_module_function(ctx, rme->module) < 0)- return -1;- }-- return 0;-} -- -/* Prepare a module to be executed by resolving all the imported- variables. */-static int js_instantiate_module(JSContext *ctx, JSModuleDef *m)-{- int i;- JSImportEntry *mi;- JSModuleDef *m1;- JSVarRef **var_refs, *var_ref;- JSObject *p;- BOOL is_c_module;-- if (m->instantiated)- return 0;- m->instantiated = TRUE;--#ifdef DUMP_MODULE_RESOLVE- {- char buf1[ATOM_GET_STR_BUF_SIZE];- printf("start instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));- }-#endif-- for(i = 0; i < m->req_module_entries_count; i++) {- JSReqModuleEntry *rme = &m->req_module_entries[i];- if (js_instantiate_module(ctx, rme->module) < 0)- goto fail;- }--#ifdef DUMP_MODULE_RESOLVE- {- char buf1[ATOM_GET_STR_BUF_SIZE];- printf("instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));- }-#endif- /* check the indirect exports */- for(i = 0; i < m->export_entries_count; i++) {- JSExportEntry *me = &m->export_entries[i];- if (me->export_type == JS_EXPORT_TYPE_INDIRECT &&- me->local_name != JS_ATOM__star_) {- JSResolveResultEnum ret;- JSExportEntry *res_me;- JSModuleDef *res_m, *m1;- m1 = m->req_module_entries[me->u.req_module_idx].module;- ret = js_resolve_export(ctx, &res_m, &res_me, m1, me->local_name);- if (ret != JS_RESOLVE_RES_FOUND) {- js_resolve_export_throw_error(ctx, ret, m, me->export_name);- goto fail;- }- }- }--#ifdef DUMP_MODULE_RESOLVE- {- printf("exported bindings:\n");- for(i = 0; i < m->export_entries_count; i++) {- JSExportEntry *me = &m->export_entries[i];- printf(" name="); print_atom(ctx, me->export_name);- printf(" local="); print_atom(ctx, me->local_name);- printf(" type=%d idx=%d\n", me->export_type, me->u.local.var_idx);- }- }-#endif-- is_c_module = (m->init_func != NULL);-- if (!is_c_module) {- p = JS_VALUE_GET_OBJ(m->func_obj);- var_refs = p->u.func.var_refs;-- for(i = 0; i < m->import_entries_count; i++) {- mi = &m->import_entries[i];-#ifdef DUMP_MODULE_RESOLVE- printf("import var_idx=%d name=", mi->var_idx);- print_atom(ctx, mi->import_name);- printf(": ");-#endif- m1 = m->req_module_entries[mi->req_module_idx].module;- if (mi->import_name == JS_ATOM__star_) {- JSValue val;- /* name space import */- val = js_get_module_ns(ctx, m1);- if (JS_IsException(val))- goto fail;- set_value(ctx, &var_refs[mi->var_idx]->value, val);-#ifdef DUMP_MODULE_RESOLVE- printf("namespace\n");-#endif- } else {- JSResolveResultEnum ret;- JSExportEntry *res_me;- JSModuleDef *res_m;- JSObject *p1;-- ret = js_resolve_export(ctx, &res_m,- &res_me, m1, mi->import_name);- if (ret != JS_RESOLVE_RES_FOUND) {- js_resolve_export_throw_error(ctx, ret, m1, mi->import_name);- goto fail;- }- if (res_me->local_name == JS_ATOM__star_) {- JSValue val;- JSModuleDef *m2;- /* name space import from */- m2 = res_m->req_module_entries[res_me->u.req_module_idx].module;- val = js_get_module_ns(ctx, m2);- if (JS_IsException(val))- goto fail;- var_ref = js_create_module_var(ctx, TRUE);- if (!var_ref) {- JS_FreeValue(ctx, val);- goto fail;- }- set_value(ctx, &var_ref->value, val);- var_refs[mi->var_idx] = var_ref;-#ifdef DUMP_MODULE_RESOLVE- printf("namespace from\n");-#endif- } else {- var_ref = res_me->u.local.var_ref;- if (!var_ref) {- p1 = JS_VALUE_GET_OBJ(res_m->func_obj);- var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];- }- var_ref->header.ref_count++;- var_refs[mi->var_idx] = var_ref;-#ifdef DUMP_MODULE_RESOLVE- printf("local export (var_ref=%p)\n", var_ref);-#endif- }- }- }-- /* keep the exported variables in the module export entries (they- are used when the eval function is deleted and cannot be- initialized before in case imports are exported) */- for(i = 0; i < m->export_entries_count; i++) {- JSExportEntry *me = &m->export_entries[i];- if (me->export_type == JS_EXPORT_TYPE_LOCAL) {- var_ref = var_refs[me->u.local.var_idx];- var_ref->header.ref_count++;- me->u.local.var_ref = var_ref;- }- }- }--#ifdef DUMP_MODULE_RESOLVE- printf("done instantiate\n");-#endif- return 0;- fail:- return -1;-}--/* return JS_ATOM_NULL if the name cannot be found. Only works with- not striped bytecode functions. */-JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels)-{- JSStackFrame *sf;- JSFunctionBytecode *b;- JSObject *p;- /* XXX: currently we just use the filename of the englobing- function. It does not work for eval(). Need to add a- ScriptOrModule info in JSFunctionBytecode */- sf = ctx->rt->current_stack_frame;- if (!sf)- return JS_ATOM_NULL;- while (n_stack_levels-- > 0) {- sf = sf->prev_frame;- if (!sf)- return JS_ATOM_NULL;- }- if (JS_VALUE_GET_TAG(sf->cur_func) != JS_TAG_OBJECT)- return JS_ATOM_NULL;- p = JS_VALUE_GET_OBJ(sf->cur_func);- if (!js_class_has_bytecode(p->class_id))- return JS_ATOM_NULL;- b = p->u.func.function_bytecode;- if (!b->has_debug)- return JS_ATOM_NULL;- return JS_DupAtom(ctx, b->debug.filename);-}--JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m)-{- return JS_DupAtom(ctx, m->module_name);-}--JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m)-{- JSValue obj;- /* allocate meta_obj only if requested to save memory */- obj = m->meta_obj;- if (JS_IsUndefined(obj)) {- obj = JS_NewObjectProto(ctx, JS_NULL);- if (JS_IsException(obj))- return JS_EXCEPTION;- m->meta_obj = obj;- }- return JS_DupValue(ctx, obj);-}--static JSValue js_import_meta(JSContext *ctx)-{- JSAtom filename;- JSModuleDef *m;- - filename = JS_GetScriptOrModuleName(ctx, 0);- if (filename == JS_ATOM_NULL)- goto fail;-- /* XXX: inefficient, need to add a module or script pointer in- JSFunctionBytecode */- m = js_find_loaded_module(ctx, filename);- JS_FreeAtom(ctx, filename);- if (!m) {- fail:- JS_ThrowTypeError(ctx, "import.meta not supported in this context");- return JS_EXCEPTION;- }- return JS_GetImportMeta(ctx, m);-}--/* used by os.Worker() and import() */-JSModuleDef *JS_RunModule(JSContext *ctx, const char *basename,- const char *filename)-{- JSModuleDef *m;- JSValue ret, func_obj;- - m = js_host_resolve_imported_module(ctx, basename, filename);- if (!m)- return NULL;- - if (js_resolve_module(ctx, m) < 0) {- js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED);- return NULL;- }-- /* Evaluate the module code */- func_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));- ret = JS_EvalFunction(ctx, func_obj);- if (JS_IsException(ret))- return NULL;- JS_FreeValue(ctx, ret);- return m;-}--static JSValue js_dynamic_import_job(JSContext *ctx,- int argc, JSValueConst *argv)-{- JSValueConst *resolving_funcs = argv;- JSValueConst basename_val = argv[2];- JSValueConst specifier = argv[3];- JSModuleDef *m;- const char *basename = NULL, *filename;- JSValue ret, err, ns;-- if (!JS_IsString(basename_val)) {- JS_ThrowTypeError(ctx, "no function filename for import()");- goto exception;- }- basename = JS_ToCString(ctx, basename_val);- if (!basename)- goto exception;-- filename = JS_ToCString(ctx, specifier);- if (!filename)- goto exception;- - m = JS_RunModule(ctx, basename, filename);- JS_FreeCString(ctx, filename);- if (!m)- goto exception;-- /* return the module namespace */- ns = js_get_module_ns(ctx, m);- if (JS_IsException(ns))- goto exception;-- ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED,- 1, (JSValueConst *)&ns);- JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */- JS_FreeValue(ctx, ns);- JS_FreeCString(ctx, basename);- return JS_UNDEFINED;- exception:-- err = JS_GetException(ctx);- ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,- 1, (JSValueConst *)&err);- JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */- JS_FreeValue(ctx, err);- JS_FreeCString(ctx, basename);- return JS_UNDEFINED;-}--static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier)-{- JSAtom basename;- JSValue promise, resolving_funcs[2], basename_val;- JSValueConst args[4];-- basename = JS_GetScriptOrModuleName(ctx, 0);- if (basename == JS_ATOM_NULL)- basename_val = JS_NULL;- else- basename_val = JS_AtomToValue(ctx, basename);- JS_FreeAtom(ctx, basename);- if (JS_IsException(basename_val))- return basename_val;- - promise = JS_NewPromiseCapability(ctx, resolving_funcs);- if (JS_IsException(promise)) {- JS_FreeValue(ctx, basename_val);- return promise;- }-- args[0] = resolving_funcs[0];- args[1] = resolving_funcs[1];- args[2] = basename_val;- args[3] = specifier;- - JS_EnqueueJob(ctx, js_dynamic_import_job, 4, args);-- JS_FreeValue(ctx, basename_val);- JS_FreeValue(ctx, resolving_funcs[0]);- JS_FreeValue(ctx, resolving_funcs[1]);- return promise;-}--/* Run the <eval> function of the module and of all its requested- modules. */-static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m)-{- JSModuleDef *m1;- int i;- JSValue ret_val;-- if (m->eval_mark)- return JS_UNDEFINED; /* avoid cycles */-- if (m->evaluated) {- /* if the module was already evaluated, rethrow the exception- it raised */- if (m->eval_has_exception) {- return JS_Throw(ctx, JS_DupValue(ctx, m->eval_exception));- } else {- return JS_UNDEFINED;- }- }-- m->eval_mark = TRUE;-- for(i = 0; i < m->req_module_entries_count; i++) {- JSReqModuleEntry *rme = &m->req_module_entries[i];- m1 = rme->module;- if (!m1->eval_mark) {- ret_val = js_evaluate_module(ctx, m1);- if (JS_IsException(ret_val)) {- m->eval_mark = FALSE;- return ret_val;- }- JS_FreeValue(ctx, ret_val);- }- }-- if (m->init_func) {- /* C module init */- if (m->init_func(ctx, m) < 0)- ret_val = JS_EXCEPTION;- else- ret_val = JS_UNDEFINED;- } else {- ret_val = JS_CallFree(ctx, m->func_obj, JS_UNDEFINED, 0, NULL);- m->func_obj = JS_UNDEFINED;- }- if (JS_IsException(ret_val)) {- /* save the thrown exception value */- m->eval_has_exception = TRUE;- m->eval_exception = JS_DupValue(ctx, ctx->rt->current_exception);- }- m->eval_mark = FALSE;- m->evaluated = TRUE;- return ret_val;-}--static __exception JSAtom js_parse_from_clause(JSParseState *s)-{- JSAtom module_name;- if (!token_is_pseudo_keyword(s, JS_ATOM_from)) {- js_parse_error(s, "from clause expected");- return JS_ATOM_NULL;- }- if (next_token(s))- return JS_ATOM_NULL;- if (s->token.val != TOK_STRING) {- js_parse_error(s, "string expected");- return JS_ATOM_NULL;- }- module_name = JS_ValueToAtom(s->ctx, s->token.u.str.str);- if (module_name == JS_ATOM_NULL)- return JS_ATOM_NULL;- if (next_token(s)) {- JS_FreeAtom(s->ctx, module_name);- return JS_ATOM_NULL;- }- return module_name;-}--static __exception int js_parse_export(JSParseState *s)-{- JSContext *ctx = s->ctx;- JSModuleDef *m = s->cur_func->module;- JSAtom local_name, export_name;- int first_export, idx, i, tok;- JSAtom module_name;- JSExportEntry *me;-- if (next_token(s))- return -1;-- tok = s->token.val;- if (tok == TOK_CLASS) {- return js_parse_class(s, FALSE, JS_PARSE_EXPORT_NAMED);- } else if (tok == TOK_FUNCTION ||- (token_is_pseudo_keyword(s, JS_ATOM_async) &&- peek_token(s, TRUE) == TOK_FUNCTION)) {- return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT,- JS_FUNC_NORMAL, JS_ATOM_NULL,- s->token.ptr, s->token.line_num,- JS_PARSE_EXPORT_NAMED, NULL);- }-- if (next_token(s))- return -1;-- switch(tok) {- case '{':- first_export = m->export_entries_count;- while (s->token.val != '}') {- if (!token_is_ident(s->token.val)) {- js_parse_error(s, "identifier expected");- return -1;- }- local_name = JS_DupAtom(ctx, s->token.u.ident.atom);- export_name = JS_ATOM_NULL;- if (next_token(s))- goto fail;- if (token_is_pseudo_keyword(s, JS_ATOM_as)) {- if (next_token(s))- goto fail;- if (!token_is_ident(s->token.val)) {- js_parse_error(s, "identifier expected");- goto fail;- }- export_name = JS_DupAtom(ctx, s->token.u.ident.atom);- if (next_token(s)) {- fail:- JS_FreeAtom(ctx, local_name);- fail1:- JS_FreeAtom(ctx, export_name);- return -1;- }- } else {- export_name = JS_DupAtom(ctx, local_name);- }- me = add_export_entry(s, m, local_name, export_name,- JS_EXPORT_TYPE_LOCAL);- JS_FreeAtom(ctx, local_name);- JS_FreeAtom(ctx, export_name);- if (!me)- return -1;- if (s->token.val != ',')- break;- if (next_token(s))- return -1;- }- if (js_parse_expect(s, '}'))- return -1;- if (token_is_pseudo_keyword(s, JS_ATOM_from)) {- module_name = js_parse_from_clause(s);- if (module_name == JS_ATOM_NULL)- return -1;- idx = add_req_module_entry(ctx, m, module_name);- JS_FreeAtom(ctx, module_name);- if (idx < 0)- return -1;- for(i = first_export; i < m->export_entries_count; i++) {- me = &m->export_entries[i];- me->export_type = JS_EXPORT_TYPE_INDIRECT;- me->u.req_module_idx = idx;- }- }- break;- case '*':- if (token_is_pseudo_keyword(s, JS_ATOM_as)) {- /* export ns from */- if (next_token(s))- return -1;- if (!token_is_ident(s->token.val)) {- js_parse_error(s, "identifier expected");- return -1;- }- export_name = JS_DupAtom(ctx, s->token.u.ident.atom);- if (next_token(s))- goto fail1;- module_name = js_parse_from_clause(s);- if (module_name == JS_ATOM_NULL)- goto fail1;- idx = add_req_module_entry(ctx, m, module_name);- JS_FreeAtom(ctx, module_name);- if (idx < 0)- goto fail1;- me = add_export_entry(s, m, JS_ATOM__star_, export_name,- JS_EXPORT_TYPE_INDIRECT);- JS_FreeAtom(ctx, export_name);- if (!me)- return -1;- me->u.req_module_idx = idx;- } else {- module_name = js_parse_from_clause(s);- if (module_name == JS_ATOM_NULL)- return -1;- idx = add_req_module_entry(ctx, m, module_name);- JS_FreeAtom(ctx, module_name);- if (idx < 0)- return -1;- if (add_star_export_entry(ctx, m, idx) < 0)- return -1;- }- break;- case TOK_DEFAULT:- if (s->token.val == TOK_CLASS) {- return js_parse_class(s, FALSE, JS_PARSE_EXPORT_DEFAULT);- } else if (s->token.val == TOK_FUNCTION ||- (token_is_pseudo_keyword(s, JS_ATOM_async) &&- peek_token(s, TRUE) == TOK_FUNCTION)) {- return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT,- JS_FUNC_NORMAL, JS_ATOM_NULL,- s->token.ptr, s->token.line_num,- JS_PARSE_EXPORT_DEFAULT, NULL);- } else {- if (js_parse_assign_expr(s, TRUE))- return -1;- }- /* set the name of anonymous functions */- set_object_name(s, JS_ATOM_default);-- /* store the value in the _default_ global variable and export- it */- local_name = JS_ATOM__default_;- if (define_var(s, s->cur_func, local_name, JS_VAR_DEF_LET) < 0)- return -1;- emit_op(s, OP_scope_put_var_init);- emit_atom(s, local_name);- emit_u16(s, 0);-- if (!add_export_entry(s, m, local_name, JS_ATOM_default,- JS_EXPORT_TYPE_LOCAL))- return -1;- break;- case TOK_VAR:- case TOK_LET:- case TOK_CONST:- return js_parse_var(s, TRUE, tok, TRUE);- default:- return js_parse_error(s, "invalid export syntax");- }- return js_parse_expect_semi(s);-}--static int add_closure_var(JSContext *ctx, JSFunctionDef *s,- BOOL is_local, BOOL is_arg,- int var_idx, JSAtom var_name,- BOOL is_const, BOOL is_lexical,- JSVarKindEnum var_kind);--static int add_import(JSParseState *s, JSModuleDef *m,- JSAtom local_name, JSAtom import_name)-{- JSContext *ctx = s->ctx;- int i, var_idx;- JSImportEntry *mi;- BOOL is_local;-- if (local_name == JS_ATOM_arguments || local_name == JS_ATOM_eval)- return js_parse_error(s, "invalid import binding");-- if (local_name != JS_ATOM_default) {- for (i = 0; i < s->cur_func->closure_var_count; i++) {- if (s->cur_func->closure_var[i].var_name == local_name)- return js_parse_error(s, "duplicate import binding");- }- }-- is_local = (import_name == JS_ATOM__star_);- var_idx = add_closure_var(ctx, s->cur_func, is_local, FALSE,- m->import_entries_count,- local_name, TRUE, TRUE, FALSE);- if (var_idx < 0)- return -1;- if (js_resize_array(ctx, (void **)&m->import_entries,- sizeof(JSImportEntry),- &m->import_entries_size,- m->import_entries_count + 1))- return -1;- mi = &m->import_entries[m->import_entries_count++];- mi->import_name = JS_DupAtom(ctx, import_name);- mi->var_idx = var_idx;- return 0;-}--static __exception int js_parse_import(JSParseState *s)-{- JSContext *ctx = s->ctx;- JSModuleDef *m = s->cur_func->module;- JSAtom local_name, import_name, module_name;- int first_import, i, idx;-- if (next_token(s))- return -1;-- first_import = m->import_entries_count;- if (s->token.val == TOK_STRING) {- module_name = JS_ValueToAtom(ctx, s->token.u.str.str);- if (module_name == JS_ATOM_NULL)- return -1;- if (next_token(s)) {- JS_FreeAtom(ctx, module_name);- return -1;- }- } else {- if (s->token.val == TOK_IDENT) {- if (s->token.u.ident.is_reserved) {- return js_parse_error_reserved_identifier(s);- }- /* "default" import */- local_name = JS_DupAtom(ctx, s->token.u.ident.atom);- import_name = JS_ATOM_default;- if (next_token(s))- goto fail;- if (add_import(s, m, local_name, import_name))- goto fail;- JS_FreeAtom(ctx, local_name);-- if (s->token.val != ',')- goto end_import_clause;- if (next_token(s))- return -1;- }-- if (s->token.val == '*') {- /* name space import */- if (next_token(s))- return -1;- if (!token_is_pseudo_keyword(s, JS_ATOM_as))- return js_parse_error(s, "expecting 'as'");- if (next_token(s))- return -1;- if (!token_is_ident(s->token.val)) {- js_parse_error(s, "identifier expected");- return -1;- }- local_name = JS_DupAtom(ctx, s->token.u.ident.atom);- import_name = JS_ATOM__star_;- if (next_token(s))- goto fail;- if (add_import(s, m, local_name, import_name))- goto fail;- JS_FreeAtom(ctx, local_name);- } else if (s->token.val == '{') {- if (next_token(s))- return -1;-- while (s->token.val != '}') {- if (!token_is_ident(s->token.val)) {- js_parse_error(s, "identifier expected");- return -1;- }- import_name = JS_DupAtom(ctx, s->token.u.ident.atom);- local_name = JS_ATOM_NULL;- if (next_token(s))- goto fail;- if (token_is_pseudo_keyword(s, JS_ATOM_as)) {- if (next_token(s))- goto fail;- if (!token_is_ident(s->token.val)) {- js_parse_error(s, "identifier expected");- goto fail;- }- local_name = JS_DupAtom(ctx, s->token.u.ident.atom);- if (next_token(s)) {- fail:- JS_FreeAtom(ctx, local_name);- JS_FreeAtom(ctx, import_name);- return -1;- }- } else {- local_name = JS_DupAtom(ctx, import_name);- }- if (add_import(s, m, local_name, import_name))- goto fail;- JS_FreeAtom(ctx, local_name);- JS_FreeAtom(ctx, import_name);- if (s->token.val != ',')- break;- if (next_token(s))- return -1;- }- if (js_parse_expect(s, '}'))- return -1;- }- end_import_clause:- module_name = js_parse_from_clause(s);- if (module_name == JS_ATOM_NULL)- return -1;- }- idx = add_req_module_entry(ctx, m, module_name);- JS_FreeAtom(ctx, module_name);- if (idx < 0)- return -1;- for(i = first_import; i < m->import_entries_count; i++)- m->import_entries[i].req_module_idx = idx;-- return js_parse_expect_semi(s);-}--static __exception int js_parse_source_element(JSParseState *s)-{- JSFunctionDef *fd = s->cur_func;- int tok;- - if (s->token.val == TOK_FUNCTION ||- (token_is_pseudo_keyword(s, JS_ATOM_async) &&- peek_token(s, TRUE) == TOK_FUNCTION)) {- if (js_parse_function_decl(s, JS_PARSE_FUNC_STATEMENT,- JS_FUNC_NORMAL, JS_ATOM_NULL,- s->token.ptr, s->token.line_num))- return -1;- } else if (s->token.val == TOK_EXPORT && fd->module) {- if (js_parse_export(s))- return -1;- } else if (s->token.val == TOK_IMPORT && fd->module &&- ((tok = peek_token(s, FALSE)) != '(' && tok != '.')) {- /* the peek_token is needed to avoid confusion with ImportCall- (dynamic import) or import.meta */- if (js_parse_import(s))- return -1;- } else {- if (js_parse_statement_or_decl(s, DECL_MASK_ALL))- return -1;- }- return 0;-}--static JSFunctionDef *js_new_function_def(JSContext *ctx,- JSFunctionDef *parent,- BOOL is_eval,- BOOL is_func_expr,- const char *filename, int line_num)-{- JSFunctionDef *fd;-- fd = js_mallocz(ctx, sizeof(*fd));- if (!fd)- return NULL;-- fd->ctx = ctx;- init_list_head(&fd->child_list);-- /* insert in parent list */- fd->parent = parent;- fd->parent_cpool_idx = -1;- if (parent) {- list_add_tail(&fd->link, &parent->child_list);- fd->js_mode = parent->js_mode;- fd->parent_scope_level = parent->scope_level;- }-- fd->is_eval = is_eval;- fd->is_func_expr = is_func_expr;- js_dbuf_init(ctx, &fd->byte_code);- fd->last_opcode_pos = -1;- fd->func_name = JS_ATOM_NULL;- fd->var_object_idx = -1;- fd->arguments_var_idx = -1;- fd->func_var_idx = -1;- fd->eval_ret_idx = -1;- fd->this_var_idx = -1;- fd->new_target_var_idx = -1;- fd->this_active_func_var_idx = -1;- fd->home_object_var_idx = -1;-- /* XXX: should distinguish arg, var and var object and body scopes */- fd->scope_level = 0; /* 0: var/arg scope, 1:body scope */- fd->scope_first = -1;- fd->scopes = fd->def_scope_array;- fd->scope_size = countof(fd->def_scope_array);- fd->scope_count = 1;- fd->scopes[0].first = -1;- fd->scopes[0].parent = -1;-- fd->filename = JS_NewAtom(ctx, filename);- fd->line_num = line_num;-- js_dbuf_init(ctx, &fd->pc2line);- //fd->pc2line_last_line_num = line_num;- //fd->pc2line_last_pc = 0;- fd->last_opcode_line_num = line_num;-- return fd;-}--static void free_bytecode_atoms(JSRuntime *rt,- const uint8_t *bc_buf, int bc_len,- BOOL use_short_opcodes)-{- int pos, len, op;- JSAtom atom;- const JSOpCode *oi;- - pos = 0;- while (pos < bc_len) {- op = bc_buf[pos];- if (use_short_opcodes)- oi = &short_opcode_info(op);- else- oi = &opcode_info[op];- - len = oi->size;- switch(oi->fmt) {- case OP_FMT_atom:- case OP_FMT_atom_u8:- case OP_FMT_atom_u16:- case OP_FMT_atom_label_u8:- case OP_FMT_atom_label_u16:- atom = get_u32(bc_buf + pos + 1);- JS_FreeAtomRT(rt, atom);- break;- default:- break;- }- pos += len;- }-}--static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd)-{- int i;- struct list_head *el, *el1;-- /* free the child functions */- list_for_each_safe(el, el1, &fd->child_list) {- JSFunctionDef *fd1;- fd1 = list_entry(el, JSFunctionDef, link);- js_free_function_def(ctx, fd1);- }-- free_bytecode_atoms(ctx->rt, fd->byte_code.buf, fd->byte_code.size,- fd->use_short_opcodes);- dbuf_free(&fd->byte_code);- js_free(ctx, fd->jump_slots);- js_free(ctx, fd->label_slots);- js_free(ctx, fd->line_number_slots);-- for(i = 0; i < fd->cpool_count; i++) {- JS_FreeValue(ctx, fd->cpool[i]);- }- js_free(ctx, fd->cpool);-- JS_FreeAtom(ctx, fd->func_name);-- for(i = 0; i < fd->var_count; i++) {- JS_FreeAtom(ctx, fd->vars[i].var_name);- }- js_free(ctx, fd->vars);- for(i = 0; i < fd->arg_count; i++) {- JS_FreeAtom(ctx, fd->args[i].var_name);- }- js_free(ctx, fd->args);-- for(i = 0; i < fd->hoisted_def_count; i++) {- JS_FreeAtom(ctx, fd->hoisted_def[i].var_name);- }- js_free(ctx, fd->hoisted_def);-- for(i = 0; i < fd->closure_var_count; i++) {- JSClosureVar *cv = &fd->closure_var[i];- JS_FreeAtom(ctx, cv->var_name);- }- js_free(ctx, fd->closure_var);-- if (fd->scopes != fd->def_scope_array)- js_free(ctx, fd->scopes);-- JS_FreeAtom(ctx, fd->filename);- dbuf_free(&fd->pc2line);-- js_free(ctx, fd->source);-- if (fd->parent) {- /* remove in parent list */- list_del(&fd->link);- }- js_free(ctx, fd);-}--#ifdef DUMP_BYTECODE-static const char *skip_lines(const char *p, int n) {- while (n-- > 0 && *p) {- while (*p && *p++ != '\n')- continue;- }- return p;-}--static void print_lines(const char *source, int line, int line1) {- const char *s = source;- const char *p = skip_lines(s, line);- if (*p) {- while (line++ < line1) {- p = skip_lines(s = p, 1);- printf(";; %.*s", (int)(p - s), s);- if (!*p) {- if (p[-1] != '\n')- printf("\n");- break;- }- }- }-}--static void dump_byte_code(JSContext *ctx, int pass,- const uint8_t *tab, int len,- const JSVarDef *args, int arg_count,- const JSVarDef *vars, int var_count,- const JSClosureVar *closure_var, int closure_var_count,- const JSValue *cpool, uint32_t cpool_count,- const char *source, int line_num,- const LabelSlot *label_slots, JSFunctionBytecode *b)-{- const JSOpCode *oi;- int pos, pos_next, op, size, idx, addr, line, line1, in_source;- uint8_t *bits = js_mallocz(ctx, len * sizeof(*bits));- BOOL use_short_opcodes = (b != NULL);-- /* scan for jump targets */- for (pos = 0; pos < len; pos = pos_next) {- op = tab[pos];- if (use_short_opcodes)- oi = &short_opcode_info(op);- else- oi = &opcode_info[op];- pos_next = pos + oi->size;- if (op < OP_COUNT) {- switch (oi->fmt) {-#if SHORT_OPCODES- case OP_FMT_label8:- pos++;- addr = (int8_t)tab[pos];- goto has_addr;- case OP_FMT_label16:- pos++;- addr = (int16_t)get_u16(tab + pos);- goto has_addr;-#endif- case OP_FMT_atom_label_u8:- case OP_FMT_atom_label_u16:- pos += 4;- /* fall thru */- case OP_FMT_label:- case OP_FMT_label_u16:- pos++;- addr = get_u32(tab + pos);- goto has_addr;- has_addr:- if (pass == 1)- addr = label_slots[addr].pos;- if (pass == 2)- addr = label_slots[addr].pos2;- if (pass == 3)- addr += pos;- if (addr >= 0 && addr < len)- bits[addr] |= 1;- break;- }- }- }- in_source = 0;- if (source) {- /* Always print first line: needed if single line */- print_lines(source, 0, 1);- in_source = 1;- }- line1 = line = 1;- pos = 0;- while (pos < len) {- op = tab[pos];- if (source) {- if (b) {- line1 = find_line_num(ctx, b, pos) - line_num + 1;- } else if (op == OP_line_num) {- line1 = get_u32(tab + pos + 1) - line_num + 1;- }- if (line1 > line) {- if (!in_source)- printf("\n");- in_source = 1;- print_lines(source, line, line1);- line = line1;- //bits[pos] |= 2;- }- }- if (in_source)- printf("\n");- in_source = 0;- if (op >= OP_COUNT) {- printf("invalid opcode (0x%02x)\n", op);- pos++;- continue;- }- if (use_short_opcodes)- oi = &short_opcode_info(op);- else- oi = &opcode_info[op];- size = oi->size;- if (pos + size > len) {- printf("truncated opcode (0x%02x)\n", op);- break;- }-#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 16)- {- int i, x, x0;- x = x0 = printf("%5d ", pos);- for (i = 0; i < size; i++) {- if (i == 6) {- printf("\n%*s", x = x0, "");- }- x += printf(" %02X", tab[pos + i]);- }- printf("%*s", x0 + 20 - x, "");- }-#endif- if (bits[pos]) {- printf("%5d: ", pos);- } else {- printf(" ");- }- printf("%s", oi->name);- pos++;- switch(oi->fmt) {- case OP_FMT_none_int:- printf(" %d", op - OP_push_0);- break;- case OP_FMT_npopx:- printf(" %d", op - OP_call0);- break;- case OP_FMT_u8:- printf(" %u", get_u8(tab + pos));- break;- case OP_FMT_i8:- printf(" %d", get_i8(tab + pos));- break;- case OP_FMT_u16:- case OP_FMT_npop:- printf(" %u", get_u16(tab + pos));- break;- case OP_FMT_npop_u16:- printf(" %u,%u", get_u16(tab + pos), get_u16(tab + pos + 2));- break;- case OP_FMT_i16:- printf(" %d", get_i16(tab + pos));- break;- case OP_FMT_i32:- printf(" %d", get_i32(tab + pos));- break;- case OP_FMT_u32:- printf(" %u", get_u32(tab + pos));- break;-#if SHORT_OPCODES- case OP_FMT_label8:- addr = get_i8(tab + pos);- goto has_addr1;- case OP_FMT_label16:- addr = get_i16(tab + pos);- goto has_addr1;-#endif- case OP_FMT_label:- addr = get_u32(tab + pos);- goto has_addr1;- has_addr1:- if (pass == 1)- printf(" %u:%u", addr, label_slots[addr].pos);- if (pass == 2)- printf(" %u:%u", addr, label_slots[addr].pos2);- if (pass == 3)- printf(" %u", addr + pos);- break;- case OP_FMT_label_u16:- addr = get_u32(tab + pos);- if (pass == 1)- printf(" %u:%u", addr, label_slots[addr].pos);- if (pass == 2)- printf(" %u:%u", addr, label_slots[addr].pos2);- if (pass == 3)- printf(" %u", addr + pos);- printf(",%u", get_u16(tab + pos + 4));- break;-#if SHORT_OPCODES- case OP_FMT_const8:- idx = get_u8(tab + pos);- goto has_pool_idx;-#endif- case OP_FMT_const:- idx = get_u32(tab + pos);- goto has_pool_idx;- has_pool_idx:- printf(" %u: ", idx);- if (idx < cpool_count) {- JS_DumpValue(ctx, cpool[idx]);- }- break;- case OP_FMT_atom:- printf(" ");- print_atom(ctx, get_u32(tab + pos));- break;- case OP_FMT_atom_u8:- printf(" ");- print_atom(ctx, get_u32(tab + pos));- printf(",%d", get_u8(tab + pos + 4));- break;- case OP_FMT_atom_u16:- printf(" ");- print_atom(ctx, get_u32(tab + pos));- printf(",%d", get_u16(tab + pos + 4));- break;- case OP_FMT_atom_label_u8:- case OP_FMT_atom_label_u16:- printf(" ");- print_atom(ctx, get_u32(tab + pos));- addr = get_u32(tab + pos + 4);- if (pass == 1)- printf(",%u:%u", addr, label_slots[addr].pos);- if (pass == 2)- printf(",%u:%u", addr, label_slots[addr].pos2);- if (pass == 3)- printf(",%u", addr + pos + 4);- if (oi->fmt == OP_FMT_atom_label_u8)- printf(",%u", get_u8(tab + pos + 8));- else- printf(",%u", get_u16(tab + pos + 8));- break;- case OP_FMT_none_loc:- idx = (op - OP_get_loc0) % 4;- goto has_loc;- case OP_FMT_loc8:- idx = get_u8(tab + pos);- goto has_loc;- case OP_FMT_loc:- idx = get_u16(tab + pos);- has_loc:- printf(" %d: ", idx);- if (idx < var_count) {- print_atom(ctx, vars[idx].var_name);- }- break;- case OP_FMT_none_arg:- idx = (op - OP_get_arg0) % 4;- goto has_arg;- case OP_FMT_arg:- idx = get_u16(tab + pos);- has_arg:- printf(" %d: ", idx);- if (idx < arg_count) {- print_atom(ctx, args[idx].var_name);- }- break;- case OP_FMT_none_var_ref:- idx = (op - OP_get_var_ref0) % 4;- goto has_var_ref;- case OP_FMT_var_ref:- idx = get_u16(tab + pos);- has_var_ref:- printf(" %d: ", idx);- if (idx < closure_var_count) {- print_atom(ctx, closure_var[idx].var_name);- }- break;- default:- break;- }- printf("\n");- pos += oi->size - 1;- }- if (source) {- if (!in_source)- printf("\n");- print_lines(source, line, INT32_MAX);- }- js_free(ctx, bits);-}--static __maybe_unused void dump_pc2line(JSContext *ctx, const uint8_t *buf, int len,- int line_num)-{- const uint8_t *p_end, *p_next, *p;- int pc, v;- unsigned int op;-- if (len <= 0)- return;-- printf("%5s %5s\n", "PC", "LINE");-- p = buf;- p_end = buf + len;- pc = 0;- while (p < p_end) {- op = *p++;- if (op == 0) {- v = unicode_from_utf8(p, p_end - p, &p_next);- if (v < 0)- goto fail;- pc += v;- p = p_next;- v = unicode_from_utf8(p, p_end - p, &p_next);- if (v < 0) {- fail:- printf("invalid pc2line encode pos=%d\n", (int)(p - buf));- return;- }- if (!(v & 1)) {- v = v >> 1;- } else {- v = -(v >> 1) - 1;- }- line_num += v;- p = p_next;- } else {- op -= PC2LINE_OP_FIRST;- pc += (op / PC2LINE_RANGE);- line_num += (op % PC2LINE_RANGE) + PC2LINE_BASE;- }- printf("%5d %5d\n", pc, line_num);- }-}--static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionBytecode *b)-{- int i;- char atom_buf[ATOM_GET_STR_BUF_SIZE];- const char *str;-- if (b->has_debug && b->debug.filename != JS_ATOM_NULL) {- str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->debug.filename);- printf("%s:%d: ", str, b->debug.line_num);- }-- str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->func_name);- printf("function: %s%s\n", &"*"[b->func_kind != JS_FUNC_GENERATOR], str);- if (b->js_mode) {- printf(" mode:");- if (b->js_mode & JS_MODE_STRICT)- printf(" strict");-#ifdef CONFIG_BIGNUM- if (b->js_mode & JS_MODE_MATH)- printf(" math");-#endif- printf("\n");- }- if (b->arg_count && b->vardefs) {- printf(" args:");- for(i = 0; i < b->arg_count; i++) {- printf(" %s", JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf),- b->vardefs[i].var_name));- }- printf("\n");- }- if (b->var_count && b->vardefs) {- printf(" locals:\n");- for(i = 0; i < b->var_count; i++) {- JSVarDef *vd = &b->vardefs[b->arg_count + i];- printf("%5d: %s %s", i,- vd->var_kind == JS_VAR_CATCH ? "catch" :- (vd->var_kind == JS_VAR_FUNCTION_DECL ||- vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) ? "function" :- vd->is_const ? "const" :- vd->is_lexical ? "let" : "var",- JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), vd->var_name));- if (vd->scope_level)- printf(" [level:%d next:%d]", vd->scope_level, vd->scope_next);- printf("\n");- }- }- if (b->closure_var_count) {- printf(" closure vars:\n");- for(i = 0; i < b->closure_var_count; i++) {- JSClosureVar *cv = &b->closure_var[i];- printf("%5d: %s %s:%s%d %s\n", i,- JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), cv->var_name),- cv->is_local ? "local" : "parent",- cv->is_arg ? "arg" : "loc", cv->var_idx,- cv->is_const ? "const" :- cv->is_lexical ? "let" : "var");- }- }- printf(" stack_size: %d\n", b->stack_size);- printf(" opcodes:\n");- dump_byte_code(ctx, 3, b->byte_code_buf, b->byte_code_len,- b->vardefs, b->arg_count,- b->vardefs ? b->vardefs + b->arg_count : NULL, b->var_count,- b->closure_var, b->closure_var_count,- b->cpool, b->cpool_count,- b->has_debug ? b->debug.source : NULL,- b->has_debug ? b->debug.line_num : -1, NULL, b);-#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 32)- if (b->has_debug)- dump_pc2line(ctx, b->debug.pc2line_buf, b->debug.pc2line_len, b->debug.line_num);-#endif- printf("\n");-}-#endif--static int add_closure_var(JSContext *ctx, JSFunctionDef *s,- BOOL is_local, BOOL is_arg,- int var_idx, JSAtom var_name,- BOOL is_const, BOOL is_lexical,- JSVarKindEnum var_kind)-{- JSClosureVar *cv;-- /* the closure variable indexes are currently stored on 16 bits */- if (s->closure_var_count >= JS_MAX_LOCAL_VARS) {- JS_ThrowInternalError(ctx, "too many closure variables");- return -1;- }-- if (js_resize_array(ctx, (void **)&s->closure_var,- sizeof(s->closure_var[0]),- &s->closure_var_size, s->closure_var_count + 1))- return -1;- cv = &s->closure_var[s->closure_var_count++];- cv->is_local = is_local;- cv->is_arg = is_arg;- cv->is_const = is_const;- cv->is_lexical = is_lexical;- cv->var_kind = var_kind;- cv->var_idx = var_idx;- cv->var_name = JS_DupAtom(ctx, var_name);- return s->closure_var_count - 1;-}--static int find_closure_var(JSContext *ctx, JSFunctionDef *s,- JSAtom var_name)-{- int i;- for(i = 0; i < s->closure_var_count; i++) {- JSClosureVar *cv = &s->closure_var[i];- if (cv->var_name == var_name)- return i;- }- return -1;-}--/* 'fd' must be a parent of 's'. Create in 's' a closure referencing a- local variable (is_local = TRUE) or a closure (is_local = FALSE) in- 'fd' */-static int get_closure_var2(JSContext *ctx, JSFunctionDef *s,- JSFunctionDef *fd, BOOL is_local,- BOOL is_arg, int var_idx, JSAtom var_name,- BOOL is_const, BOOL is_lexical,- JSVarKindEnum var_kind)-{- int i;-- if (fd != s->parent) {- var_idx = get_closure_var2(ctx, s->parent, fd, is_local,- is_arg, var_idx, var_name,- is_const, is_lexical, var_kind);- if (var_idx < 0)- return -1;- is_local = FALSE;- }- for(i = 0; i < s->closure_var_count; i++) {- JSClosureVar *cv = &s->closure_var[i];- if (cv->var_idx == var_idx && cv->is_arg == is_arg &&- cv->is_local == is_local)- return i;- }- return add_closure_var(ctx, s, is_local, is_arg, var_idx, var_name,- is_const, is_lexical, var_kind);-}--static int get_closure_var(JSContext *ctx, JSFunctionDef *s,- JSFunctionDef *fd, BOOL is_arg,- int var_idx, JSAtom var_name,- BOOL is_const, BOOL is_lexical,- JSVarKindEnum var_kind)-{- return get_closure_var2(ctx, s, fd, TRUE, is_arg,- var_idx, var_name, is_const, is_lexical,- var_kind);-}--static int get_with_scope_opcode(int op)-{- if (op == OP_scope_get_var_undef)- return OP_with_get_var;- else- return OP_with_get_var + (op - OP_scope_get_var);-}--static BOOL can_opt_put_ref_value(const uint8_t *bc_buf, int pos)-{- int opcode = bc_buf[pos];- return (bc_buf[pos + 1] == OP_put_ref_value &&- (opcode == OP_insert3 ||- opcode == OP_perm4 ||- opcode == OP_nop ||- opcode == OP_rot3l));-}--static BOOL can_opt_put_global_ref_value(const uint8_t *bc_buf, int pos)-{- int opcode = bc_buf[pos];- return (bc_buf[pos + 1] == OP_put_ref_value &&- (opcode == OP_insert3 ||- opcode == OP_perm4 ||- opcode == OP_nop ||- opcode == OP_rot3l));-}--static int optimize_scope_make_ref(JSContext *ctx, JSFunctionDef *s,- DynBuf *bc, uint8_t *bc_buf,- LabelSlot *ls, int pos_next,- int get_op, int var_idx)-{- int label_pos, end_pos, pos;-- /* XXX: should optimize `loc(a) += expr` as `expr add_loc(a)`- but only if expr does not modify `a`.- should scan the code between pos_next and label_pos- for operations that can potentially change `a`:- OP_scope_make_ref(a), function calls, jumps and gosub.- */- /* replace the reference get/put with normal variable- accesses */- if (bc_buf[pos_next] == OP_get_ref_value) {- dbuf_putc(bc, get_op);- dbuf_put_u16(bc, var_idx);- pos_next++;- }- /* remove the OP_label to make room for replacement */- /* label should have a refcount of 0 anyway */- /* XXX: should avoid this patch by inserting nops in phase 1 */- label_pos = ls->pos;- pos = label_pos - 5;- assert(bc_buf[pos] == OP_label);- /* label points to an instruction pair:- - insert3 / put_ref_value- - perm4 / put_ref_value- - rot3l / put_ref_value- - nop / put_ref_value- */- end_pos = label_pos + 2;- if (bc_buf[label_pos] == OP_insert3)- bc_buf[pos++] = OP_dup;- bc_buf[pos] = get_op + 1;- put_u16(bc_buf + pos + 1, var_idx);- pos += 3;- /* pad with OP_nop */- while (pos < end_pos)- bc_buf[pos++] = OP_nop;- return pos_next;-}--static int optimize_scope_make_global_ref(JSContext *ctx, JSFunctionDef *s,- DynBuf *bc, uint8_t *bc_buf,- LabelSlot *ls, int pos_next,- JSAtom var_name)-{- int label_pos, end_pos, pos, op;- BOOL is_strict;- is_strict = ((s->js_mode & JS_MODE_STRICT) != 0);-- /* replace the reference get/put with normal variable- accesses */- if (is_strict) {- /* need to check if the variable exists before evaluating the right- expression */- /* XXX: need an extra OP_true if destructuring an array */- dbuf_putc(bc, OP_check_var);- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));- } else {- /* XXX: need 2 extra OP_true if destructuring an array */- }- if (bc_buf[pos_next] == OP_get_ref_value) {- dbuf_putc(bc, OP_get_var);- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));- pos_next++;- }- /* remove the OP_label to make room for replacement */- /* label should have a refcount of 0 anyway */- /* XXX: should have emitted several OP_nop to avoid this kludge */- label_pos = ls->pos;- pos = label_pos - 5;- assert(bc_buf[pos] == OP_label);- end_pos = label_pos + 2;- op = bc_buf[label_pos];- if (is_strict) {- if (op != OP_nop) {- switch(op) {- case OP_insert3:- op = OP_insert2;- break;- case OP_perm4:- op = OP_perm3;- break;- case OP_rot3l:- op = OP_swap;- break;- default:- abort();- }- bc_buf[pos++] = op;- }- } else {- if (op == OP_insert3)- bc_buf[pos++] = OP_dup;- }- if (is_strict) {- bc_buf[pos] = OP_put_var_strict;- /* XXX: need 1 extra OP_drop if destructuring an array */- } else {- bc_buf[pos] = OP_put_var;- /* XXX: need 2 extra OP_drop if destructuring an array */- }- put_u32(bc_buf + pos + 1, JS_DupAtom(ctx, var_name));- pos += 5;- /* pad with OP_nop */- while (pos < end_pos)- bc_buf[pos++] = OP_nop;- return pos_next;-}--static int add_var_this(JSContext *ctx, JSFunctionDef *fd)-{- int idx;- idx = add_var(ctx, fd, JS_ATOM_this);- if (idx >= 0 && fd->is_derived_class_constructor) {- JSVarDef *vd = &fd->vars[idx];- /* XXX: should have is_this flag or var type */- vd->is_lexical = 1; /* used to trigger 'uninitialized' checks- in a derived class constructor */- }- return idx;-}--static int resolve_pseudo_var(JSContext *ctx, JSFunctionDef *s,- JSAtom var_name)-{- int var_idx;-- if (!s->has_this_binding)- return -1;- switch(var_name) {- case JS_ATOM_home_object:- /* 'home_object' pseudo variable */- var_idx = s->home_object_var_idx = add_var(ctx, s, var_name);- break;- case JS_ATOM_this_active_func:- /* 'this.active_func' pseudo variable */- var_idx = s->this_active_func_var_idx = add_var(ctx, s, var_name);- break;- case JS_ATOM_new_target:- /* 'new.target' pseudo variable */- var_idx = s->new_target_var_idx = add_var(ctx, s, var_name);- break;- case JS_ATOM_this:- /* 'this' pseudo variable */- var_idx = s->this_var_idx = add_var_this(ctx, s);- break;- default:- var_idx = -1;- break;- }- return var_idx;-}--/* return the position of the next opcode */-static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s,- JSAtom var_name, int scope_level, int op,- DynBuf *bc, uint8_t *bc_buf,- LabelSlot *ls, int pos_next, int arg_valid)-{- int idx, var_idx, is_put;- int label_done;- BOOL is_func_var = FALSE;- JSFunctionDef *fd;- JSVarDef *vd;- BOOL is_pseudo_var;-- label_done = -1;-- /* XXX: could be simpler to use a specific function to- resolve the pseudo variables */- is_pseudo_var = (var_name == JS_ATOM_home_object ||- var_name == JS_ATOM_this_active_func ||- var_name == JS_ATOM_new_target ||- var_name == JS_ATOM_this);-- /* resolve local scoped variables */- var_idx = -1;- for (idx = s->scopes[scope_level].first; idx >= 0;) {- vd = &s->vars[idx];- if (vd->var_name == var_name) {- if (op == OP_scope_put_var || op == OP_scope_make_ref) {- if (vd->is_const) {- dbuf_putc(bc, OP_throw_var);- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));- dbuf_putc(bc, JS_THROW_VAR_RO);- goto done;- }- is_func_var = vd->is_func_var;- }- var_idx = idx;- break;- } else- if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) {- dbuf_putc(bc, OP_get_loc);- dbuf_put_u16(bc, idx);- dbuf_putc(bc, get_with_scope_opcode(op));- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));- label_done = new_label_fd(s, label_done);- dbuf_put_u32(bc, label_done);- dbuf_putc(bc, 1);- update_label(s, label_done, 1);- s->jump_size++;- }- idx = vd->scope_next;- }- if (var_idx < 0) {- /* XXX: scoping issues:- should not resolve vars from the function body during argument parse,- `arguments` and function-name should not be hidden by later vars.- */- var_idx = find_var(ctx, s, var_name);- if (var_idx >= 0) {- if (scope_level == 0- && (var_idx & ARGUMENT_VAR_OFFSET)- && (var_idx - ARGUMENT_VAR_OFFSET) >= arg_valid) {- /* referring to an uninitialized argument */- dbuf_putc(bc, OP_throw_var);- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));- dbuf_putc(bc, JS_THROW_VAR_UNINITIALIZED);- }- if (!(var_idx & ARGUMENT_VAR_OFFSET))- is_func_var = s->vars[var_idx].is_func_var;- }-- if (var_idx < 0 && is_pseudo_var)- var_idx = resolve_pseudo_var(ctx, s, var_name);-- if (var_idx < 0 && var_name == JS_ATOM_arguments &&- s->has_arguments_binding) {- /* 'arguments' pseudo variable */- var_idx = add_arguments_var(ctx, s, var_name);- }- if (var_idx < 0 && s->is_func_expr && var_name == s->func_name) {- /* add a new variable with the function name */- var_idx = add_func_var(ctx, s, var_name);- is_func_var = TRUE;- }- }- if (var_idx >= 0) {- /* OP_scope_put_var_init is only used to initialize a- lexical variable, so it is never used in a with or var object. It- can be used with a closure (module global variable case). */- switch (op) {- case OP_scope_make_ref:- if (is_func_var) {- /* Create a dummy object reference for the func_var */- dbuf_putc(bc, OP_object);- dbuf_putc(bc, OP_get_loc);- dbuf_put_u16(bc, var_idx);- dbuf_putc(bc, OP_define_field);- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));- dbuf_putc(bc, OP_push_atom_value);- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));- } else- if (label_done == -1 && can_opt_put_ref_value(bc_buf, ls->pos)) {- int get_op;- if (var_idx & ARGUMENT_VAR_OFFSET) {- get_op = OP_get_arg;- var_idx -= ARGUMENT_VAR_OFFSET;- } else {- if (s->vars[var_idx].is_lexical)- get_op = OP_get_loc_check;- else- get_op = OP_get_loc;- }- pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls,- pos_next, get_op, var_idx);- } else {- /* Create a dummy object with a named slot that is- a reference to the local variable */- if (var_idx & ARGUMENT_VAR_OFFSET) {- dbuf_putc(bc, OP_make_arg_ref);- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));- dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET);- } else {- dbuf_putc(bc, OP_make_loc_ref);- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));- dbuf_put_u16(bc, var_idx);- }- }- break;- case OP_scope_get_ref:- dbuf_putc(bc, OP_undefined);- /* fall thru */- case OP_scope_get_var_undef:- case OP_scope_get_var:- case OP_scope_put_var:- case OP_scope_put_var_init:- is_put = (op == OP_scope_put_var || op == OP_scope_put_var_init);- if (var_idx & ARGUMENT_VAR_OFFSET) {- dbuf_putc(bc, OP_get_arg + is_put);- dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET);- /* XXX: should test if argument reference needs TDZ check */- } else {- if (is_put) {- if (s->vars[var_idx].is_lexical) {- if (op == OP_scope_put_var_init) {- /* 'this' can only be initialized once */- if (var_name == JS_ATOM_this)- dbuf_putc(bc, OP_put_loc_check_init);- else- dbuf_putc(bc, OP_put_loc);- } else {- dbuf_putc(bc, OP_put_loc_check);- }- } else {- dbuf_putc(bc, OP_put_loc);- }- } else {- if (s->vars[var_idx].is_lexical) {- dbuf_putc(bc, OP_get_loc_check);- } else {- dbuf_putc(bc, OP_get_loc);- }- }- dbuf_put_u16(bc, var_idx);- }- break;- case OP_scope_delete_var:- dbuf_putc(bc, OP_push_false);- break;- }- goto done;- }- /* check eval object */- if (s->var_object_idx >= 0 && !is_pseudo_var) {- dbuf_putc(bc, OP_get_loc);- dbuf_put_u16(bc, s->var_object_idx);- dbuf_putc(bc, get_with_scope_opcode(op));- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));- label_done = new_label_fd(s, label_done);- dbuf_put_u32(bc, label_done);- dbuf_putc(bc, 0);- update_label(s, label_done, 1);- s->jump_size++;- }- /* check parent scopes */- for (fd = s; fd->parent;) {- scope_level = fd->parent_scope_level;- fd = fd->parent;- if (scope_level == 0) {- /* function is defined as part of the argument parsing: hide vars- from the function body.- XXX: variables created from argument destructuring might need- to be visible, should refine this method.- */- var_idx = find_arg(ctx, fd, var_name);- goto check_idx;- }- for (idx = fd->scopes[scope_level].first; idx >= 0;) {- vd = &fd->vars[idx];- if (vd->var_name == var_name) {- if (op == OP_scope_put_var || op == OP_scope_make_ref) {- if (vd->is_const) {- dbuf_putc(bc, OP_throw_var);- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));- dbuf_putc(bc, JS_THROW_VAR_RO);- goto done;- }- is_func_var = vd->is_func_var;- }- var_idx = idx;- break;- } else if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) {- vd->is_captured = 1;- idx = get_closure_var(ctx, s, fd, FALSE, idx, vd->var_name, FALSE, FALSE, JS_VAR_NORMAL);- if (idx >= 0) {- dbuf_putc(bc, OP_get_var_ref);- dbuf_put_u16(bc, idx);- dbuf_putc(bc, get_with_scope_opcode(op));- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));- label_done = new_label_fd(s, label_done);- dbuf_put_u32(bc, label_done);- dbuf_putc(bc, 1);- update_label(s, label_done, 1);- s->jump_size++;- }- }- idx = vd->scope_next;- }- if (var_idx >= 0)- break;-- var_idx = find_var(ctx, fd, var_name);- check_idx:- if (var_idx >= 0) {- if (!(var_idx & ARGUMENT_VAR_OFFSET))- is_func_var = fd->vars[var_idx].is_func_var;- break;- }- if (is_pseudo_var) {- var_idx = resolve_pseudo_var(ctx, fd, var_name);- if (var_idx >= 0)- break;- }- if (var_name == JS_ATOM_arguments && fd->has_arguments_binding) {- var_idx = add_arguments_var(ctx, fd, var_name);- break;- }- if (fd->is_func_expr && fd->func_name == var_name) {- /* add a new variable with the function name */- var_idx = add_func_var(ctx, fd, var_name);- is_func_var = TRUE;- break;- }-- /* check eval object */- if (fd->var_object_idx >= 0 && !is_pseudo_var) {- fd->vars[fd->var_object_idx].is_captured = 1;- idx = get_closure_var(ctx, s, fd, FALSE,- fd->var_object_idx, JS_ATOM__var_,- FALSE, FALSE, JS_VAR_NORMAL);- dbuf_putc(bc, OP_get_var_ref);- dbuf_put_u16(bc, idx);- dbuf_putc(bc, get_with_scope_opcode(op));- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));- label_done = new_label_fd(s, label_done);- dbuf_put_u32(bc, label_done);- dbuf_putc(bc, 0);- update_label(s, label_done, 1);- s->jump_size++;- }-- if (fd->is_eval)- break; /* it it necessarily the top level function */- }-- /* check direct eval scope (in the closure of the eval function- which is necessarily at the top level) */- if (!fd)- fd = s;- if (var_idx < 0 && fd->is_eval) {- int idx1;- for (idx1 = 0; idx1 < fd->closure_var_count; idx1++) {- JSClosureVar *cv = &fd->closure_var[idx1];- if (var_name == cv->var_name) {- if (fd != s) {- idx = get_closure_var2(ctx, s, fd,- FALSE,- cv->is_arg, idx1,- cv->var_name, cv->is_const,- cv->is_lexical, cv->var_kind);- } else {- idx = idx1;- }- goto has_idx;- } else if ((cv->var_name == JS_ATOM__var_ ||- cv->var_name == JS_ATOM__with_) && !is_pseudo_var) {- int is_with = (cv->var_name == JS_ATOM__with_);- if (fd != s) {- idx = get_closure_var2(ctx, s, fd,- FALSE,- cv->is_arg, idx1,- cv->var_name, FALSE, FALSE,- JS_VAR_NORMAL);- } else {- idx = idx1;- }- dbuf_putc(bc, OP_get_var_ref);- dbuf_put_u16(bc, idx);- dbuf_putc(bc, get_with_scope_opcode(op));- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));- label_done = new_label_fd(s, label_done);- dbuf_put_u32(bc, label_done);- dbuf_putc(bc, is_with);- update_label(s, label_done, 1);- s->jump_size++;- }- }- }-- if (var_idx >= 0) {- /* find the corresponding closure variable */- if (var_idx & ARGUMENT_VAR_OFFSET) {- fd->args[var_idx - ARGUMENT_VAR_OFFSET].is_captured = 1;- idx = get_closure_var(ctx, s, fd,- TRUE, var_idx - ARGUMENT_VAR_OFFSET,- var_name, FALSE, FALSE, JS_VAR_NORMAL);- } else {- fd->vars[var_idx].is_captured = 1;- idx = get_closure_var(ctx, s, fd,- FALSE, var_idx,- var_name,- fd->vars[var_idx].is_const,- fd->vars[var_idx].is_lexical,- fd->vars[var_idx].var_kind);- }- if (idx >= 0) {- has_idx:- if ((op == OP_scope_put_var || op == OP_scope_make_ref) &&- s->closure_var[idx].is_const) {- dbuf_putc(bc, OP_throw_var);- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));- dbuf_putc(bc, JS_THROW_VAR_RO);- goto done;- }- switch (op) {- case OP_scope_make_ref:- if (is_func_var) {- /* Create a dummy object reference for the func_var */- dbuf_putc(bc, OP_object);- dbuf_putc(bc, OP_get_var_ref);- dbuf_put_u16(bc, idx);- dbuf_putc(bc, OP_define_field);- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));- dbuf_putc(bc, OP_push_atom_value);- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));- } else- if (label_done == -1 &&- can_opt_put_ref_value(bc_buf, ls->pos)) {- int get_op;- if (s->closure_var[idx].is_lexical)- get_op = OP_get_var_ref_check;- else- get_op = OP_get_var_ref;- pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls,- pos_next,- get_op, idx);- } else {- /* Create a dummy object with a named slot that is- a reference to the closure variable */- dbuf_putc(bc, OP_make_var_ref_ref);- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));- dbuf_put_u16(bc, idx);- }- break;- case OP_scope_get_ref:- /* XXX: should create a dummy object with a named slot that is- a reference to the closure variable */- dbuf_putc(bc, OP_undefined);- /* fall thru */- case OP_scope_get_var_undef:- case OP_scope_get_var:- case OP_scope_put_var:- case OP_scope_put_var_init:- is_put = (op == OP_scope_put_var ||- op == OP_scope_put_var_init);- if (is_put) {- if (s->closure_var[idx].is_lexical) {- if (op == OP_scope_put_var_init) {- /* 'this' can only be initialized once */- if (var_name == JS_ATOM_this)- dbuf_putc(bc, OP_put_var_ref_check_init);- else- dbuf_putc(bc, OP_put_var_ref);- } else {- dbuf_putc(bc, OP_put_var_ref_check);- }- } else {- dbuf_putc(bc, OP_put_var_ref);- }- } else {- if (s->closure_var[idx].is_lexical) {- dbuf_putc(bc, OP_get_var_ref_check);- } else {- dbuf_putc(bc, OP_get_var_ref);- }- }- dbuf_put_u16(bc, idx);- break;- case OP_scope_delete_var:- dbuf_putc(bc, OP_push_false);- break;- }- goto done;- }- }-- /* global variable access */-- switch (op) {- case OP_scope_make_ref:- if (label_done == -1 && can_opt_put_global_ref_value(bc_buf, ls->pos)) {- pos_next = optimize_scope_make_global_ref(ctx, s, bc, bc_buf, ls,- pos_next, var_name);- } else {- dbuf_putc(bc, OP_make_var_ref);- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));- }- break;- case OP_scope_get_ref:- /* XXX: should create a dummy object with a named slot that is- a reference to the global variable */- dbuf_putc(bc, OP_undefined);- dbuf_putc(bc, OP_get_var);- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));- break;- case OP_scope_get_var_undef:- case OP_scope_get_var:- case OP_scope_put_var:- dbuf_putc(bc, OP_get_var_undef + (op - OP_scope_get_var_undef));- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));- break;- case OP_scope_put_var_init:- dbuf_putc(bc, OP_put_var_init);- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));- break;- case OP_scope_delete_var:- dbuf_putc(bc, OP_delete_var);- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));- break;- }-done:- if (label_done >= 0) {- dbuf_putc(bc, OP_label);- dbuf_put_u32(bc, label_done);- s->label_slots[label_done].pos2 = bc->size;- }- return pos_next;-}--/* search in all scopes */-static int find_private_class_field_all(JSContext *ctx, JSFunctionDef *fd,- JSAtom name, int scope_level)-{- int idx;-- idx = fd->scopes[scope_level].first;- while (idx >= 0) {- if (fd->vars[idx].var_name == name)- return idx;- idx = fd->vars[idx].scope_next;- }- return -1;-}--static void get_loc_or_ref(DynBuf *bc, BOOL is_ref, int idx)-{- /* if the field is not initialized, the error is catched when- accessing it */- if (is_ref) - dbuf_putc(bc, OP_get_var_ref);- else- dbuf_putc(bc, OP_get_loc);- dbuf_put_u16(bc, idx);-}--static int resolve_scope_private_field1(JSContext *ctx,- BOOL *pis_ref, int *pvar_kind,- JSFunctionDef *s,- JSAtom var_name, int scope_level)-{- int idx, var_kind;- JSFunctionDef *fd;- BOOL is_ref;- - fd = s;- is_ref = FALSE;- for(;;) {- idx = find_private_class_field_all(ctx, fd, var_name, scope_level);- if (idx >= 0) {- var_kind = fd->vars[idx].var_kind;- if (is_ref) {- idx = get_closure_var(ctx, s, fd, FALSE, idx, var_name,- TRUE, TRUE, JS_VAR_NORMAL);- if (idx < 0)- return -1;- }- break;- }- scope_level = fd->parent_scope_level;- if (!fd->parent) {- if (fd->is_eval) {- /* closure of the eval function (top level) */- for (idx = 0; idx < fd->closure_var_count; idx++) {- JSClosureVar *cv = &fd->closure_var[idx];- if (cv->var_name == var_name) {- var_kind = cv->var_kind;- is_ref = TRUE;- if (fd != s) {- idx = get_closure_var2(ctx, s, fd,- FALSE,- cv->is_arg, idx,- cv->var_name, cv->is_const,- cv->is_lexical,- cv->var_kind);- if (idx < 0)- return -1;- }- goto done;- }- }- }- /* XXX: no line number info */- JS_ThrowSyntaxErrorAtom(ctx, "undefined private field '%s'",- var_name);- return -1;- } else {- fd = fd->parent;- }- is_ref = TRUE;- }- done:- *pis_ref = is_ref;- *pvar_kind = var_kind;- return idx;-}--/* return 0 if OK or -1 if the private field could not be resolved */-static int resolve_scope_private_field(JSContext *ctx, JSFunctionDef *s,- JSAtom var_name, int scope_level, int op,- DynBuf *bc)-{- int idx, var_kind;- BOOL is_ref;-- idx = resolve_scope_private_field1(ctx, &is_ref, &var_kind, s,- var_name, scope_level);- if (idx < 0)- return -1;- assert(var_kind != JS_VAR_NORMAL);- switch (op) {- case OP_scope_get_private_field:- case OP_scope_get_private_field2:- switch(var_kind) {- case JS_VAR_PRIVATE_FIELD:- if (op == OP_scope_get_private_field2)- dbuf_putc(bc, OP_dup);- get_loc_or_ref(bc, is_ref, idx);- dbuf_putc(bc, OP_get_private_field);- break;- case JS_VAR_PRIVATE_METHOD:- get_loc_or_ref(bc, is_ref, idx);- dbuf_putc(bc, OP_check_brand);- if (op != OP_scope_get_private_field2)- dbuf_putc(bc, OP_nip);- break;- case JS_VAR_PRIVATE_GETTER:- case JS_VAR_PRIVATE_GETTER_SETTER:- if (op == OP_scope_get_private_field2)- dbuf_putc(bc, OP_dup);- get_loc_or_ref(bc, is_ref, idx);- dbuf_putc(bc, OP_check_brand);- dbuf_putc(bc, OP_call_method);- dbuf_put_u16(bc, 0);- break;- case JS_VAR_PRIVATE_SETTER:- /* XXX: add clearer error message */- dbuf_putc(bc, OP_throw_var);- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));- dbuf_putc(bc, JS_THROW_VAR_RO);- break;- default:- abort();- }- break;- case OP_scope_put_private_field:- switch(var_kind) {- case JS_VAR_PRIVATE_FIELD:- get_loc_or_ref(bc, is_ref, idx);- dbuf_putc(bc, OP_put_private_field);- break;- case JS_VAR_PRIVATE_METHOD:- case JS_VAR_PRIVATE_GETTER:- /* XXX: add clearer error message */- dbuf_putc(bc, OP_throw_var);- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));- dbuf_putc(bc, JS_THROW_VAR_RO);- break;- case JS_VAR_PRIVATE_SETTER:- case JS_VAR_PRIVATE_GETTER_SETTER:- {- JSAtom setter_name = get_private_setter_name(ctx, var_name);- if (setter_name == JS_ATOM_NULL)- return -1;- idx = resolve_scope_private_field1(ctx, &is_ref,- &var_kind, s,- setter_name, scope_level);- JS_FreeAtom(ctx, setter_name);- if (idx < 0)- return -1;- assert(var_kind == JS_VAR_PRIVATE_SETTER);- get_loc_or_ref(bc, is_ref, idx);- dbuf_putc(bc, OP_swap);- /* obj func value */- dbuf_putc(bc, OP_rot3r);- /* value obj func */- dbuf_putc(bc, OP_check_brand);- dbuf_putc(bc, OP_rot3l);- /* obj func value */- dbuf_putc(bc, OP_call_method);- dbuf_put_u16(bc, 1);- }- break;- default:- abort();- }- break;- default:- abort();- }- return 0;-}--static void mark_eval_captured_variables(JSContext *ctx, JSFunctionDef *s,- int scope_level)-{- int idx;- JSVarDef *vd;-- for (idx = s->scopes[scope_level].first; idx >= 0;) {- vd = &s->vars[idx];- vd->is_captured = 1;- idx = vd->scope_next;- }-}--static void add_eval_variables(JSContext *ctx, JSFunctionDef *s)-{- JSFunctionDef *fd;- JSVarDef *vd;- int i, scope_level, scope_idx;- BOOL has_arguments_binding, has_this_binding;-- /* in non strict mode, variables are created in the caller's- environment object */- if (!s->is_eval && !(s->js_mode & JS_MODE_STRICT)) {- s->var_object_idx = add_var(ctx, s, JS_ATOM__var_);- }-- /* eval can potentially use 'arguments' so we must define it */- has_this_binding = s->has_this_binding;- if (has_this_binding) {- if (s->this_var_idx < 0)- s->this_var_idx = add_var_this(ctx, s);- if (s->new_target_var_idx < 0)- s->new_target_var_idx = add_var(ctx, s, JS_ATOM_new_target);- if (s->is_derived_class_constructor && s->this_active_func_var_idx < 0)- s->this_active_func_var_idx = add_var(ctx, s, JS_ATOM_this_active_func);- if (s->has_home_object && s->home_object_var_idx < 0)- s->home_object_var_idx = add_var(ctx, s, JS_ATOM_home_object);- }- has_arguments_binding = s->has_arguments_binding;- if (has_arguments_binding)- add_arguments_var(ctx, s, JS_ATOM_arguments);- if (s->is_func_expr && s->func_name != JS_ATOM_NULL)- add_func_var(ctx, s, s->func_name);-- /* eval can use all the variables of the enclosing functions, so- they must be all put in the closure. The closure variables are- ordered by scope. It works only because no closure are created- before. */- assert(s->is_eval || s->closure_var_count == 0);-- /* XXX: inefficient, but eval performance is less critical */- fd = s;- for(;;) {- scope_level = fd->parent_scope_level;- fd = fd->parent;- if (!fd)- break;- scope_idx = fd->scopes[scope_level].first;- /* add 'this' if it was not previously added */- if (!has_this_binding && fd->has_this_binding) {- if (fd->this_var_idx < 0)- fd->this_var_idx = add_var_this(ctx, fd);- if (fd->new_target_var_idx < 0)- fd->new_target_var_idx = add_var(ctx, fd, JS_ATOM_new_target);- if (fd->is_derived_class_constructor && fd->this_active_func_var_idx < 0)- fd->this_active_func_var_idx = add_var(ctx, fd, JS_ATOM_this_active_func);- if (fd->has_home_object && fd->home_object_var_idx < 0)- fd->home_object_var_idx = add_var(ctx, fd, JS_ATOM_home_object);- has_this_binding = TRUE;- }- /* add 'arguments' if it was not previously added */- if (!has_arguments_binding && fd->has_arguments_binding) {- add_arguments_var(ctx, fd, JS_ATOM_arguments);- has_arguments_binding = TRUE;- }- /* add function name */- if (fd->is_func_expr && fd->func_name != JS_ATOM_NULL)- add_func_var(ctx, fd, fd->func_name);-- /* add lexical variables */- while (scope_idx >= 0) {- vd = &fd->vars[scope_idx];- vd->is_captured = 1;- get_closure_var(ctx, s, fd, FALSE, scope_idx,- vd->var_name, vd->is_const, vd->is_lexical, vd->var_kind);- scope_idx = vd->scope_next;- }- /* add unscoped variables */- for(i = 0; i < fd->arg_count; i++) {- vd = &fd->args[i];- if (vd->var_name != JS_ATOM_NULL) {- get_closure_var(ctx, s, fd,- TRUE, i, vd->var_name, FALSE, FALSE,- JS_VAR_NORMAL);- }- }- for(i = 0; i < fd->var_count; i++) {- vd = &fd->vars[i];- /* do not close top level last result */- if (vd->scope_level == 0 &&- vd->var_name != JS_ATOM__ret_ &&- vd->var_name != JS_ATOM_NULL) {- get_closure_var(ctx, s, fd,- FALSE, i, vd->var_name, FALSE, FALSE,- JS_VAR_NORMAL);- }- }- if (fd->is_eval) {- int idx;- /* add direct eval variables (we are necessarily at the- top level) */- for (idx = 0; idx < fd->closure_var_count; idx++) {- JSClosureVar *cv = &fd->closure_var[idx];- get_closure_var2(ctx, s, fd,- FALSE, cv->is_arg,- idx, cv->var_name, cv->is_const,- cv->is_lexical, cv->var_kind);- }- }- }-}--/* for direct eval compilation: add references to the variables of the- calling function */-static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s,- JSFunctionBytecode *b, int scope_idx)-{- int i, count;- JSVarDef *vd;-- count = b->arg_count + b->var_count + b->closure_var_count;- s->closure_var = NULL;- s->closure_var_count = 0;- s->closure_var_size = count;- if (count == 0)- return 0;- s->closure_var = js_malloc(ctx, sizeof(s->closure_var[0]) * count);- if (!s->closure_var)- return -1;- /* Add lexical variables in scope at the point of evaluation */- for (i = scope_idx; i >= 0;) {- vd = &b->vardefs[b->arg_count + i];- if (vd->scope_level > 0) {- JSClosureVar *cv = &s->closure_var[s->closure_var_count++];- cv->is_local = TRUE;- cv->is_arg = FALSE;- cv->is_const = vd->is_const;- cv->is_lexical = vd->is_lexical;- cv->var_kind = vd->var_kind;- cv->var_idx = i;- cv->var_name = JS_DupAtom(ctx, vd->var_name);- }- i = vd->scope_next;- }- /* Add argument variables */- for(i = 0; i < b->arg_count; i++) {- JSClosureVar *cv = &s->closure_var[s->closure_var_count++];- vd = &b->vardefs[i];- cv->is_local = TRUE;- cv->is_arg = TRUE;- cv->is_const = FALSE;- cv->is_lexical = FALSE;- cv->var_kind = JS_VAR_NORMAL;- cv->var_idx = i;- cv->var_name = JS_DupAtom(ctx, vd->var_name);- }- /* Add local non lexical variables */- for(i = 0; i < b->var_count; i++) {- vd = &b->vardefs[b->arg_count + i];- if (vd->scope_level == 0 && vd->var_name != JS_ATOM__ret_) {- JSClosureVar *cv = &s->closure_var[s->closure_var_count++];- cv->is_local = TRUE;- cv->is_arg = FALSE;- cv->is_const = FALSE;- cv->is_lexical = FALSE;- cv->var_kind = JS_VAR_NORMAL;- cv->var_idx = i;- cv->var_name = JS_DupAtom(ctx, vd->var_name);- }- }- for(i = 0; i < b->closure_var_count; i++) {- JSClosureVar *cv0 = &b->closure_var[i];- JSClosureVar *cv = &s->closure_var[s->closure_var_count++];- cv->is_local = FALSE;- cv->is_arg = cv0->is_arg;- cv->is_const = cv0->is_const;- cv->is_lexical = cv0->is_lexical;- cv->var_kind = cv0->var_kind;- cv->var_idx = i;- cv->var_name = JS_DupAtom(ctx, cv0->var_name);- }- return 0;-}--typedef struct CodeContext {- const uint8_t *bc_buf; /* code buffer */- int bc_len; /* length of the code buffer */- int pos; /* position past the matched code pattern */- int line_num; /* last visited OP_line_num parameter or -1 */- int op;- int idx;- int label;- int val;- JSAtom atom;-} CodeContext;--#define M2(op1, op2) ((op1) | ((op2) << 8))-#define M3(op1, op2, op3) ((op1) | ((op2) << 8) | ((op3) << 16))-#define M4(op1, op2, op3, op4) ((op1) | ((op2) << 8) | ((op3) << 16) | ((op4) << 24))--static BOOL code_match(CodeContext *s, int pos, ...)-{- const uint8_t *tab = s->bc_buf;- int op, len, op1, line_num, pos_next;- va_list ap;- BOOL ret = FALSE;-- line_num = -1;- va_start(ap, pos);-- for(;;) {- op1 = va_arg(ap, int);- if (op1 == -1) {- s->pos = pos;- s->line_num = line_num;- ret = TRUE;- break;- }- for (;;) {- if (pos >= s->bc_len)- goto done;- op = tab[pos];- len = opcode_info[op].size;- pos_next = pos + len;- if (pos_next > s->bc_len)- goto done;- if (op == OP_line_num) {- line_num = get_u32(tab + pos + 1);- pos = pos_next;- } else {- break;- }- }- if (op != op1) {- if (op1 == (uint8_t)op1 || !op)- break;- if (op != (uint8_t)op1- && op != (uint8_t)(op1 >> 8)- && op != (uint8_t)(op1 >> 16)- && op != (uint8_t)(op1 >> 24)) {- break;- }- s->op = op;- }-- pos++;- switch(opcode_info[op].fmt) {- case OP_FMT_loc8:- case OP_FMT_u8:- {- int idx = tab[pos];- int arg = va_arg(ap, int);- if (arg == -1) {- s->idx = idx;- } else {- if (arg != idx)- goto done;- }- break;- }- case OP_FMT_u16:- case OP_FMT_npop:- case OP_FMT_loc:- case OP_FMT_arg:- case OP_FMT_var_ref:- {- int idx = get_u16(tab + pos);- int arg = va_arg(ap, int);- if (arg == -1) {- s->idx = idx;- } else {- if (arg != idx)- goto done;- }- break;- }- case OP_FMT_i32:- case OP_FMT_u32:- case OP_FMT_label:- case OP_FMT_const:- {- s->label = get_u32(tab + pos);- break;- }- case OP_FMT_label_u16:- {- s->label = get_u32(tab + pos);- s->val = get_u16(tab + pos + 4);- break;- }- case OP_FMT_atom:- {- s->atom = get_u32(tab + pos);- break;- }- case OP_FMT_atom_u8:- {- s->atom = get_u32(tab + pos);- s->val = get_u8(tab + pos + 4);- break;- }- case OP_FMT_atom_u16:- {- s->atom = get_u32(tab + pos);- s->val = get_u16(tab + pos + 4);- break;- }- case OP_FMT_atom_label_u8:- {- s->atom = get_u32(tab + pos);- s->label = get_u32(tab + pos + 4);- s->val = get_u8(tab + pos + 8);- break;- }- default:- break;- }- pos = pos_next;- }- done:- va_end(ap);- return ret;-}--static void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, DynBuf *bc)-{- int i, idx, var_idx;-- /* add the hoisted functions and variables */- for(i = 0; i < s->hoisted_def_count; i++) {- JSHoistedDef *hf = &s->hoisted_def[i];- int has_closure = 0;- BOOL force_init = hf->force_init;- if (s->is_global_var && hf->var_name != JS_ATOM_NULL) {- /* we are in an eval, so the closure contains all the- enclosing variables */- /* If the outer function has a variable environment,- create a property for the variable there */- for(idx = 0; idx < s->closure_var_count; idx++) {- JSClosureVar *cv = &s->closure_var[idx];- if (cv->var_name == hf->var_name) {- has_closure = 2;- force_init = FALSE;- break;- }- if (cv->var_name == JS_ATOM__var_) {- dbuf_putc(bc, OP_get_var_ref);- dbuf_put_u16(bc, idx);- has_closure = 1;- force_init = TRUE;- break;- }- }- if (!has_closure) {- int flags;-- flags = 0;- if (s->eval_type != JS_EVAL_TYPE_GLOBAL)- flags |= JS_PROP_CONFIGURABLE;- if (hf->cpool_idx >= 0 && !hf->is_lexical) {- /* global function definitions need a specific handling */- dbuf_putc(bc, OP_fclosure);- dbuf_put_u32(bc, hf->cpool_idx);-- dbuf_putc(bc, OP_define_func);- dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));- dbuf_putc(bc, flags);-- goto done_hoisted_def;- } else {- if (hf->is_lexical) {- flags |= DEFINE_GLOBAL_LEX_VAR;- if (!hf->is_const)- flags |= JS_PROP_WRITABLE;- }- dbuf_putc(bc, OP_define_var);- dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));- dbuf_putc(bc, flags);- }- }- }- if (hf->cpool_idx >= 0 || force_init) {- if (hf->cpool_idx >= 0) {- dbuf_putc(bc, OP_fclosure);- dbuf_put_u32(bc, hf->cpool_idx);- if (hf->var_name == JS_ATOM__default_) {- /* set default export function name */- dbuf_putc(bc, OP_set_name);- dbuf_put_u32(bc, JS_DupAtom(ctx, JS_ATOM_default));- }- } else {- dbuf_putc(bc, OP_undefined);- }- if (s->is_global_var) {- if (has_closure == 2) {- dbuf_putc(bc, OP_put_var_ref);- dbuf_put_u16(bc, idx);- } else if (has_closure == 1) {- dbuf_putc(bc, OP_define_field);- dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));- dbuf_putc(bc, OP_drop);- } else {- /* XXX: Check if variable is writable and enumerable */- dbuf_putc(bc, OP_put_var);- dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));- }- } else {- var_idx = hf->var_idx;- if (var_idx & ARGUMENT_VAR_OFFSET) {- dbuf_putc(bc, OP_put_arg);- dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET);- } else {- dbuf_putc(bc, OP_put_loc);- dbuf_put_u16(bc, var_idx);- }- }- }- done_hoisted_def:- JS_FreeAtom(ctx, hf->var_name);- }- js_free(ctx, s->hoisted_def);- s->hoisted_def = NULL;- s->hoisted_def_count = 0;- s->hoisted_def_size = 0;-}--static int skip_dead_code(JSFunctionDef *s, const uint8_t *bc_buf, int bc_len,- int pos, int *linep)-{- int op, len, label;-- for (; pos < bc_len; pos += len) {- op = bc_buf[pos];- len = opcode_info[op].size;- if (op == OP_line_num) {- *linep = get_u32(bc_buf + pos + 1);- } else- if (op == OP_label) {- label = get_u32(bc_buf + pos + 1);- if (update_label(s, label, 0) > 0)- break;-#if 0- if (s->label_slots[label].first_reloc) {- printf("line %d: unreferenced label %d:%d has relocations\n",- *linep, label, s->label_slots[label].pos2);- }-#endif- assert(s->label_slots[label].first_reloc == NULL);- } else {- /* XXX: output a warning for unreachable code? */- JSAtom atom;- switch(opcode_info[op].fmt) {- case OP_FMT_label:- case OP_FMT_label_u16:- label = get_u32(bc_buf + pos + 1);- update_label(s, label, -1);- break;- case OP_FMT_atom_label_u8:- case OP_FMT_atom_label_u16:- label = get_u32(bc_buf + pos + 5);- update_label(s, label, -1);- /* fall thru */- case OP_FMT_atom:- case OP_FMT_atom_u8:- case OP_FMT_atom_u16:- atom = get_u32(bc_buf + pos + 1);- JS_FreeAtom(s->ctx, atom);- break;- default:- break;- }- }- }- return pos;-}--static int get_label_pos(JSFunctionDef *s, int label)-{- int i, pos;- for (i = 0; i < 20; i++) {- pos = s->label_slots[label].pos;- for (;;) {- switch (s->byte_code.buf[pos]) {- case OP_line_num:- case OP_label:- pos += 5;- continue;- case OP_goto:- label = get_u32(s->byte_code.buf + pos + 1);- break;- default:- return pos;- }- break;- }- }- return pos;-}--/* convert global variable accesses to local variables or closure- variables when necessary */-static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s)-{- int pos, pos_next, bc_len, op, len, i, idx, arg_valid, line_num;- uint8_t *bc_buf;- JSAtom var_name;- DynBuf bc_out;- CodeContext cc;- int scope;-- cc.bc_buf = bc_buf = s->byte_code.buf;- cc.bc_len = bc_len = s->byte_code.size;- js_dbuf_init(ctx, &bc_out);-- /* first pass for runtime checks (must be done before the- variables are created) */- if (s->is_global_var) {- for(i = 0; i < s->hoisted_def_count; i++) {- JSHoistedDef *hf = &s->hoisted_def[i];- int flags;-- if (hf->var_name != JS_ATOM_NULL) {- /* check if global variable (XXX: simplify) */- for(idx = 0; idx < s->closure_var_count; idx++) {- JSClosureVar *cv = &s->closure_var[idx];- if (cv->var_name == hf->var_name) {- if (s->eval_type == JS_EVAL_TYPE_DIRECT &&- cv->is_lexical) {- /* Check if a lexical variable is- redefined as 'var'. XXX: Could abort- compilation here, but for consistency- with the other checks, we delay the- error generation. */- dbuf_putc(&bc_out, OP_throw_var);- dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name));- dbuf_putc(&bc_out, JS_THROW_VAR_REDECL);- }- goto next;- }- if (cv->var_name == JS_ATOM__var_)- goto next;- }-- dbuf_putc(&bc_out, OP_check_define_var);- dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name));- flags = 0;- if (hf->is_lexical)- flags |= DEFINE_GLOBAL_LEX_VAR;- if (hf->cpool_idx >= 0)- flags |= DEFINE_GLOBAL_FUNC_VAR;- dbuf_putc(&bc_out, flags);- }- next: ;- }- }-- arg_valid = 0;- line_num = 0; /* avoid warning */- for (pos = 0; pos < bc_len; pos = pos_next) {- op = bc_buf[pos];- len = opcode_info[op].size;- pos_next = pos + len;- switch(op) {- case OP_line_num:- line_num = get_u32(bc_buf + pos + 1);- s->line_number_size++;- goto no_change;-- case OP_set_arg_valid_upto:- arg_valid = get_u16(bc_buf + pos + 1);- break;- case OP_eval: /* convert scope index to adjusted variable index */- {- int call_argc = get_u16(bc_buf + pos + 1);- scope = get_u16(bc_buf + pos + 1 + 2);- mark_eval_captured_variables(ctx, s, scope);- dbuf_putc(&bc_out, op);- dbuf_put_u16(&bc_out, call_argc);- dbuf_put_u16(&bc_out, s->scopes[scope].first + 1);- }- break;- case OP_apply_eval: /* convert scope index to adjusted variable index */- scope = get_u16(bc_buf + pos + 1);- mark_eval_captured_variables(ctx, s, scope);- dbuf_putc(&bc_out, op);- dbuf_put_u16(&bc_out, s->scopes[scope].first + 1);- break;- case OP_scope_get_var_undef:- case OP_scope_get_var:- case OP_scope_put_var:- case OP_scope_delete_var:- case OP_scope_get_ref:- case OP_scope_put_var_init:- var_name = get_u32(bc_buf + pos + 1);- scope = get_u16(bc_buf + pos + 5);- pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out,- NULL, NULL, pos_next, arg_valid);- JS_FreeAtom(ctx, var_name);- break;- case OP_scope_make_ref:- {- int label;- LabelSlot *ls;- var_name = get_u32(bc_buf + pos + 1);- label = get_u32(bc_buf + pos + 5);- scope = get_u16(bc_buf + pos + 9);- ls = &s->label_slots[label];- ls->ref_count--; /* always remove label reference */- pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out,- bc_buf, ls, pos_next, arg_valid);- JS_FreeAtom(ctx, var_name);- }- break;- case OP_scope_get_private_field:- case OP_scope_get_private_field2:- case OP_scope_put_private_field:- {- int ret;- var_name = get_u32(bc_buf + pos + 1);- scope = get_u16(bc_buf + pos + 5);- ret = resolve_scope_private_field(ctx, s, var_name, scope, op, &bc_out);- if (ret < 0)- goto fail;- JS_FreeAtom(ctx, var_name);- }- break;- case OP_gosub:- s->jump_size++;- if (OPTIMIZE) {- /* remove calls to empty finalizers */- int label;- LabelSlot *ls;-- label = get_u32(bc_buf + pos + 1);- assert(label >= 0 && label < s->label_count);- ls = &s->label_slots[label];- if (code_match(&cc, ls->pos, OP_ret, -1)) {- ls->ref_count--;- break;- }- }- goto no_change;- case OP_drop:- if (0) {- /* remove drops before return_undef */- /* do not perform this optimization in pass2 because- it breaks patterns recognised in resolve_labels */- int pos1 = pos_next;- int line1 = line_num;- while (code_match(&cc, pos1, OP_drop, -1)) {- if (cc.line_num >= 0) line1 = cc.line_num;- pos1 = cc.pos;- }- if (code_match(&cc, pos1, OP_return_undef, -1)) {- pos_next = pos1;- if (line1 != -1 && line1 != line_num) {- line_num = line1;- s->line_number_size++;- dbuf_putc(&bc_out, OP_line_num);- dbuf_put_u32(&bc_out, line_num);- }- break;- }- }- goto no_change;- case OP_insert3:- if (OPTIMIZE) {- /* Transformation: insert3 put_array_el|put_ref_value drop -> put_array_el|put_ref_value */- if (code_match(&cc, pos_next, M2(OP_put_array_el, OP_put_ref_value), OP_drop, -1)) {- dbuf_putc(&bc_out, cc.op);- pos_next = cc.pos;- if (cc.line_num != -1 && cc.line_num != line_num) {- line_num = cc.line_num;- s->line_number_size++;- dbuf_putc(&bc_out, OP_line_num);- dbuf_put_u32(&bc_out, line_num);- }- break;- }- }- goto no_change;-- case OP_goto:- s->jump_size++;- /* fall thru */- case OP_tail_call:- case OP_tail_call_method:- case OP_return:- case OP_return_undef:- case OP_throw:- case OP_throw_var:- case OP_ret:- if (OPTIMIZE) {- /* remove dead code */- int line = -1;- dbuf_put(&bc_out, bc_buf + pos, len);- pos = skip_dead_code(s, bc_buf, bc_len, pos + len, &line);- pos_next = pos;- if (pos < bc_len && line >= 0 && line_num != line) {- line_num = line;- s->line_number_size++;- dbuf_putc(&bc_out, OP_line_num);- dbuf_put_u32(&bc_out, line_num);- }- break;- }- goto no_change;-- case OP_label:- {- int label;- LabelSlot *ls;-- label = get_u32(bc_buf + pos + 1);- assert(label >= 0 && label < s->label_count);- ls = &s->label_slots[label];- ls->pos2 = bc_out.size + opcode_info[op].size;- }- goto no_change;-- case OP_enter_scope:- {- int scope_idx, scope = get_u16(bc_buf + pos + 1);-- if (scope == 1) {- instantiate_hoisted_definitions(ctx, s, &bc_out);- }-- for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) {- JSVarDef *vd = &s->vars[scope_idx];- if (vd->scope_level == scope) {- if (vd->var_kind == JS_VAR_FUNCTION_DECL ||- vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) {- /* Initialize lexical variable upon entering scope */- dbuf_putc(&bc_out, OP_fclosure);- dbuf_put_u32(&bc_out, vd->func_pool_or_scope_idx);- dbuf_putc(&bc_out, OP_put_loc);- dbuf_put_u16(&bc_out, scope_idx);- } else {- /* XXX: should check if variable can be used- before initialization */- dbuf_putc(&bc_out, OP_set_loc_uninitialized);- dbuf_put_u16(&bc_out, scope_idx);- }- scope_idx = vd->scope_next;- } else {- break;- }- }- }- break;-- case OP_leave_scope:- {- int scope_idx, scope = get_u16(bc_buf + pos + 1);-- for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) {- JSVarDef *vd = &s->vars[scope_idx];- if (vd->scope_level == scope) {- if (vd->is_captured) {- dbuf_putc(&bc_out, OP_close_loc);- dbuf_put_u16(&bc_out, scope_idx);- }- scope_idx = vd->scope_next;- } else {- break;- }- }- }- break;-- case OP_set_name:- {- /* remove dummy set_name opcodes */- JSAtom name = get_u32(bc_buf + pos + 1);- if (name == JS_ATOM_NULL)- break;- }- goto no_change;-- case OP_if_false:- case OP_if_true:- case OP_catch:- s->jump_size++;- goto no_change;-- case OP_dup:- if (OPTIMIZE) {- /* Transformation: dup if_false(l1) drop, l1: if_false(l2) -> if_false(l2) */- /* Transformation: dup if_true(l1) drop, l1: if_true(l2) -> if_true(l2) */- if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), OP_drop, -1)) {- int lab0, lab1, op1, pos1, line1, pos2;- lab0 = lab1 = cc.label;- assert(lab1 >= 0 && lab1 < s->label_count);- op1 = cc.op;- pos1 = cc.pos;- line1 = cc.line_num;- while (code_match(&cc, (pos2 = get_label_pos(s, lab1)), OP_dup, op1, OP_drop, -1)) {- lab1 = cc.label;- }- if (code_match(&cc, pos2, op1, -1)) {- s->jump_size++;- update_label(s, lab0, -1);- update_label(s, cc.label, +1);- dbuf_putc(&bc_out, op1);- dbuf_put_u32(&bc_out, cc.label);- pos_next = pos1;- if (line1 != -1 && line1 != line_num) {- line_num = line1;- s->line_number_size++;- dbuf_putc(&bc_out, OP_line_num);- dbuf_put_u32(&bc_out, line_num);- }- break;- }- }- }- goto no_change;-- case OP_nop:- /* remove erased code */- break;- case OP_set_class_name:- /* only used during parsing */- break;- - default:- no_change:- dbuf_put(&bc_out, bc_buf + pos, len);- break;- }- }-- /* set the new byte code */- dbuf_free(&s->byte_code);- s->byte_code = bc_out;- if (dbuf_error(&s->byte_code)) {- JS_ThrowOutOfMemory(ctx);- return -1;- }- return 0;- fail:- /* continue the copy to keep the atom refcounts consistent */- /* XXX: find a better solution ? */- for (; pos < bc_len; pos = pos_next) {- op = bc_buf[pos];- len = opcode_info[op].size;- pos_next = pos + len;- dbuf_put(&bc_out, bc_buf + pos, len);- }- dbuf_free(&s->byte_code);- s->byte_code = bc_out;- return -1;-}--/* the pc2line table gives a line number for each PC value */-static void add_pc2line_info(JSFunctionDef *s, uint32_t pc, int line_num)-{- if (s->line_number_slots != NULL- && s->line_number_count < s->line_number_size- && pc >= s->line_number_last_pc- && line_num != s->line_number_last) {- s->line_number_slots[s->line_number_count].pc = pc;- s->line_number_slots[s->line_number_count].line_num = line_num;- s->line_number_count++;- s->line_number_last_pc = pc;- s->line_number_last = line_num;- }-}--static void compute_pc2line_info(JSFunctionDef *s)-{- if (!(s->js_mode & JS_MODE_STRIP) && s->line_number_slots) {- int last_line_num = s->line_num;- uint32_t last_pc = 0;- int i;-- js_dbuf_init(s->ctx, &s->pc2line);- for (i = 0; i < s->line_number_count; i++) {- uint32_t pc = s->line_number_slots[i].pc;- int line_num = s->line_number_slots[i].line_num;- int diff_pc, diff_line;-- if (line_num < 0)- continue;-- diff_pc = pc - last_pc;- diff_line = line_num - last_line_num;- if (diff_line == 0 || diff_pc < 0)- continue;-- if (diff_line >= PC2LINE_BASE &&- diff_line < PC2LINE_BASE + PC2LINE_RANGE &&- diff_pc <= PC2LINE_DIFF_PC_MAX) {- dbuf_putc(&s->pc2line, (diff_line - PC2LINE_BASE) +- diff_pc * PC2LINE_RANGE + PC2LINE_OP_FIRST);- } else {- /* longer encoding */- dbuf_putc(&s->pc2line, 0);- dbuf_put_leb128(&s->pc2line, diff_pc);- dbuf_put_sleb128(&s->pc2line, diff_line);- }- last_pc = pc;- last_line_num = line_num;- }- }-}--static RelocEntry *add_reloc(JSContext *ctx, LabelSlot *ls, uint32_t addr, int size)-{- RelocEntry *re;- re = js_malloc(ctx, sizeof(*re));- if (!re)- return NULL;- re->addr = addr;- re->size = size;- re->next = ls->first_reloc;- ls->first_reloc = re;- return re;-}--static BOOL code_has_label(CodeContext *s, int pos, int label)-{- while (pos < s->bc_len) {- int op = s->bc_buf[pos];- if (op == OP_line_num) {- pos += 5;- continue;- }- if (op == OP_label) {- int lab = get_u32(s->bc_buf + pos + 1);- if (lab == label)- return TRUE;- pos += 5;- continue;- }- if (op == OP_goto) {- int lab = get_u32(s->bc_buf + pos + 1);- if (lab == label)- return TRUE;- }- break;- }- return FALSE;-}--/* return the target label, following the OP_goto jumps- the first opcode at destination is stored in *pop- */-static int find_jump_target(JSFunctionDef *s, int label, int *pop, int *pline)-{- int i, pos, op;-- update_label(s, label, -1);- for (i = 0; i < 10; i++) {- assert(label >= 0 && label < s->label_count);- pos = s->label_slots[label].pos2;- for (;;) {- switch(op = s->byte_code.buf[pos]) {- case OP_line_num:- if (pline)- *pline = get_u32(s->byte_code.buf + pos + 1);- /* fall thru */- case OP_label:- pos += opcode_info[op].size;- continue;- case OP_goto:- label = get_u32(s->byte_code.buf + pos + 1);- break;- case OP_drop:- /* ignore drop opcodes if followed by OP_return_undef */- while (s->byte_code.buf[++pos] == OP_drop)- continue;- if (s->byte_code.buf[pos] == OP_return_undef)- op = OP_return_undef;- /* fall thru */- default:- goto done;- }- break;- }- }- /* cycle detected, could issue a warning */- done:- *pop = op;- update_label(s, label, +1);- return label;-}--static void push_short_int(DynBuf *bc_out, int val)-{-#if SHORT_OPCODES- if (val >= -1 && val <= 7) {- dbuf_putc(bc_out, OP_push_0 + val);- return;- }- if (val == (int8_t)val) {- dbuf_putc(bc_out, OP_push_i8);- dbuf_putc(bc_out, val);- return;- }- if (val == (int16_t)val) {- dbuf_putc(bc_out, OP_push_i16);- dbuf_put_u16(bc_out, val);- return;- }-#endif- dbuf_putc(bc_out, OP_push_i32);- dbuf_put_u32(bc_out, val);-}--static void put_short_code(DynBuf *bc_out, int op, int idx)-{-#if SHORT_OPCODES- if (idx < 4) {- switch (op) {- case OP_get_loc:- dbuf_putc(bc_out, OP_get_loc0 + idx);- return;- case OP_put_loc:- dbuf_putc(bc_out, OP_put_loc0 + idx);- return;- case OP_set_loc:- dbuf_putc(bc_out, OP_set_loc0 + idx);- return;- case OP_get_arg:- dbuf_putc(bc_out, OP_get_arg0 + idx);- return;- case OP_put_arg:- dbuf_putc(bc_out, OP_put_arg0 + idx);- return;- case OP_set_arg:- dbuf_putc(bc_out, OP_set_arg0 + idx);- return;- case OP_get_var_ref:- dbuf_putc(bc_out, OP_get_var_ref0 + idx);- return;- case OP_put_var_ref:- dbuf_putc(bc_out, OP_put_var_ref0 + idx);- return;- case OP_set_var_ref:- dbuf_putc(bc_out, OP_set_var_ref0 + idx);- return;- case OP_call:- dbuf_putc(bc_out, OP_call0 + idx);- return;- }- }- if (idx < 256) {- switch (op) {- case OP_get_loc:- dbuf_putc(bc_out, OP_get_loc8);- dbuf_putc(bc_out, idx);- return;- case OP_put_loc:- dbuf_putc(bc_out, OP_put_loc8);- dbuf_putc(bc_out, idx);- return;- case OP_set_loc:- dbuf_putc(bc_out, OP_set_loc8);- dbuf_putc(bc_out, idx);- return;- }- }-#endif- dbuf_putc(bc_out, op);- dbuf_put_u16(bc_out, idx);-}--/* peephole optimizations and resolve goto/labels */-static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)-{- int pos, pos_next, bc_len, op, op1, len, i, line_num;- const uint8_t *bc_buf;- DynBuf bc_out;- LabelSlot *label_slots, *ls;- RelocEntry *re, *re_next;- CodeContext cc;- int label;-#if SHORT_OPCODES- JumpSlot *jp;-#endif-- label_slots = s->label_slots;-- line_num = s->line_num;-- cc.bc_buf = bc_buf = s->byte_code.buf;- cc.bc_len = bc_len = s->byte_code.size;- js_dbuf_init(ctx, &bc_out);--#if SHORT_OPCODES- if (s->jump_size) {- s->jump_slots = js_mallocz(s->ctx, sizeof(*s->jump_slots) * s->jump_size);- if (s->jump_slots == NULL)- return -1;- }-#endif- /* XXX: Should skip this phase if not generating SHORT_OPCODES */- if (s->line_number_size && !(s->js_mode & JS_MODE_STRIP)) {- s->line_number_slots = js_mallocz(s->ctx, sizeof(*s->line_number_slots) * s->line_number_size);- if (s->line_number_slots == NULL)- return -1;- s->line_number_last = s->line_num;- s->line_number_last_pc = 0;- }-- /* initialize the 'home_object' variable if needed */- if (s->home_object_var_idx >= 0) {- dbuf_putc(&bc_out, OP_special_object);- dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_HOME_OBJECT);- put_short_code(&bc_out, OP_put_loc, s->home_object_var_idx);- }- /* initialize the 'this.active_func' variable if needed */- if (s->this_active_func_var_idx >= 0) {- dbuf_putc(&bc_out, OP_special_object);- dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC);- put_short_code(&bc_out, OP_put_loc, s->this_active_func_var_idx);- }- /* initialize the 'new.target' variable if needed */- if (s->new_target_var_idx >= 0) {- dbuf_putc(&bc_out, OP_special_object);- dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_NEW_TARGET);- put_short_code(&bc_out, OP_put_loc, s->new_target_var_idx);- }- /* initialize the 'this' variable if needed. In a derived class- constructor, this is initially uninitialized. */- if (s->this_var_idx >= 0) {- if (s->is_derived_class_constructor) {- dbuf_putc(&bc_out, OP_set_loc_uninitialized);- dbuf_put_u16(&bc_out, s->this_var_idx);- } else {- dbuf_putc(&bc_out, OP_push_this);- put_short_code(&bc_out, OP_put_loc, s->this_var_idx);- }- }- /* initialize the 'arguments' variable if needed */- if (s->arguments_var_idx >= 0) {- if ((s->js_mode & JS_MODE_STRICT) || !s->has_simple_parameter_list) {- dbuf_putc(&bc_out, OP_special_object);- dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_ARGUMENTS);- } else {- dbuf_putc(&bc_out, OP_special_object);- dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS);- }- put_short_code(&bc_out, OP_put_loc, s->arguments_var_idx);- }- /* initialize a reference to the current function if needed */- if (s->func_var_idx >= 0) {- dbuf_putc(&bc_out, OP_special_object);- dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC);- put_short_code(&bc_out, OP_put_loc, s->func_var_idx);- }- /* initialize the variable environment object if needed */- if (s->var_object_idx >= 0) {- dbuf_putc(&bc_out, OP_special_object);- dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT);- put_short_code(&bc_out, OP_put_loc, s->var_object_idx);- }-- for (pos = 0; pos < bc_len; pos = pos_next) {- int val;- op = bc_buf[pos];- len = opcode_info[op].size;- pos_next = pos + len;- switch(op) {- case OP_line_num:- /* line number info (for debug). We put it in a separate- compressed table to reduce memory usage and get better- performance */- line_num = get_u32(bc_buf + pos + 1);- break;-- case OP_label:- {- label = get_u32(bc_buf + pos + 1);- assert(label >= 0 && label < s->label_count);- ls = &label_slots[label];- assert(ls->addr == -1);- ls->addr = bc_out.size;- /* resolve the relocation entries */- for(re = ls->first_reloc; re != NULL; re = re_next) {- int diff = ls->addr - re->addr;- re_next = re->next;- switch (re->size) {- case 4:- put_u32(bc_out.buf + re->addr, diff);- break;- case 2:- assert(diff == (int16_t)diff);- put_u16(bc_out.buf + re->addr, diff);- break;- case 1:- assert(diff == (int8_t)diff);- put_u8(bc_out.buf + re->addr, diff);- break;- }- js_free(ctx, re);- }- ls->first_reloc = NULL;- }- break;-- case OP_call:- case OP_call_method:- {- /* detect and transform tail calls */- int argc;- argc = get_u16(bc_buf + pos + 1);- if (code_match(&cc, pos_next, OP_return, -1)) {- if (cc.line_num >= 0) line_num = cc.line_num;- add_pc2line_info(s, bc_out.size, line_num);- put_short_code(&bc_out, op + 1, argc);- pos_next = skip_dead_code(s, bc_buf, bc_len, cc.pos, &line_num);- break;- }- add_pc2line_info(s, bc_out.size, line_num);- put_short_code(&bc_out, op, argc);- break;- }- goto no_change;-- case OP_return:- case OP_return_undef:- case OP_return_async:- case OP_throw:- case OP_throw_var:- pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);- goto no_change;-- case OP_goto:- label = get_u32(bc_buf + pos + 1);- has_goto:- if (OPTIMIZE) {- int line1 = -1;- /* Use custom matcher because multiple labels can follow */- label = find_jump_target(s, label, &op1, &line1);- if (code_has_label(&cc, pos_next, label)) {- /* jump to next instruction: remove jump */- update_label(s, label, -1);- break;- }- if (op1 == OP_return || op1 == OP_return_undef || op1 == OP_throw) {- /* jump to return/throw: remove jump, append return/throw */- /* updating the line number obfuscates assembly listing */- //if (line1 >= 0) line_num = line1;- update_label(s, label, -1);- add_pc2line_info(s, bc_out.size, line_num);- dbuf_putc(&bc_out, op1);- pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);- break;- }- /* XXX: should duplicate single instructions followed by goto or return */- /* For example, can match one of these followed by return:- push_i32 / push_const / push_atom_value / get_var /- undefined / null / push_false / push_true / get_ref_value /- get_loc / get_arg / get_var_ref- */- }- goto has_label;-- case OP_gosub:- label = get_u32(bc_buf + pos + 1);- if (0 && OPTIMIZE) {- label = find_jump_target(s, label, &op1, NULL);- if (op1 == OP_ret) {- update_label(s, label, -1);- /* empty finally clause: remove gosub */- break;- }- }- goto has_label;-- case OP_catch:- label = get_u32(bc_buf + pos + 1);- goto has_label;-- case OP_if_true:- case OP_if_false:- label = get_u32(bc_buf + pos + 1);- if (OPTIMIZE) {- label = find_jump_target(s, label, &op1, NULL);- /* transform if_false/if_true(l1) label(l1) -> drop label(l1) */- if (code_has_label(&cc, pos_next, label)) {- update_label(s, label, -1);- dbuf_putc(&bc_out, OP_drop);- break;- }- /* transform if_false(l1) goto(l2) label(l1) -> if_false(l2) label(l1) */- if (code_match(&cc, pos_next, OP_goto, -1)) {- int pos1 = cc.pos;- int line1 = cc.line_num;- if (code_has_label(&cc, pos1, label)) {- if (line1 >= 0) line_num = line1;- pos_next = pos1;- update_label(s, label, -1);- label = cc.label;- op ^= OP_if_true ^ OP_if_false;- }- }- }- has_label:- add_pc2line_info(s, bc_out.size, line_num);- if (op == OP_goto) {- pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);- }- assert(label >= 0 && label < s->label_count);- ls = &label_slots[label];-#if SHORT_OPCODES- jp = &s->jump_slots[s->jump_count++];- jp->op = op;- jp->size = 4;- jp->pos = bc_out.size + 1;- jp->label = label;-- if (ls->addr == -1) {- int diff = ls->pos2 - pos - 1;- if (diff < 128 && (op == OP_if_false || op == OP_if_true || op == OP_goto)) {- jp->size = 1;- jp->op = OP_if_false8 + (op - OP_if_false);- dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false));- dbuf_putc(&bc_out, 0);- if (!add_reloc(ctx, ls, bc_out.size - 1, 1))- goto fail;- break;- }- if (diff < 32768 && op == OP_goto) {- jp->size = 2;- jp->op = OP_goto16;- dbuf_putc(&bc_out, OP_goto16);- dbuf_put_u16(&bc_out, 0);- if (!add_reloc(ctx, ls, bc_out.size - 2, 2))- goto fail;- break;- }- } else {- int diff = ls->addr - bc_out.size - 1;- if (diff == (int8_t)diff && (op == OP_if_false || op == OP_if_true || op == OP_goto)) {- jp->size = 1;- jp->op = OP_if_false8 + (op - OP_if_false);- dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false));- dbuf_putc(&bc_out, diff);- break;- }- if (diff == (int16_t)diff && op == OP_goto) {- jp->size = 2;- jp->op = OP_goto16;- dbuf_putc(&bc_out, OP_goto16);- dbuf_put_u16(&bc_out, diff);- break;- }- }-#endif- dbuf_putc(&bc_out, op);- dbuf_put_u32(&bc_out, ls->addr - bc_out.size);- if (ls->addr == -1) {- /* unresolved yet: create a new relocation entry */- if (!add_reloc(ctx, ls, bc_out.size - 4, 4))- goto fail;- }- break;- case OP_with_get_var:- case OP_with_put_var:- case OP_with_delete_var:- case OP_with_make_ref:- case OP_with_get_ref:- case OP_with_get_ref_undef:- {- JSAtom atom;- int is_with;-- atom = get_u32(bc_buf + pos + 1);- label = get_u32(bc_buf + pos + 5);- is_with = bc_buf[pos + 9];- if (OPTIMIZE) {- label = find_jump_target(s, label, &op1, NULL);- }- assert(label >= 0 && label < s->label_count);- ls = &label_slots[label];- add_pc2line_info(s, bc_out.size, line_num);-#if SHORT_OPCODES- jp = &s->jump_slots[s->jump_count++];- jp->op = op;- jp->size = 4;- jp->pos = bc_out.size + 5;- jp->label = label;-#endif- dbuf_putc(&bc_out, op);- dbuf_put_u32(&bc_out, atom);- dbuf_put_u32(&bc_out, ls->addr - bc_out.size);- if (ls->addr == -1) {- /* unresolved yet: create a new relocation entry */- if (!add_reloc(ctx, ls, bc_out.size - 4, 4))- goto fail;- }- dbuf_putc(&bc_out, is_with);- }- break;-- case OP_drop:- if (OPTIMIZE) {- /* remove useless drops before return */- if (code_match(&cc, pos_next, OP_return_undef, -1)) {- if (cc.line_num >= 0) line_num = cc.line_num;- break;- }- }- goto no_change;-- case OP_null:-#if SHORT_OPCODES- if (OPTIMIZE) {- /* transform null strict_eq into is_null */- if (code_match(&cc, pos_next, OP_strict_eq, -1)) {- if (cc.line_num >= 0) line_num = cc.line_num;- add_pc2line_info(s, bc_out.size, line_num);- dbuf_putc(&bc_out, OP_is_null);- pos_next = cc.pos;- break;- }- /* transform null strict_neq if_false/if_true -> is_null if_true/if_false */- if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) {- if (cc.line_num >= 0) line_num = cc.line_num;- add_pc2line_info(s, bc_out.size, line_num);- dbuf_putc(&bc_out, OP_is_null);- pos_next = cc.pos;- label = cc.label;- op = cc.op ^ OP_if_false ^ OP_if_true;- goto has_label;- }- }-#endif- /* fall thru */- case OP_push_false:- case OP_push_true:- if (OPTIMIZE) {- val = (op == OP_push_true);- if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {- has_constant_test:- if (cc.line_num >= 0) line_num = cc.line_num;- if (val == cc.op - OP_if_false) {- /* transform null if_false(l1) -> goto l1 */- /* transform false if_false(l1) -> goto l1 */- /* transform true if_true(l1) -> goto l1 */- pos_next = cc.pos;- op = OP_goto;- label = cc.label;- goto has_goto;- } else {- /* transform null if_true(l1) -> nop */- /* transform false if_true(l1) -> nop */- /* transform true if_false(l1) -> nop */- pos_next = cc.pos;- update_label(s, cc.label, -1);- break;- }- }- }- goto no_change;-- case OP_push_i32:- if (OPTIMIZE) {- /* transform i32(val) neg -> i32(-val) */- val = get_i32(bc_buf + pos + 1);- if ((val != INT32_MIN && val != 0)- && code_match(&cc, pos_next, OP_neg, -1)) {- if (cc.line_num >= 0) line_num = cc.line_num;- if (code_match(&cc, cc.pos, OP_drop, -1)) {- if (cc.line_num >= 0) line_num = cc.line_num;- } else {- add_pc2line_info(s, bc_out.size, line_num);- push_short_int(&bc_out, -val);- }- pos_next = cc.pos;- break;- }- /* remove push/drop pairs generated by the parser */- if (code_match(&cc, pos_next, OP_drop, -1)) {- if (cc.line_num >= 0) line_num = cc.line_num;- pos_next = cc.pos;- break;- }- /* Optimize constant tests: `if (0)`, `if (1)`, `if (!0)`... */- if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {- val = (val != 0);- goto has_constant_test;- }- add_pc2line_info(s, bc_out.size, line_num);- push_short_int(&bc_out, val);- break;- }- goto no_change;--#if SHORT_OPCODES- case OP_push_const:- case OP_fclosure:- if (OPTIMIZE) {- int idx = get_u32(bc_buf + pos + 1);- if (idx < 256) {- add_pc2line_info(s, bc_out.size, line_num);- dbuf_putc(&bc_out, OP_push_const8 + op - OP_push_const);- dbuf_putc(&bc_out, idx);- break;- }- }- goto no_change;-- case OP_get_field:- if (OPTIMIZE) {- JSAtom atom = get_u32(bc_buf + pos + 1);- if (atom == JS_ATOM_length) {- JS_FreeAtom(ctx, atom);- add_pc2line_info(s, bc_out.size, line_num);- dbuf_putc(&bc_out, OP_get_length);- break;- }- }- goto no_change;-#endif- case OP_push_atom_value:- if (OPTIMIZE) {- JSAtom atom = get_u32(bc_buf + pos + 1);- /* remove push/drop pairs generated by the parser */- if (code_match(&cc, pos_next, OP_drop, -1)) {- JS_FreeAtom(ctx, atom);- if (cc.line_num >= 0) line_num = cc.line_num;- pos_next = cc.pos;- break;- }-#if SHORT_OPCODES- if (atom == JS_ATOM_empty_string) {- JS_FreeAtom(ctx, atom);- add_pc2line_info(s, bc_out.size, line_num);- dbuf_putc(&bc_out, OP_push_empty_string);- break;- }-#endif- }- goto no_change;-- case OP_to_propkey:- case OP_to_propkey2:- if (OPTIMIZE) {- /* remove redundant to_propkey/to_propkey2 opcodes when storing simple data */- if (code_match(&cc, pos_next, M3(OP_get_loc, OP_get_arg, OP_get_var_ref), -1, OP_put_array_el, -1)- || code_match(&cc, pos_next, M3(OP_push_i32, OP_push_const, OP_push_atom_value), OP_put_array_el, -1)- || code_match(&cc, pos_next, M4(OP_undefined, OP_null, OP_push_true, OP_push_false), OP_put_array_el, -1)) {- break;- }- }- goto no_change;-- case OP_undefined:- if (OPTIMIZE) {- /* remove push/drop pairs generated by the parser */- if (code_match(&cc, pos_next, OP_drop, -1)) {- if (cc.line_num >= 0) line_num = cc.line_num;- pos_next = cc.pos;- break;- }- /* transform undefined return -> return_undefined */- if (code_match(&cc, pos_next, OP_return, -1)) {- if (cc.line_num >= 0) line_num = cc.line_num;- add_pc2line_info(s, bc_out.size, line_num);- dbuf_putc(&bc_out, OP_return_undef);- pos_next = cc.pos;- break;- }- /* transform undefined if_true(l1)/if_false(l1) -> nop/goto(l1) */- if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {- val = 0;- goto has_constant_test;- }-#if SHORT_OPCODES- /* transform undefined strict_eq -> is_undefined */- if (code_match(&cc, pos_next, OP_strict_eq, -1)) {- if (cc.line_num >= 0) line_num = cc.line_num;- add_pc2line_info(s, bc_out.size, line_num);- dbuf_putc(&bc_out, OP_is_undefined);- pos_next = cc.pos;- break;- }- /* transform undefined strict_neq if_false/if_true -> is_undefined if_true/if_false */- if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) {- if (cc.line_num >= 0) line_num = cc.line_num;- add_pc2line_info(s, bc_out.size, line_num);- dbuf_putc(&bc_out, OP_is_undefined);- pos_next = cc.pos;- label = cc.label;- op = cc.op ^ OP_if_false ^ OP_if_true;- goto has_label;- }-#endif- }- goto no_change;-- case OP_insert2:- if (OPTIMIZE) {- /* Transformation:- insert2 put_field(a) drop -> put_field(a)- insert2 put_var_strict(a) drop -> put_var_strict(a)- */- if (code_match(&cc, pos_next, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) {- if (cc.line_num >= 0) line_num = cc.line_num;- add_pc2line_info(s, bc_out.size, line_num);- dbuf_putc(&bc_out, cc.op);- dbuf_put_u32(&bc_out, cc.atom);- pos_next = cc.pos;- break;- }- }- goto no_change;-- case OP_dup:- if (OPTIMIZE) {- /* Transformation: dup put_x(n) drop -> put_x(n) */- int op1, line2 = -1;- /* Transformation: dup put_x(n) -> set_x(n) */- if (code_match(&cc, pos_next, M3(OP_put_loc, OP_put_arg, OP_put_var_ref), -1, -1)) {- if (cc.line_num >= 0) line_num = cc.line_num;- op1 = cc.op + 1; /* put_x -> set_x */- pos_next = cc.pos;- if (code_match(&cc, cc.pos, OP_drop, -1)) {- if (cc.line_num >= 0) line_num = cc.line_num;- op1 -= 1; /* set_x drop -> put_x */- pos_next = cc.pos;- if (code_match(&cc, cc.pos, op1 - 1, cc.idx, -1)) {- line2 = cc.line_num; /* delay line number update */- op1 += 1; /* put_x(n) get_x(n) -> set_x(n) */- pos_next = cc.pos;- }- }- add_pc2line_info(s, bc_out.size, line_num);- put_short_code(&bc_out, op1, cc.idx);- if (line2 >= 0) line_num = line2;- break;- }- }- goto no_change;-- case OP_get_loc:- if (OPTIMIZE) {- /* transformation:- get_loc(n) post_dec put_loc(n) drop -> dec_loc(n)- get_loc(n) post_inc put_loc(n) drop -> inc_loc(n)- get_loc(n) dec dup put_loc(n) drop -> dec_loc(n)- get_loc(n) inc dup put_loc(n) drop -> inc_loc(n)- */- int idx;- idx = get_u16(bc_buf + pos + 1);- if (idx >= 256)- goto no_change;- if (code_match(&cc, pos_next, M2(OP_post_dec, OP_post_inc), OP_put_loc, idx, OP_drop, -1) ||- code_match(&cc, pos_next, M2(OP_dec, OP_inc), OP_dup, OP_put_loc, idx, OP_drop, -1)) {- if (cc.line_num >= 0) line_num = cc.line_num;- add_pc2line_info(s, bc_out.size, line_num);- dbuf_putc(&bc_out, (cc.op == OP_inc || cc.op == OP_post_inc) ? OP_inc_loc : OP_dec_loc);- dbuf_putc(&bc_out, idx);- pos_next = cc.pos;- break;- }- /* transformation:- get_loc(n) push_atom_value(x) add dup put_loc(n) drop -> push_atom_value(x) add_loc(n)- */- if (code_match(&cc, pos_next, OP_push_atom_value, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) {- if (cc.line_num >= 0) line_num = cc.line_num;- add_pc2line_info(s, bc_out.size, line_num);-#if SHORT_OPCODES- if (cc.atom == JS_ATOM_empty_string) {- JS_FreeAtom(ctx, cc.atom);- dbuf_putc(&bc_out, OP_push_empty_string);- } else-#endif- {- dbuf_putc(&bc_out, OP_push_atom_value);- dbuf_put_u32(&bc_out, cc.atom);- }- dbuf_putc(&bc_out, OP_add_loc);- dbuf_putc(&bc_out, idx);- pos_next = cc.pos;- break;- }- /* transformation:- get_loc(n) push_i32(x) add dup put_loc(n) drop -> push_i32(x) add_loc(n)- */- if (code_match(&cc, pos_next, OP_push_i32, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) {- if (cc.line_num >= 0) line_num = cc.line_num;- add_pc2line_info(s, bc_out.size, line_num);- push_short_int(&bc_out, cc.label);- dbuf_putc(&bc_out, OP_add_loc);- dbuf_putc(&bc_out, idx);- pos_next = cc.pos;- break;- }- /* transformation: XXX: also do these:- get_loc(n) get_loc(x) add dup put_loc(n) drop -> get_loc(x) add_loc(n)- get_loc(n) get_arg(x) add dup put_loc(n) drop -> get_arg(x) add_loc(n)- get_loc(n) get_var_ref(x) add dup put_loc(n) drop -> get_var_ref(x) add_loc(n)- */- 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)) {- if (cc.line_num >= 0) line_num = cc.line_num;- add_pc2line_info(s, bc_out.size, line_num);- put_short_code(&bc_out, cc.op, cc.idx);- dbuf_putc(&bc_out, OP_add_loc);- dbuf_putc(&bc_out, idx);- pos_next = cc.pos;- break;- }- add_pc2line_info(s, bc_out.size, line_num);- put_short_code(&bc_out, op, idx);- break;- }- goto no_change;-#if SHORT_OPCODES- case OP_get_arg:- case OP_get_var_ref:- if (OPTIMIZE) {- int idx;- idx = get_u16(bc_buf + pos + 1);- add_pc2line_info(s, bc_out.size, line_num);- put_short_code(&bc_out, op, idx);- break;- }- goto no_change;-#endif- case OP_put_loc:- case OP_put_arg:- case OP_put_var_ref:- if (OPTIMIZE) {- /* transformation: put_x(n) get_x(n) -> set_x(n) */- int idx;- idx = get_u16(bc_buf + pos + 1);- if (code_match(&cc, pos_next, op - 1, idx, -1)) {- if (cc.line_num >= 0) line_num = cc.line_num;- add_pc2line_info(s, bc_out.size, line_num);- put_short_code(&bc_out, op + 1, idx);- pos_next = cc.pos;- break;- }- add_pc2line_info(s, bc_out.size, line_num);- put_short_code(&bc_out, op, idx);- break;- }- goto no_change;-- case OP_post_inc:- case OP_post_dec:- if (OPTIMIZE) {- /* transformation:- post_inc put_x drop -> inc put_x- post_inc perm3 put_field drop -> inc put_field- post_inc perm3 put_var_strict drop -> inc put_var_strict- post_inc perm4 put_array_el drop -> inc put_array_el- */- int op1, idx;- if (code_match(&cc, pos_next, M3(OP_put_loc, OP_put_arg, OP_put_var_ref), -1, OP_drop, -1)) {- if (cc.line_num >= 0) line_num = cc.line_num;- op1 = cc.op;- idx = cc.idx;- pos_next = cc.pos;- if (code_match(&cc, cc.pos, op1 - 1, idx, -1)) {- if (cc.line_num >= 0) line_num = cc.line_num;- op1 += 1; /* put_x(n) get_x(n) -> set_x(n) */- pos_next = cc.pos;- }- add_pc2line_info(s, bc_out.size, line_num);- dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));- put_short_code(&bc_out, op1, idx);- break;- }- if (code_match(&cc, pos_next, OP_perm3, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) {- if (cc.line_num >= 0) line_num = cc.line_num;- add_pc2line_info(s, bc_out.size, line_num);- dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));- dbuf_putc(&bc_out, cc.op);- dbuf_put_u32(&bc_out, cc.atom);- pos_next = cc.pos;- break;- }- if (code_match(&cc, pos_next, OP_perm4, OP_put_array_el, OP_drop, -1)) {- if (cc.line_num >= 0) line_num = cc.line_num;- add_pc2line_info(s, bc_out.size, line_num);- dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));- dbuf_putc(&bc_out, OP_put_array_el);- pos_next = cc.pos;- break;- }- }- goto no_change;--#if SHORT_OPCODES- case OP_typeof:- if (OPTIMIZE) {- /* simplify typeof tests */- if (code_match(&cc, pos_next, OP_push_atom_value, M4(OP_strict_eq, OP_strict_neq, OP_eq, OP_neq), -1)) {- if (cc.line_num >= 0) line_num = cc.line_num;- int op1 = (cc.op == OP_strict_eq || cc.op == OP_eq) ? OP_strict_eq : OP_strict_neq;- int op2 = -1;- switch (cc.atom) {- case JS_ATOM_undefined:- op2 = OP_typeof_is_undefined;- break;- case JS_ATOM_function:- op2 = OP_typeof_is_function;- break;- }- if (op2 >= 0) {- /* transform typeof(s) == "<type>" into is_<type> */- if (op1 == OP_strict_eq) {- add_pc2line_info(s, bc_out.size, line_num);- dbuf_putc(&bc_out, op2);- JS_FreeAtom(ctx, cc.atom);- pos_next = cc.pos;- break;- }- if (op1 == OP_strict_neq && code_match(&cc, cc.pos, OP_if_false, -1)) {- /* transform typeof(s) != "<type>" if_false into is_<type> if_true */- if (cc.line_num >= 0) line_num = cc.line_num;- add_pc2line_info(s, bc_out.size, line_num);- dbuf_putc(&bc_out, op2);- JS_FreeAtom(ctx, cc.atom);- pos_next = cc.pos;- label = cc.label;- op = OP_if_true;- goto has_label;- }- }- }- }- goto no_change;-#endif-- default:- no_change:- add_pc2line_info(s, bc_out.size, line_num);- dbuf_put(&bc_out, bc_buf + pos, len);- break;- }- }-- /* check that there were no missing labels */- for(i = 0; i < s->label_count; i++) {- assert(label_slots[i].first_reloc == NULL);- }-#if SHORT_OPCODES- if (OPTIMIZE) {- /* more jump optimizations */- int patch_offsets = 0;- for (i = 0, jp = s->jump_slots; i < s->jump_count; i++, jp++) {- LabelSlot *ls;- JumpSlot *jp1;- int j, pos, diff, delta;-- delta = 3;- switch (op = jp->op) {- case OP_goto16:- delta = 1;- /* fall thru */- case OP_if_false:- case OP_if_true:- case OP_goto:- pos = jp->pos;- diff = s->label_slots[jp->label].addr - pos;- if (diff >= -128 && diff <= 127 + delta) {- //put_u8(bc_out.buf + pos, diff);- jp->size = 1;- if (op == OP_goto16) {- bc_out.buf[pos - 1] = jp->op = OP_goto8;- } else {- bc_out.buf[pos - 1] = jp->op = OP_if_false8 + (op - OP_if_false);- }- goto shrink;- } else- if (diff == (int16_t)diff && op == OP_goto) {- //put_u16(bc_out.buf + pos, diff);- jp->size = 2;- delta = 2;- bc_out.buf[pos - 1] = jp->op = OP_goto16;- shrink:- /* XXX: should reduce complexity, using 2 finger copy scheme */- memmove(bc_out.buf + pos + jp->size, bc_out.buf + pos + jp->size + delta,- bc_out.size - pos - jp->size - delta);- bc_out.size -= delta;- patch_offsets++;- for (j = 0, ls = s->label_slots; j < s->label_count; j++, ls++) {- if (ls->addr > pos)- ls->addr -= delta;- }- for (j = i + 1, jp1 = jp + 1; j < s->jump_count; j++, jp1++) {- if (jp1->pos > pos)- jp1->pos -= delta;- }- for (j = 0; j < s->line_number_count; j++) {- if (s->line_number_slots[j].pc > pos)- s->line_number_slots[j].pc -= delta;- }- continue;- }- break;- }- }- if (patch_offsets) {- JumpSlot *jp1;- int j;- for (j = 0, jp1 = s->jump_slots; j < s->jump_count; j++, jp1++) {- int diff1 = s->label_slots[jp1->label].addr - jp1->pos;- switch (jp1->size) {- case 1:- put_u8(bc_out.buf + jp1->pos, diff1);- break;- case 2:- put_u16(bc_out.buf + jp1->pos, diff1);- break;- case 4:- put_u32(bc_out.buf + jp1->pos, diff1);- break;- }- }- }- }- js_free(ctx, s->jump_slots);- s->jump_slots = NULL;-#endif- js_free(ctx, s->label_slots);- s->label_slots = NULL;- /* XXX: should delay until copying to runtime bytecode function */- compute_pc2line_info(s);- js_free(ctx, s->line_number_slots);- s->line_number_slots = NULL;- /* set the new byte code */- dbuf_free(&s->byte_code);- s->byte_code = bc_out;- s->use_short_opcodes = TRUE;- if (dbuf_error(&s->byte_code)) {- JS_ThrowOutOfMemory(ctx);- return -1;- }- return 0;- fail:- /* XXX: not safe */- dbuf_free(&bc_out);- return -1;-}--/* compute the maximum stack size needed by the function */--typedef struct StackSizeState {- int stack_len_max;- uint16_t *stack_level_tab;-} StackSizeState;--static __exception int compute_stack_size_rec(JSContext *ctx,- JSFunctionDef *fd,- StackSizeState *s,- int pos, int op, int stack_len)-{- int bc_len, diff, n_pop, pos_next;- const JSOpCode *oi;- const uint8_t *bc_buf;-- if (stack_len > s->stack_len_max) {- s->stack_len_max = stack_len;- if (s->stack_len_max > JS_STACK_SIZE_MAX)- goto stack_overflow;- }- bc_buf = fd->byte_code.buf;- bc_len = fd->byte_code.size;- for(;;) {- if ((unsigned)pos >= bc_len)- goto buf_overflow;-#if 0- printf("%5d: %d\n", pos, stack_len);-#endif- if (s->stack_level_tab[pos] != 0xffff) {- /* already explored: check that the stack size is consistent */- if (s->stack_level_tab[pos] != stack_len) {- JS_ThrowInternalError(ctx, "unconsistent stack size: %d %d (pc=%d)",- s->stack_level_tab[pos], stack_len, pos);- return -1;- } else {- return 0;- }- } else {- s->stack_level_tab[pos] = stack_len;- }-- op = bc_buf[pos];- if (op == 0 || op >= OP_COUNT) {- JS_ThrowInternalError(ctx, "invalid opcode (op=%d, pc=%d)", op, pos);- return -1;- }- oi = &short_opcode_info(op);- pos_next = pos + oi->size;- if (pos_next > bc_len) {- buf_overflow:- JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);- return -1;- }- n_pop = oi->n_pop;- /* call pops a variable number of arguments */- if (oi->fmt == OP_FMT_npop || oi->fmt == OP_FMT_npop_u16) {- n_pop += get_u16(bc_buf + pos + 1);- } else {-#if SHORT_OPCODES- if (oi->fmt == OP_FMT_npopx) {- n_pop += op - OP_call0;- }-#endif- }-- if (stack_len < n_pop) {- JS_ThrowInternalError(ctx, "stack underflow (op=%d, pc=%d)", op, pos);- return -1;- }- stack_len += oi->n_push - n_pop;- if (stack_len > s->stack_len_max) {- s->stack_len_max = stack_len;- if (s->stack_len_max > JS_STACK_SIZE_MAX)- goto stack_overflow;- }- switch(op) {- case OP_tail_call:- case OP_tail_call_method:- case OP_return:- case OP_return_undef:- case OP_return_async:- case OP_throw:- case OP_throw_var:- case OP_ret:- goto done;- case OP_goto:- diff = get_u32(bc_buf + pos + 1);- pos_next = pos + 1 + diff;- break;-#if SHORT_OPCODES- case OP_goto16:- diff = (int16_t)get_u16(bc_buf + pos + 1);- pos_next = pos + 1 + diff;- break;- case OP_goto8:- diff = (int8_t)bc_buf[pos + 1];- pos_next = pos + 1 + diff;- break;- case OP_if_true8:- case OP_if_false8:- diff = (int8_t)bc_buf[pos + 1];- if (compute_stack_size_rec(ctx, fd, s, pos + 1 + diff, op, stack_len))- return -1;- break;-#endif- case OP_if_true:- case OP_if_false:- case OP_catch:- diff = get_u32(bc_buf + pos + 1);- if (compute_stack_size_rec(ctx, fd, s, pos + 1 + diff, op, stack_len))- return -1;- break;- case OP_gosub:- diff = get_u32(bc_buf + pos + 1);- if (compute_stack_size_rec(ctx, fd, s, pos + 1 + diff, op, stack_len + 1))- return -1;- break;- case OP_with_get_var:- case OP_with_delete_var:- diff = get_u32(bc_buf + pos + 5);- if (compute_stack_size_rec(ctx, fd, s, pos + 5 + diff, op, stack_len + 1))- return -1;- break;- case OP_with_make_ref:- case OP_with_get_ref:- case OP_with_get_ref_undef:- diff = get_u32(bc_buf + pos + 5);- if (compute_stack_size_rec(ctx, fd, s, pos + 5 + diff, op, stack_len + 2))- return -1;- break;- case OP_with_put_var:- diff = get_u32(bc_buf + pos + 5);- if (compute_stack_size_rec(ctx, fd, s, pos + 5 + diff, op, stack_len - 1))- return -1;- break;-- default:- break;- }- pos = pos_next;- }- done:- return 0;-- stack_overflow:- JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos);- return -1;-}--static __exception int compute_stack_size(JSContext *ctx,- JSFunctionDef *fd,- int *pstack_size)-{- StackSizeState s_s, *s = &s_s;- int bc_len, i, ret;-- bc_len = fd->byte_code.size;- /* bc_len > 0 */- s->stack_level_tab = js_malloc(ctx, sizeof(s->stack_level_tab[0]) * bc_len);- if (!s->stack_level_tab)- return -1;- for(i = 0; i < bc_len; i++)- s->stack_level_tab[i] = 0xffff;- s->stack_len_max = 0;- ret = compute_stack_size_rec(ctx, fd, s, 0, OP_invalid, 0);- js_free(ctx, s->stack_level_tab);- *pstack_size = s->stack_len_max;- return ret;-}--static int add_module_variables(JSContext *ctx, JSFunctionDef *fd)-{- int i, idx;- JSModuleDef *m = fd->module;- JSExportEntry *me;- JSHoistedDef *hf;-- /* The imported global variables were added as closure variables- in js_parse_import(). We add here the module global- variables. */-- for(i = 0; i < fd->hoisted_def_count; i++) {- hf = &fd->hoisted_def[i];- if (add_closure_var(ctx, fd, TRUE, FALSE, i, hf->var_name, hf->is_const,- hf->is_lexical, FALSE) < 0)- return -1;- }-- /* resolve the variable names of the local exports */- for(i = 0; i < m->export_entries_count; i++) {- me = &m->export_entries[i];- if (me->export_type == JS_EXPORT_TYPE_LOCAL) {- idx = find_closure_var(ctx, fd, me->local_name);- if (idx < 0) {- JS_ThrowSyntaxErrorAtom(ctx, "exported variable '%s' does not exist",- me->local_name);- return -1;- }- me->u.local.var_idx = idx;- }- }- return 0;-}--/* create a function object from a function definition. The function- definition is freed. All the child functions are also created. It- must be done this way to resolve all the variables. */-static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)-{- JSValue func_obj;- JSFunctionBytecode *b;- struct list_head *el, *el1;- int stack_size, scope, idx;- int function_size, byte_code_offset, cpool_offset;- int closure_var_offset, vardefs_offset;-- /* recompute scope linkage */- for (scope = 0; scope < fd->scope_count; scope++) {- fd->scopes[scope].first = -1;- }- for (idx = 0; idx < fd->var_count; idx++) {- JSVarDef *vd = &fd->vars[idx];- vd->scope_next = fd->scopes[vd->scope_level].first;- fd->scopes[vd->scope_level].first = idx;- }- for (scope = 2; scope < fd->scope_count; scope++) {- JSVarScope *sd = &fd->scopes[scope];- if (sd->first == -1)- sd->first = fd->scopes[sd->parent].first;- }- for (idx = 0; idx < fd->var_count; idx++) {- JSVarDef *vd = &fd->vars[idx];- if (vd->scope_next == -1 && vd->scope_level > 1) {- scope = fd->scopes[vd->scope_level].parent;- vd->scope_next = fd->scopes[scope].first;- }- }-- /* if the function contains an eval call, the closure variables- are used to compile the eval and they must be ordered by scope,- so it is necessary to create the closure variables before any- other variable lookup is done. */- if (fd->has_eval_call)- add_eval_variables(ctx, fd);-- /* add the module global variables in the closure */- if (fd->module) {- if (add_module_variables(ctx, fd))- goto fail;- }-- /* first create all the child functions */- list_for_each_safe(el, el1, &fd->child_list) {- JSFunctionDef *fd1;- int cpool_idx;-- fd1 = list_entry(el, JSFunctionDef, link);- cpool_idx = fd1->parent_cpool_idx;- func_obj = js_create_function(ctx, fd1);- if (JS_IsException(func_obj))- goto fail;- /* save it in the constant pool */- assert(cpool_idx >= 0);- fd->cpool[cpool_idx] = func_obj;- }--#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 4)- if (!(fd->js_mode & JS_MODE_STRIP)) {- printf("pass 1\n");- dump_byte_code(ctx, 1, fd->byte_code.buf, fd->byte_code.size,- fd->args, fd->arg_count, fd->vars, fd->var_count,- fd->closure_var, fd->closure_var_count,- fd->cpool, fd->cpool_count, fd->source, fd->line_num,- fd->label_slots, NULL);- printf("\n");- }-#endif-- if (resolve_variables(ctx, fd))- goto fail;--#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 2)- if (!(fd->js_mode & JS_MODE_STRIP)) {- printf("pass 2\n");- dump_byte_code(ctx, 2, fd->byte_code.buf, fd->byte_code.size,- fd->args, fd->arg_count, fd->vars, fd->var_count,- fd->closure_var, fd->closure_var_count,- fd->cpool, fd->cpool_count, fd->source, fd->line_num,- fd->label_slots, NULL);- printf("\n");- }-#endif-- if (resolve_labels(ctx, fd))- goto fail;-- if (compute_stack_size(ctx, fd, &stack_size) < 0)- goto fail;-- if (fd->js_mode & JS_MODE_STRIP) {- function_size = offsetof(JSFunctionBytecode, debug);- } else {- function_size = sizeof(*b);- }- cpool_offset = function_size;- function_size += fd->cpool_count * sizeof(*fd->cpool);- vardefs_offset = function_size;- if (!(fd->js_mode & JS_MODE_STRIP) || fd->has_eval_call) {- function_size += (fd->arg_count + fd->var_count) * sizeof(*b->vardefs);- }- closure_var_offset = function_size;- function_size += fd->closure_var_count * sizeof(*fd->closure_var);- byte_code_offset = function_size;- function_size += fd->byte_code.size;-- b = js_mallocz(ctx, function_size);- if (!b)- goto fail;- b->header.ref_count = 1;-- b->byte_code_buf = (void *)((uint8_t*)b + byte_code_offset);- b->byte_code_len = fd->byte_code.size;- memcpy(b->byte_code_buf, fd->byte_code.buf, fd->byte_code.size);- js_free(ctx, fd->byte_code.buf);- fd->byte_code.buf = NULL;-- b->func_name = fd->func_name;- if (fd->arg_count + fd->var_count > 0) {- if ((fd->js_mode & JS_MODE_STRIP) && !fd->has_eval_call) {- /* Strip variable definitions not needed at runtime */- int i;- for(i = 0; i < fd->var_count; i++) {- JS_FreeAtom(ctx, fd->vars[i].var_name);- }- for(i = 0; i < fd->arg_count; i++) {- JS_FreeAtom(ctx, fd->args[i].var_name);- }- for(i = 0; i < fd->closure_var_count; i++) {- JS_FreeAtom(ctx, fd->closure_var[i].var_name);- fd->closure_var[i].var_name = JS_ATOM_NULL;- }- } else {- b->vardefs = (void *)((uint8_t*)b + vardefs_offset);- memcpy(b->vardefs, fd->args, fd->arg_count * sizeof(fd->args[0]));- memcpy(b->vardefs + fd->arg_count, fd->vars, fd->var_count * sizeof(fd->vars[0]));- }- b->var_count = fd->var_count;- b->arg_count = fd->arg_count;- b->defined_arg_count = fd->defined_arg_count;- js_free(ctx, fd->args);- js_free(ctx, fd->vars);- }- b->cpool_count = fd->cpool_count;- if (b->cpool_count) {- b->cpool = (void *)((uint8_t*)b + cpool_offset);- memcpy(b->cpool, fd->cpool, b->cpool_count * sizeof(*b->cpool));- }- js_free(ctx, fd->cpool);- fd->cpool = NULL;-- b->stack_size = stack_size;-- if (fd->js_mode & JS_MODE_STRIP) {- JS_FreeAtom(ctx, fd->filename);- dbuf_free(&fd->pc2line); // probably useless- } else {- /* XXX: source and pc2line info should be packed at the end of the- JSFunctionBytecode structure, avoiding allocation overhead- */- b->has_debug = 1;- b->debug.filename = fd->filename;- b->debug.line_num = fd->line_num;-- //DynBuf pc2line;- //compute_pc2line_info(fd, &pc2line);- //js_free(ctx, fd->line_number_slots)- b->debug.pc2line_buf = js_realloc(ctx, fd->pc2line.buf, fd->pc2line.size);- if (!b->debug.pc2line_buf)- b->debug.pc2line_buf = fd->pc2line.buf;- b->debug.pc2line_len = fd->pc2line.size;- b->debug.source = fd->source;- b->debug.source_len = fd->source_len;- }- if (fd->scopes != fd->def_scope_array)- js_free(ctx, fd->scopes);-- b->closure_var_count = fd->closure_var_count;- if (b->closure_var_count) {- b->closure_var = (void *)((uint8_t*)b + closure_var_offset);- memcpy(b->closure_var, fd->closure_var, b->closure_var_count * sizeof(*b->closure_var));- }- js_free(ctx, fd->closure_var);- fd->closure_var = NULL;-- b->has_prototype = fd->has_prototype;- b->has_simple_parameter_list = fd->has_simple_parameter_list;- b->js_mode = fd->js_mode;- b->is_derived_class_constructor = fd->is_derived_class_constructor;- b->func_kind = fd->func_kind;- b->need_home_object = (fd->home_object_var_idx >= 0 ||- fd->need_home_object);- b->new_target_allowed = fd->new_target_allowed;- b->super_call_allowed = fd->super_call_allowed;- b->super_allowed = fd->super_allowed;- b->arguments_allowed = fd->arguments_allowed;- b->backtrace_barrier = fd->backtrace_barrier;- b->realm = JS_DupContext(ctx);-- add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);- -#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 1)- if (!(fd->js_mode & JS_MODE_STRIP)) {- js_dump_function_bytecode(ctx, b);- }-#endif-- if (fd->parent) {- /* remove from parent list */- list_del(&fd->link);- }-- js_free(ctx, fd);- return JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b);- fail:- js_free_function_def(ctx, fd);- return JS_EXCEPTION;-}--static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b)-{- int i;--#if 0- {- char buf[ATOM_GET_STR_BUF_SIZE];- printf("freeing %s\n",- JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));- }-#endif- free_bytecode_atoms(rt, b->byte_code_buf, b->byte_code_len, TRUE);-- if (b->vardefs) {- for(i = 0; i < b->arg_count + b->var_count; i++) {- JS_FreeAtomRT(rt, b->vardefs[i].var_name);- }- }- for(i = 0; i < b->cpool_count; i++)- JS_FreeValueRT(rt, b->cpool[i]);-- for(i = 0; i < b->closure_var_count; i++) {- JSClosureVar *cv = &b->closure_var[i];- JS_FreeAtomRT(rt, cv->var_name);- }- if (b->realm)- JS_FreeContext(b->realm);-- JS_FreeAtomRT(rt, b->func_name);- if (b->has_debug) {- JS_FreeAtomRT(rt, b->debug.filename);- js_free_rt(rt, b->debug.pc2line_buf);- js_free_rt(rt, b->debug.source);- }-- remove_gc_object(&b->header);- if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && b->header.ref_count != 0) {- list_add_tail(&b->header.link, &rt->gc_zero_ref_count_list);- } else {- js_free_rt(rt, b);- }-}--static __exception int js_parse_directives(JSParseState *s)-{- char str[20];- JSParsePos pos;- BOOL has_semi;-- if (s->token.val != TOK_STRING)- return 0;-- js_parse_get_pos(s, &pos);-- while(s->token.val == TOK_STRING) {- /* Copy actual source string representation */- snprintf(str, sizeof str, "%.*s",- (int)(s->buf_ptr - s->token.ptr - 2), s->token.ptr + 1);-- if (next_token(s))- return -1;-- has_semi = FALSE;- switch (s->token.val) {- case ';':- if (next_token(s))- return -1;- has_semi = TRUE;- break;- case '}':- case TOK_EOF:- has_semi = TRUE;- break;- case TOK_NUMBER:- case TOK_STRING:- case TOK_TEMPLATE:- case TOK_IDENT:- case TOK_REGEXP:- case TOK_DEC:- case TOK_INC:- case TOK_NULL:- case TOK_FALSE:- case TOK_TRUE:- case TOK_IF:- case TOK_RETURN:- case TOK_VAR:- case TOK_THIS:- case TOK_DELETE:- case TOK_TYPEOF:- case TOK_NEW:- case TOK_DO:- case TOK_WHILE:- case TOK_FOR:- case TOK_SWITCH:- case TOK_THROW:- case TOK_TRY:- case TOK_FUNCTION:- case TOK_DEBUGGER:- case TOK_WITH:- case TOK_CLASS:- case TOK_CONST:- case TOK_ENUM:- case TOK_EXPORT:- case TOK_IMPORT:- case TOK_SUPER:- case TOK_INTERFACE:- case TOK_LET:- case TOK_PACKAGE:- case TOK_PRIVATE:- case TOK_PROTECTED:- case TOK_PUBLIC:- case TOK_STATIC:- /* automatic insertion of ';' */- if (s->got_lf)- has_semi = TRUE;- break;- default:- break;- }- if (!has_semi)- break;- if (!strcmp(str, "use strict")) {- s->cur_func->has_use_strict = TRUE;- s->cur_func->js_mode |= JS_MODE_STRICT;- }-#if !defined(DUMP_BYTECODE) || !(DUMP_BYTECODE & 8)- else if (!strcmp(str, "use strip")) {- s->cur_func->js_mode |= JS_MODE_STRIP;- }-#endif-#ifdef CONFIG_BIGNUM- else if (s->ctx->bignum_ext && !strcmp(str, "use math")) {- s->cur_func->js_mode |= JS_MODE_MATH;- }-#endif- }- return js_parse_seek_token(s, &pos);-}--static int js_parse_function_check_names(JSParseState *s, JSFunctionDef *fd,- JSAtom func_name)-{- JSAtom name;- int i, idx;-- if (fd->js_mode & JS_MODE_STRICT) {- if (!fd->has_simple_parameter_list && fd->has_use_strict) {- return js_parse_error(s, "\"use strict\" not allowed in function with default or destructuring parameter");- }- if (func_name == JS_ATOM_eval || func_name == JS_ATOM_arguments) {- return js_parse_error(s, "invalid function name in strict code");- }- for (idx = 0; idx < fd->arg_count; idx++) {- name = fd->args[idx].var_name;-- if (name == JS_ATOM_eval || name == JS_ATOM_arguments) {- return js_parse_error(s, "invalid argument name in strict code");- }- }- }- /* check async_generator case */- if ((fd->js_mode & JS_MODE_STRICT)- || !fd->has_simple_parameter_list- || (fd->func_type == JS_PARSE_FUNC_METHOD && fd->func_kind == JS_FUNC_ASYNC)- || fd->func_type == JS_PARSE_FUNC_ARROW- || fd->func_type == JS_PARSE_FUNC_METHOD) {- for (idx = 0; idx < fd->arg_count; idx++) {- name = fd->args[idx].var_name;- if (name != JS_ATOM_NULL) {- for (i = 0; i < idx; i++) {- if (fd->args[i].var_name == name)- goto duplicate;- }- /* Check if argument name duplicates a destructuring parameter */- /* XXX: should have a flag for such variables */- for (i = 0; i < fd->var_count; i++) {- if (fd->vars[i].var_name == name)- goto duplicate;- }- }- }- }- return 0;--duplicate:- return js_parse_error(s, "duplicate argument names not allowed in this context");-}--/* create a function to initialize class fields */-static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s)-{- JSFunctionDef *fd;- - fd = js_new_function_def(s->ctx, s->cur_func, FALSE, FALSE,- s->filename, 0);- if (!fd)- return NULL;- fd->func_name = JS_ATOM_NULL;- fd->has_prototype = FALSE;- fd->has_home_object = TRUE;- - fd->has_arguments_binding = FALSE;- fd->has_this_binding = TRUE;- fd->is_derived_class_constructor = FALSE;- fd->new_target_allowed = TRUE;- fd->super_call_allowed = FALSE;- fd->super_allowed = fd->has_home_object;- fd->arguments_allowed = FALSE;- - fd->func_kind = JS_FUNC_NORMAL;- fd->func_type = JS_PARSE_FUNC_METHOD;- return fd;-}--/* func_name must be JS_ATOM_NULL for JS_PARSE_FUNC_STATEMENT and- JS_PARSE_FUNC_EXPR, JS_PARSE_FUNC_ARROW and JS_PARSE_FUNC_VAR */-static __exception int js_parse_function_decl2(JSParseState *s,- JSParseFunctionEnum func_type,- JSFunctionKindEnum func_kind,- JSAtom func_name,- const uint8_t *ptr,- int function_line_num,- JSParseExportEnum export_flag,- JSFunctionDef **pfd)-{- JSContext *ctx = s->ctx;- JSFunctionDef *fd = s->cur_func;- BOOL is_expr;- int func_idx, lexical_func_idx = -1;- BOOL has_opt_arg;- BOOL create_func_var = FALSE;-- is_expr = (func_type != JS_PARSE_FUNC_STATEMENT &&- func_type != JS_PARSE_FUNC_VAR);-- if (func_type == JS_PARSE_FUNC_STATEMENT ||- func_type == JS_PARSE_FUNC_VAR ||- func_type == JS_PARSE_FUNC_EXPR) {- if (func_kind == JS_FUNC_NORMAL &&- token_is_pseudo_keyword(s, JS_ATOM_async) &&- peek_token(s, TRUE) != '\n') {- if (next_token(s))- return -1;- func_kind = JS_FUNC_ASYNC;- }- if (next_token(s))- return -1;- if (s->token.val == '*') {- if (next_token(s))- return -1;- func_kind |= JS_FUNC_GENERATOR;- }-- if (s->token.val == TOK_IDENT) {- if (s->token.u.ident.is_reserved ||- (s->token.u.ident.atom == JS_ATOM_yield &&- func_type == JS_PARSE_FUNC_EXPR &&- (func_kind & JS_FUNC_GENERATOR)) ||- (s->token.u.ident.atom == JS_ATOM_await &&- func_type == JS_PARSE_FUNC_EXPR &&- (func_kind & JS_FUNC_ASYNC))) {- return js_parse_error_reserved_identifier(s);- }- }- if (s->token.val == TOK_IDENT ||- (((s->token.val == TOK_YIELD && !(fd->js_mode & JS_MODE_STRICT)) ||- (s->token.val == TOK_AWAIT && !s->is_module)) &&- func_type == JS_PARSE_FUNC_EXPR)) {- func_name = JS_DupAtom(ctx, s->token.u.ident.atom);- if (next_token(s)) {- JS_FreeAtom(ctx, func_name);- return -1;- }- } else {- if (func_type != JS_PARSE_FUNC_EXPR &&- export_flag != JS_PARSE_EXPORT_DEFAULT) {- return js_parse_error(s, "function name expected");- }- }- } else if (func_type != JS_PARSE_FUNC_ARROW) {- func_name = JS_DupAtom(ctx, func_name);- }-- if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_MODULE &&- (func_type == JS_PARSE_FUNC_STATEMENT || func_type == JS_PARSE_FUNC_VAR)) {- JSHoistedDef *hf;- hf = find_hoisted_def(fd, func_name);- /* XXX: should check scope chain */- if (hf && hf->scope_level == fd->scope_level) {- js_parse_error(s, "invalid redefinition of global identifier in module code");- JS_FreeAtom(ctx, func_name);- return -1;- }- }-- if (func_type == JS_PARSE_FUNC_VAR) {- /* Create lexical name here so function closure contains it */- if (!(fd->js_mode & JS_MODE_STRICT)- && find_lexical_decl(ctx, fd, func_name, fd->scope_first, FALSE) < 0- && !((func_idx = find_var(ctx, fd, func_name)) >= 0 && (func_idx & ARGUMENT_VAR_OFFSET))- && !(func_name == JS_ATOM_arguments && fd->has_arguments_binding)) {- create_func_var = TRUE;- }- if (fd->is_eval &&- (fd->eval_type == JS_EVAL_TYPE_GLOBAL ||- fd->eval_type == JS_EVAL_TYPE_MODULE) &&- fd->scope_level == 1) {- /* avoid creating a lexical variable in the global- scope. XXX: check annex B */- JSHoistedDef *hf;- hf = find_hoisted_def(fd, func_name);- /* XXX: should check scope chain */- if (hf && hf->scope_level == fd->scope_level) {- js_parse_error(s, "invalid redefinition of global identifier");- JS_FreeAtom(ctx, func_name);- return -1;- }- } else {- /* Always create a lexical name, fail if at the same scope as- existing name */- /* Lexical variable will be initialized upon entering scope */- lexical_func_idx = define_var(s, fd, func_name,- func_kind != JS_FUNC_NORMAL ?- JS_VAR_DEF_NEW_FUNCTION_DECL :- JS_VAR_DEF_FUNCTION_DECL);- if (lexical_func_idx < 0) {- JS_FreeAtom(ctx, func_name);- return -1;- }- }- }-- fd = js_new_function_def(ctx, fd, FALSE, is_expr,- s->filename, function_line_num);- if (!fd) {- JS_FreeAtom(ctx, func_name);- return -1;- }- if (pfd)- *pfd = fd;- s->cur_func = fd;- fd->func_name = func_name;- /* XXX: test !fd->is_generator is always false */- fd->has_prototype = (func_type == JS_PARSE_FUNC_STATEMENT ||- func_type == JS_PARSE_FUNC_VAR ||- func_type == JS_PARSE_FUNC_EXPR) &&- func_kind == JS_FUNC_NORMAL;- fd->has_home_object = (func_type == JS_PARSE_FUNC_METHOD ||- func_type == JS_PARSE_FUNC_GETTER ||- func_type == JS_PARSE_FUNC_SETTER ||- func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR ||- func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR);- fd->has_arguments_binding = (func_type != JS_PARSE_FUNC_ARROW);- fd->has_this_binding = fd->has_arguments_binding;- fd->is_derived_class_constructor = (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR);- if (func_type == JS_PARSE_FUNC_ARROW) {- fd->new_target_allowed = fd->parent->new_target_allowed;- fd->super_call_allowed = fd->parent->super_call_allowed;- fd->super_allowed = fd->parent->super_allowed;- fd->arguments_allowed = fd->parent->arguments_allowed;- } else {- fd->new_target_allowed = TRUE;- fd->super_call_allowed = fd->is_derived_class_constructor;- fd->super_allowed = fd->has_home_object;- fd->arguments_allowed = TRUE;- }-- /* fd->in_function_body == FALSE prevents yield/await during the parsing- of the arguments in generator/async functions. They are parsed as- regular identifiers for other function kinds. */- fd->func_kind = func_kind;- fd->func_type = func_type;-- if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR ||- func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) {- /* error if not invoked as a constructor */- emit_op(s, OP_check_ctor);- }-- if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) {- emit_class_field_init(s);- }- - /* parse arguments */- fd->has_simple_parameter_list = TRUE;- has_opt_arg = FALSE;- if (func_type == JS_PARSE_FUNC_ARROW && s->token.val == TOK_IDENT) {- JSAtom name;- if (s->token.u.ident.is_reserved) {- js_parse_error_reserved_identifier(s);- goto fail;- }- name = s->token.u.ident.atom;- if (add_arg(ctx, fd, name) < 0)- goto fail;- fd->defined_arg_count = 1;- } else {- if (js_parse_expect(s, '('))- goto fail;-- while (s->token.val != ')') {- JSAtom name;- BOOL rest = FALSE;- int idx;-- if (s->token.val == TOK_ELLIPSIS) {- fd->has_simple_parameter_list = FALSE;- rest = TRUE;- if (next_token(s))- goto fail;- }- if (s->token.val == '[' || s->token.val == '{') {- fd->has_simple_parameter_list = FALSE;- if (rest) {- emit_op(s, OP_rest);- emit_u16(s, fd->arg_count);- } else {- /* unnamed arg for destructuring */- idx = add_arg(ctx, fd, JS_ATOM_NULL);- emit_op(s, OP_get_arg);- emit_u16(s, idx);- }- if (js_parse_destructuring_element(s, TOK_VAR, 1, TRUE, -1, TRUE))- goto fail;- } else if (s->token.val == TOK_IDENT) {- if (s->token.u.ident.is_reserved) {- js_parse_error_reserved_identifier(s);- goto fail;- }- name = s->token.u.ident.atom;- if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) {- js_parse_error_reserved_identifier(s);- goto fail;- }- idx = add_arg(ctx, fd, name);- if (idx < 0)- goto fail;- if (next_token(s))- goto fail;- if (rest) {- emit_op(s, OP_rest);- emit_u16(s, idx);- emit_op(s, OP_put_arg);- emit_u16(s, idx);- fd->has_simple_parameter_list = FALSE;- has_opt_arg = TRUE;- } else if (s->token.val == '=') {- fd->has_simple_parameter_list = FALSE;- has_opt_arg = TRUE;-- if (next_token(s))- goto fail;-- /* optimize `x = void 0` default value: no code needed */- if (s->token.val == TOK_VOID) {- JSParsePos pos;- js_parse_get_pos(s, &pos);- if (next_token(s))- goto fail;- if (s->token.val == TOK_NUMBER) {- if (next_token(s))- goto fail;- if (s->token.val == ',') {- if (next_token(s))- goto fail;- continue;- }- if (s->token.val == ')') {- continue;- }- }- if (js_parse_seek_token(s, &pos))- goto fail;- }-#if 0- /* XXX: not correct for eval code */- /* Check for a default value of `undefined`- to omit default argument processing */- if (s->token.val == TOK_IDENT &&- s->token.u.ident.atom == JS_ATOM_undefined &&- fd->parent == NULL &&- ((tok = peek_token(s, FALSE)) == ',' || tok == ')')) {- if (next_token(s)) /* ignore undefined token */- goto fail;- } else-#endif- {- int label = new_label(s);- if (idx > 0) {- emit_op(s, OP_set_arg_valid_upto);- emit_u16(s, idx);- }- emit_op(s, OP_get_arg);- emit_u16(s, idx);- emit_op(s, OP_undefined);- emit_op(s, OP_strict_eq);- emit_goto(s, OP_if_false, label);- if (js_parse_assign_expr(s, TRUE))- goto fail;- set_object_name(s, name);- emit_op(s, OP_put_arg);- emit_u16(s, idx);- emit_label(s, label);- }- } else if (!has_opt_arg) {- fd->defined_arg_count++;- }- } else {- js_parse_error(s, "missing formal parameter");- goto fail;- }- if (rest && s->token.val != ')') {- js_parse_expect(s, ')');- goto fail;- }- if (s->token.val == ')')- break;- if (js_parse_expect(s, ','))- goto fail;- }- if ((func_type == JS_PARSE_FUNC_GETTER && fd->arg_count != 0) ||- (func_type == JS_PARSE_FUNC_SETTER && fd->arg_count != 1)) {- js_parse_error(s, "invalid number of arguments for getter or setter");- goto fail;- }- }-- if (next_token(s))- goto fail;-- /* generator function: yield after the parameters are evaluated */- if (func_kind == JS_FUNC_GENERATOR ||- func_kind == JS_FUNC_ASYNC_GENERATOR)- emit_op(s, OP_initial_yield);-- /* in generators, yield expression is forbidden during the parsing- of the arguments */- fd->in_function_body = TRUE;- push_scope(s); /* enter body scope: fd->scope_level = 1 */-- if (s->token.val == TOK_ARROW) {- if (next_token(s))- goto fail;-- if (s->token.val != '{') {- if (js_parse_function_check_names(s, fd, func_name))- goto fail;-- if (js_parse_assign_expr(s, TRUE))- goto fail;-- if (func_kind != JS_FUNC_NORMAL)- emit_op(s, OP_return_async);- else- emit_op(s, OP_return);-- if (!(fd->js_mode & JS_MODE_STRIP)) {- /* save the function source code */- /* the end of the function source code is after the last- token of the function source stored into s->last_ptr */- fd->source_len = s->last_ptr - ptr;- fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len);- if (!fd->source)- goto fail;- }- goto done;- }- }-- if (js_parse_expect(s, '{'))- goto fail;-- if (js_parse_directives(s))- goto fail;-- /* in strict_mode, check function and argument names */- if (js_parse_function_check_names(s, fd, func_name))- goto fail;-- while (s->token.val != '}') {- if (js_parse_source_element(s))- goto fail;- }- if (!(fd->js_mode & JS_MODE_STRIP)) {- /* save the function source code */- fd->source_len = s->buf_ptr - ptr;- fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len);- if (!fd->source)- goto fail;- }-- if (next_token(s)) {- /* consume the '}' */- goto fail;- }-- /* in case there is no return, add one */- if (js_is_live_code(s)) {- emit_return(s, FALSE);- }-done:- s->cur_func = fd->parent;-- /* create the function object */- {- int idx;- JSAtom func_name = fd->func_name;-- /* the real object will be set at the end of the compilation */- idx = cpool_add(s, JS_NULL);- fd->parent_cpool_idx = idx;-- if (is_expr) {- /* for constructors, no code needs to be generated here */- if (func_type != JS_PARSE_FUNC_CLASS_CONSTRUCTOR &&- func_type != JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) {- /* OP_fclosure creates the function object from the bytecode- and adds the scope information */- emit_op(s, OP_fclosure);- emit_u32(s, idx);- if (func_name == JS_ATOM_NULL) {- emit_op(s, OP_set_name);- emit_u32(s, JS_ATOM_NULL);- }- }- } else if (func_type == JS_PARSE_FUNC_VAR) {- emit_op(s, OP_fclosure);- emit_u32(s, idx);- if (create_func_var) {- if (s->cur_func->is_global_var) {- JSHoistedDef *hf;- /* the global variable must be defined at the start of the- function */- hf = add_hoisted_def(ctx, s->cur_func, -1, func_name, -1, FALSE);- if (!hf)- goto fail;- /* it is considered as defined at the top level- (needed for annex B.3.3.4 and B.3.3.5- checks) */- hf->scope_level = 0; - hf->force_init = ((s->cur_func->js_mode & JS_MODE_STRICT) != 0);- /* store directly into global var, bypass lexical scope */- emit_op(s, OP_dup);- emit_op(s, OP_scope_put_var);- emit_atom(s, func_name);- emit_u16(s, 0);- } else {- /* do not call define_var to bypass lexical scope check */- func_idx = find_var(ctx, s->cur_func, func_name);- if (func_idx < 0) {- func_idx = add_var(ctx, s->cur_func, func_name);- if (func_idx < 0)- goto fail;- }- /* store directly into local var, bypass lexical catch scope */- emit_op(s, OP_dup);- emit_op(s, OP_scope_put_var);- emit_atom(s, func_name);- emit_u16(s, 0);- }- }- if (lexical_func_idx >= 0) {- /* lexical variable will be initialized upon entering scope */- s->cur_func->vars[lexical_func_idx].func_pool_or_scope_idx = idx;- emit_op(s, OP_drop);- } else {- /* store function object into its lexical name */- /* XXX: could use OP_put_loc directly */- emit_op(s, OP_scope_put_var_init);- emit_atom(s, func_name);- emit_u16(s, s->cur_func->scope_level);- }- } else {- if (!s->cur_func->is_global_var) {- int var_idx = define_var(s, s->cur_func, func_name, JS_VAR_DEF_VAR);-- if (var_idx < 0)- goto fail;- /* the variable will be assigned at the top of the function */- if (!add_hoisted_def(ctx, s->cur_func, idx, JS_ATOM_NULL, var_idx, FALSE))- goto fail;- } else {- JSAtom func_var_name;- if (func_name == JS_ATOM_NULL)- func_var_name = JS_ATOM__default_; /* export default */- else- func_var_name = func_name;- /* the variable will be assigned at the top of the function */- if (!add_hoisted_def(ctx, s->cur_func, idx, func_var_name, -1, FALSE))- goto fail;- if (export_flag != JS_PARSE_EXPORT_NONE) {- if (!add_export_entry(s, s->cur_func->module, func_var_name,- export_flag == JS_PARSE_EXPORT_NAMED ? func_var_name : JS_ATOM_default, JS_EXPORT_TYPE_LOCAL))- goto fail;- }- }- }- }- return 0;- fail:- s->cur_func = fd->parent;- js_free_function_def(ctx, fd);- if (pfd)- *pfd = NULL;- return -1;-}--static __exception int js_parse_function_decl(JSParseState *s,- JSParseFunctionEnum func_type,- JSFunctionKindEnum func_kind,- JSAtom func_name,- const uint8_t *ptr,- int function_line_num)-{- return js_parse_function_decl2(s, func_type, func_kind, func_name, ptr,- function_line_num, JS_PARSE_EXPORT_NONE,- NULL);-}--static __exception int js_parse_program(JSParseState *s)-{- JSFunctionDef *fd = s->cur_func;- int idx;-- if (next_token(s))- return -1;-- if (js_parse_directives(s))- return -1;-- fd->is_global_var = (fd->eval_type == JS_EVAL_TYPE_GLOBAL) ||- (fd->eval_type == JS_EVAL_TYPE_MODULE) ||- !(fd->js_mode & JS_MODE_STRICT);-- if (!s->is_module) {- /* hidden variable for the return value */- fd->eval_ret_idx = idx = add_var(s->ctx, fd, JS_ATOM__ret_);- if (idx < 0)- return -1;- }-- while (s->token.val != TOK_EOF) {- if (js_parse_source_element(s))- return -1;- }-- if (!s->is_module) {- /* return the value of the hidden variable eval_ret_idx */- emit_op(s, OP_get_loc);- emit_u16(s, fd->eval_ret_idx);-- emit_op(s, OP_return);- } else {- emit_op(s, OP_return_undef);- }-- return 0;-}--static void js_parse_init(JSContext *ctx, JSParseState *s,- const char *input, size_t input_len,- const char *filename)-{- memset(s, 0, sizeof(*s));- s->ctx = ctx;- s->filename = filename;- s->line_num = 1;- s->buf_ptr = (const uint8_t *)input;- s->buf_end = s->buf_ptr + input_len;- s->token.val = ' ';- s->token.line_num = 1;-}--static JSValue JS_EvalFunctionInternal(JSContext *ctx, JSValue fun_obj,- JSValueConst this_obj,- JSVarRef **var_refs, JSStackFrame *sf)-{- JSValue ret_val;- uint32_t tag;-- tag = JS_VALUE_GET_TAG(fun_obj);- if (tag == JS_TAG_FUNCTION_BYTECODE) {- fun_obj = js_closure(ctx, fun_obj, var_refs, sf);- ret_val = JS_CallFree(ctx, fun_obj, this_obj, 0, NULL);- } else if (tag == JS_TAG_MODULE) {- JSModuleDef *m;- m = JS_VALUE_GET_PTR(fun_obj);- /* the module refcount should be >= 2 */- JS_FreeValue(ctx, fun_obj);- if (js_create_module_function(ctx, m) < 0)- goto fail;- if (js_instantiate_module(ctx, m) < 0)- goto fail;- ret_val = js_evaluate_module(ctx, m);- if (JS_IsException(ret_val)) {- fail:- js_free_modules(ctx, JS_FREE_MODULE_NOT_EVALUATED);- return JS_EXCEPTION;- }- } else {- JS_FreeValue(ctx, fun_obj);- ret_val = JS_ThrowTypeError(ctx, "bytecode function expected");- }- return ret_val;-}--JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj)-{- return JS_EvalFunctionInternal(ctx, fun_obj, ctx->global_obj, NULL, NULL);-}--static void skip_shebang(JSParseState *s)-{- const uint8_t *p = s->buf_ptr;- int c;-- if (p[0] == '#' && p[1] == '!') {- p += 2;- while (p < s->buf_end) {- if (*p == '\n' || *p == '\r') {- break;- } else if (*p >= 0x80) {- c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);- if (c == CP_LS || c == CP_PS) {- break;- } else if (c == -1) {- p++; /* skip invalid UTF-8 */- }- } else {- p++;- }- }- s->buf_ptr = p;- }-}--/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */-static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,- const char *input, size_t input_len,- const char *filename, int flags, int scope_idx)-{- JSParseState s1, *s = &s1;- int err, js_mode, eval_type;- JSValue fun_obj, ret_val;- JSStackFrame *sf;- JSVarRef **var_refs;- JSFunctionBytecode *b;- JSFunctionDef *fd;- JSModuleDef *m;-- js_parse_init(ctx, s, input, input_len, filename);- skip_shebang(s);-- eval_type = flags & JS_EVAL_TYPE_MASK;- m = NULL;- if (eval_type == JS_EVAL_TYPE_DIRECT) {- JSObject *p;- sf = ctx->rt->current_stack_frame;- assert(sf != NULL);- assert(JS_VALUE_GET_TAG(sf->cur_func) == JS_TAG_OBJECT);- p = JS_VALUE_GET_OBJ(sf->cur_func);- assert(js_class_has_bytecode(p->class_id));- b = p->u.func.function_bytecode;- var_refs = p->u.func.var_refs;- js_mode = b->js_mode;- } else {- sf = NULL;- b = NULL;- var_refs = NULL;- js_mode = 0;- if (flags & JS_EVAL_FLAG_STRICT)- js_mode |= JS_MODE_STRICT;- if (flags & JS_EVAL_FLAG_STRIP)- js_mode |= JS_MODE_STRIP;- if (eval_type == JS_EVAL_TYPE_MODULE) {- JSAtom module_name = JS_NewAtom(ctx, filename);- if (module_name == JS_ATOM_NULL)- return JS_EXCEPTION;- m = js_new_module_def(ctx, module_name);- if (!m)- return JS_EXCEPTION;- js_mode |= JS_MODE_STRICT;- }- }- fd = js_new_function_def(ctx, NULL, TRUE, FALSE, filename, 1);- if (!fd)- goto fail1;- s->cur_func = fd;- fd->eval_type = eval_type;- fd->has_this_binding = (eval_type != JS_EVAL_TYPE_DIRECT);- fd->backtrace_barrier = ((flags & JS_EVAL_FLAG_BACKTRACE_BARRIER) != 0);- if (eval_type == JS_EVAL_TYPE_DIRECT) {- fd->new_target_allowed = b->new_target_allowed;- fd->super_call_allowed = b->super_call_allowed;- fd->super_allowed = b->super_allowed;- fd->arguments_allowed = b->arguments_allowed;- } else {- fd->new_target_allowed = FALSE;- fd->super_call_allowed = FALSE;- fd->super_allowed = FALSE;- fd->arguments_allowed = TRUE;- }- fd->js_mode = js_mode;- fd->func_name = JS_DupAtom(ctx, JS_ATOM__eval_);- if (b) {- if (add_closure_variables(ctx, fd, b, scope_idx))- goto fail;- }- fd->module = m;- s->is_module = (m != NULL);- s->allow_html_comments = !s->is_module;-- push_scope(s); /* body scope */-- err = js_parse_program(s);- if (err) {- fail:- free_token(s, &s->token);- js_free_function_def(ctx, fd);- goto fail1;- }-- /* create the function object and all the enclosed functions */- fun_obj = js_create_function(ctx, fd);- if (JS_IsException(fun_obj))- goto fail1;- /* Could add a flag to avoid resolution if necessary */- if (m) {- m->func_obj = fun_obj;- if (js_resolve_module(ctx, m) < 0)- goto fail1;- fun_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));- }- if (flags & JS_EVAL_FLAG_COMPILE_ONLY) {- ret_val = fun_obj;- } else {- ret_val = JS_EvalFunctionInternal(ctx, fun_obj, this_obj, var_refs, sf);- }- return ret_val;- fail1:- /* XXX: should free all the unresolved dependencies */- if (m)- js_free_module_def(ctx, m);- return JS_EXCEPTION;-}--/* the indirection is needed to make 'eval' optional */-static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,- const char *input, size_t input_len,- const char *filename, int flags, int scope_idx)-{- if (unlikely(!ctx->eval_internal)) {- return JS_ThrowTypeError(ctx, "eval is not supported");- }- return ctx->eval_internal(ctx, this_obj, input, input_len, filename,- flags, scope_idx);-}--static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,- JSValueConst val, int flags, int scope_idx)-{- JSValue ret;- const char *str;- size_t len;-- if (!JS_IsString(val))- return JS_DupValue(ctx, val);- str = JS_ToCStringLen(ctx, &len, val);- if (!str)- return JS_EXCEPTION;- ret = JS_EvalInternal(ctx, this_obj, str, len, "<input>", flags, scope_idx);- JS_FreeCString(ctx, str);- return ret;--}--JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len,- const char *filename, int eval_flags)-{- int eval_type = eval_flags & JS_EVAL_TYPE_MASK;- JSValue ret;-- assert(eval_type == JS_EVAL_TYPE_GLOBAL ||- eval_type == JS_EVAL_TYPE_MODULE);- ret = JS_EvalInternal(ctx, ctx->global_obj, input, input_len, filename,- eval_flags, -1);- return ret;-}--int JS_ResolveModule(JSContext *ctx, JSValueConst obj)-{- if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) {- JSModuleDef *m = JS_VALUE_GET_PTR(obj);- if (js_resolve_module(ctx, m) < 0) {- js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED);- return -1;- }- }- return 0;-}--/*******************************************************************/-/* object list */--typedef struct {- JSObject *obj;- uint32_t hash_next; /* -1 if no next entry */-} JSObjectListEntry;--/* XXX: reuse it to optimize weak references */-typedef struct {- JSObjectListEntry *object_tab;- int object_count;- int object_size;- uint32_t *hash_table;- uint32_t hash_size;-} JSObjectList;--static void js_object_list_init(JSObjectList *s)-{- memset(s, 0, sizeof(*s));-}--static uint32_t js_object_list_get_hash(JSObject *p, uint32_t hash_size)-{- return ((uintptr_t)p * 3163) & (hash_size - 1);-}--static int js_object_list_resize_hash(JSContext *ctx, JSObjectList *s,- uint32_t new_hash_size)-{- JSObjectListEntry *e;- uint32_t i, h, *new_hash_table;-- new_hash_table = js_malloc(ctx, sizeof(new_hash_table[0]) * new_hash_size);- if (!new_hash_table)- return -1;- js_free(ctx, s->hash_table);- s->hash_table = new_hash_table;- s->hash_size = new_hash_size;- - for(i = 0; i < s->hash_size; i++) {- s->hash_table[i] = -1;- }- for(i = 0; i < s->object_count; i++) {- e = &s->object_tab[i];- h = js_object_list_get_hash(e->obj, s->hash_size); - e->hash_next = s->hash_table[h];- s->hash_table[h] = i;- }- return 0;-}--/* the reference count of 'obj' is not modified. Return 0 if OK, -1 if- memory error */-static int js_object_list_add(JSContext *ctx, JSObjectList *s, JSObject *obj)-{- JSObjectListEntry *e;- uint32_t h, new_hash_size;- - if (js_resize_array(ctx, (void *)&s->object_tab,- sizeof(s->object_tab[0]),- &s->object_size, s->object_count + 1))- return -1;- if (unlikely((s->object_count + 1) >= s->hash_size)) {- new_hash_size = max_uint32(s->hash_size, 4);- while (new_hash_size <= s->object_count)- new_hash_size *= 2;- if (js_object_list_resize_hash(ctx, s, new_hash_size))- return -1;- }- e = &s->object_tab[s->object_count++];- h = js_object_list_get_hash(obj, s->hash_size); - e->obj = obj;- e->hash_next = s->hash_table[h];- s->hash_table[h] = s->object_count - 1;- return 0;-}--/* return -1 if not present or the object index */-static int js_object_list_find(JSContext *ctx, JSObjectList *s, JSObject *obj)-{- JSObjectListEntry *e;- uint32_t h, p;-- /* must test empty size because there is no hash table */- if (s->object_count == 0)- return -1;- h = js_object_list_get_hash(obj, s->hash_size); - p = s->hash_table[h];- while (p != -1) {- e = &s->object_tab[p];- if (e->obj == obj)- return p;- p = e->hash_next;- }- return -1;-}--static void js_object_list_end(JSContext *ctx, JSObjectList *s)-{- js_free(ctx, s->object_tab);- js_free(ctx, s->hash_table);-}--/*******************************************************************/-/* binary object writer & reader */--typedef enum BCTagEnum {- BC_TAG_NULL = 1,- BC_TAG_UNDEFINED,- BC_TAG_BOOL_FALSE,- BC_TAG_BOOL_TRUE,- BC_TAG_INT32,- BC_TAG_FLOAT64,- BC_TAG_STRING,- BC_TAG_OBJECT,- BC_TAG_ARRAY,- BC_TAG_BIG_INT,- BC_TAG_BIG_FLOAT,- BC_TAG_BIG_DECIMAL,- BC_TAG_TEMPLATE_OBJECT,- BC_TAG_FUNCTION_BYTECODE,- BC_TAG_MODULE,- BC_TAG_TYPED_ARRAY,- BC_TAG_ARRAY_BUFFER,- BC_TAG_SHARED_ARRAY_BUFFER,- BC_TAG_DATE,- BC_TAG_OBJECT_VALUE,- BC_TAG_OBJECT_REFERENCE,-} BCTagEnum;--#ifdef CONFIG_BIGNUM-#define BC_BASE_VERSION 2-#else-#define BC_BASE_VERSION 1-#endif-#define BC_BE_VERSION 0x40-#ifdef WORDS_BIGENDIAN-#define BC_VERSION (BC_BASE_VERSION | BC_BE_VERSION)-#else-#define BC_VERSION BC_BASE_VERSION-#endif--typedef struct BCWriterState {- JSContext *ctx;- DynBuf dbuf;- BOOL byte_swap : 8;- BOOL allow_bytecode : 8;- BOOL allow_sab : 8;- BOOL allow_reference : 8;- uint32_t first_atom;- uint32_t *atom_to_idx;- int atom_to_idx_size;- JSAtom *idx_to_atom;- int idx_to_atom_count;- int idx_to_atom_size;- uint8_t **sab_tab;- int sab_tab_len;- int sab_tab_size;- /* list of referenced objects (used if allow_reference = TRUE) */- JSObjectList object_list;-} BCWriterState;--#ifdef DUMP_READ_OBJECT-static const char * const bc_tag_str[] = {- "invalid",- "null",- "undefined",- "false",- "true",- "int32",- "float64",- "string",- "object",- "array",- "bigint",- "bigfloat",- "bigdecimal",- "template",- "function",- "module",- "TypedArray",- "ArrayBuffer",- "SharedArrayBuffer",- "Date",- "ObjectValue",- "ObjectReference",-};-#endif--static void bc_put_u8(BCWriterState *s, uint8_t v)-{- dbuf_putc(&s->dbuf, v);-}--static void bc_put_u16(BCWriterState *s, uint16_t v)-{- if (s->byte_swap)- v = bswap16(v);- dbuf_put_u16(&s->dbuf, v);-}--static __maybe_unused void bc_put_u32(BCWriterState *s, uint32_t v)-{- if (s->byte_swap)- v = bswap32(v);- dbuf_put_u32(&s->dbuf, v);-}--static void bc_put_u64(BCWriterState *s, uint64_t v)-{- if (s->byte_swap)- v = bswap64(v);- dbuf_put(&s->dbuf, (uint8_t *)&v, sizeof(v));-}--static void bc_put_leb128(BCWriterState *s, uint32_t v)-{- dbuf_put_leb128(&s->dbuf, v);-}--static void bc_put_sleb128(BCWriterState *s, int32_t v)-{- dbuf_put_sleb128(&s->dbuf, v);-}--static void bc_set_flags(uint32_t *pflags, int *pidx, uint32_t val, int n)-{- *pflags = *pflags | (val << *pidx);- *pidx += n;-}--static int bc_atom_to_idx(BCWriterState *s, uint32_t *pres, JSAtom atom)-{- uint32_t v;-- if (atom < s->first_atom || __JS_AtomIsTaggedInt(atom)) {- *pres = atom;- return 0;- }- atom -= s->first_atom;- if (atom < s->atom_to_idx_size && s->atom_to_idx[atom] != 0) {- *pres = s->atom_to_idx[atom];- return 0;- }- if (atom >= s->atom_to_idx_size) {- int old_size, i;- old_size = s->atom_to_idx_size;- if (js_resize_array(s->ctx, (void **)&s->atom_to_idx,- sizeof(s->atom_to_idx[0]), &s->atom_to_idx_size,- atom + 1))- return -1;- /* XXX: could add a specific js_resize_array() function to do it */- for(i = old_size; i < s->atom_to_idx_size; i++)- s->atom_to_idx[i] = 0;- }- if (js_resize_array(s->ctx, (void **)&s->idx_to_atom,- sizeof(s->idx_to_atom[0]),- &s->idx_to_atom_size, s->idx_to_atom_count + 1))- goto fail;-- v = s->idx_to_atom_count++;- s->idx_to_atom[v] = atom + s->first_atom;- v += s->first_atom;- s->atom_to_idx[atom] = v;- *pres = v;- return 0;- fail:- *pres = 0;- return -1;-}--static int bc_put_atom(BCWriterState *s, JSAtom atom)-{- uint32_t v;-- if (__JS_AtomIsTaggedInt(atom)) {- v = (__JS_AtomToUInt32(atom) << 1) | 1;- } else {- if (bc_atom_to_idx(s, &v, atom))- return -1;- v <<= 1;- }- bc_put_leb128(s, v);- return 0;-}--static void bc_byte_swap(uint8_t *bc_buf, int bc_len)-{- int pos, len, op, fmt;-- pos = 0;- while (pos < bc_len) {- op = bc_buf[pos];- len = short_opcode_info(op).size;- fmt = short_opcode_info(op).fmt;- switch(fmt) {- case OP_FMT_u16:- case OP_FMT_i16:- case OP_FMT_label16:- case OP_FMT_npop:- case OP_FMT_loc:- case OP_FMT_arg:- case OP_FMT_var_ref:- put_u16(bc_buf + pos + 1,- bswap16(get_u16(bc_buf + pos + 1)));- break;- case OP_FMT_i32:- case OP_FMT_u32:- case OP_FMT_const:- case OP_FMT_label:- case OP_FMT_atom:- case OP_FMT_atom_u8:- put_u32(bc_buf + pos + 1,- bswap32(get_u32(bc_buf + pos + 1)));- break;- case OP_FMT_atom_u16:- case OP_FMT_label_u16:- put_u32(bc_buf + pos + 1,- bswap32(get_u32(bc_buf + pos + 1)));- put_u16(bc_buf + pos + 1 + 4,- bswap16(get_u16(bc_buf + pos + 1 + 4)));- break;- case OP_FMT_atom_label_u8:- case OP_FMT_atom_label_u16:- put_u32(bc_buf + pos + 1,- bswap32(get_u32(bc_buf + pos + 1)));- put_u32(bc_buf + pos + 1 + 4,- bswap32(get_u32(bc_buf + pos + 1 + 4)));- if (fmt == OP_FMT_atom_label_u16) {- put_u16(bc_buf + pos + 1 + 4 + 4,- bswap16(get_u16(bc_buf + pos + 1 + 4 + 4)));- }- break;- case OP_FMT_npop_u16:- put_u16(bc_buf + pos + 1,- bswap16(get_u16(bc_buf + pos + 1)));- put_u16(bc_buf + pos + 1 + 2,- bswap16(get_u16(bc_buf + pos + 1 + 2)));- break;- default:- break;- }- pos += len;- }-}--static int JS_WriteFunctionBytecode(BCWriterState *s,- const uint8_t *bc_buf1, int bc_len)-{- int pos, len, op;- JSAtom atom;- uint8_t *bc_buf;- uint32_t val;-- bc_buf = js_malloc(s->ctx, bc_len);- if (!bc_buf)- return -1;- memcpy(bc_buf, bc_buf1, bc_len);-- pos = 0;- while (pos < bc_len) {- op = bc_buf[pos];- len = short_opcode_info(op).size;- switch(short_opcode_info(op).fmt) {- case OP_FMT_atom:- case OP_FMT_atom_u8:- case OP_FMT_atom_u16:- case OP_FMT_atom_label_u8:- case OP_FMT_atom_label_u16:- atom = get_u32(bc_buf + pos + 1);- if (bc_atom_to_idx(s, &val, atom))- goto fail;- put_u32(bc_buf + pos + 1, val);- break;- default:- break;- }- pos += len;- }-- if (s->byte_swap)- bc_byte_swap(bc_buf, bc_len);-- dbuf_put(&s->dbuf, bc_buf, bc_len);-- js_free(s->ctx, bc_buf);- return 0;- fail:- js_free(s->ctx, bc_buf);- return -1;-}--static void JS_WriteString(BCWriterState *s, JSString *p)-{- int i;- bc_put_leb128(s, ((uint32_t)p->len << 1) | p->is_wide_char);- if (p->is_wide_char) {- for(i = 0; i < p->len; i++)- bc_put_u16(s, p->u.str16[i]);- } else {- dbuf_put(&s->dbuf, p->u.str8, p->len);- }-}--#ifdef CONFIG_BIGNUM-static int JS_WriteBigNum(BCWriterState *s, JSValueConst obj)-{- uint32_t tag, tag1;- int64_t e;- JSBigFloat *bf = JS_VALUE_GET_PTR(obj);- bf_t *a = &bf->num;- size_t len, i, n1, j;- limb_t v;-- tag = JS_VALUE_GET_TAG(obj);- switch(tag) {- case JS_TAG_BIG_INT:- tag1 = BC_TAG_BIG_INT;- break;- case JS_TAG_BIG_FLOAT:- tag1 = BC_TAG_BIG_FLOAT;- break;- case JS_TAG_BIG_DECIMAL:- tag1 = BC_TAG_BIG_DECIMAL;- break;- default:- abort();- }- bc_put_u8(s, tag1);-- /* sign + exponent */- if (a->expn == BF_EXP_ZERO)- e = 0;- else if (a->expn == BF_EXP_INF)- e = 1;- else if (a->expn == BF_EXP_NAN)- e = 2;- else if (a->expn >= 0)- e = a->expn + 3;- else- e = a->expn;- e = (e << 1) | a->sign;- if (e < INT32_MIN || e > INT32_MAX) {- JS_ThrowInternalError(s->ctx, "bignum exponent is too large");- return -1;- }- bc_put_sleb128(s, e);-- /* mantissa */- if (a->len != 0) {- if (tag != JS_TAG_BIG_DECIMAL) {- i = 0;- while (i < a->len && a->tab[i] == 0)- i++;- assert(i < a->len);- v = a->tab[i];- n1 = sizeof(limb_t);- while ((v & 0xff) == 0) {- n1--;- v >>= 8;- }- i++;- len = (a->len - i) * sizeof(limb_t) + n1;- if (len > INT32_MAX) {- JS_ThrowInternalError(s->ctx, "bignum is too large");- return -1;- }- bc_put_leb128(s, len);- /* always saved in byte based little endian representation */- for(j = 0; j < n1; j++) {- dbuf_putc(&s->dbuf, v >> (j * 8));- }- for(; i < a->len; i++) {- limb_t v = a->tab[i];-#if LIMB_BITS == 32-#ifdef WORDS_BIGENDIAN- v = bswap32(v);-#endif- dbuf_put_u32(&s->dbuf, v);-#else-#ifdef WORDS_BIGENDIAN- v = bswap64(v);-#endif- dbuf_put_u64(&s->dbuf, v);-#endif- }- } else {- int bpos, d;- uint8_t v8;- size_t i0;- - /* little endian BCD */- i = 0;- while (i < a->len && a->tab[i] == 0)- i++;- assert(i < a->len);- len = a->len * LIMB_DIGITS;- v = a->tab[i];- j = 0;- while ((v % 10) == 0) {- j++;- v /= 10;- }- len -= j;- assert(len > 0);- if (len > INT32_MAX) {- JS_ThrowInternalError(s->ctx, "bignum is too large");- return -1;- }- bc_put_leb128(s, len);- - bpos = 0;- v8 = 0;- i0 = i;- for(; i < a->len; i++) {- if (i != i0) {- v = a->tab[i];- j = 0;- }- for(; j < LIMB_DIGITS; j++) {- d = v % 10;- v /= 10;- if (bpos == 0) {- v8 = d;- bpos = 1;- } else {- dbuf_putc(&s->dbuf, v8 | (d << 4));- bpos = 0;- }- }- }- /* flush the last digit */- if (bpos) {- dbuf_putc(&s->dbuf, v8);- }- }- }- return 0;-}-#endif /* CONFIG_BIGNUM */--static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj);--static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj)-{- JSFunctionBytecode *b = JS_VALUE_GET_PTR(obj);- uint32_t flags;- int idx, i;- - bc_put_u8(s, BC_TAG_FUNCTION_BYTECODE);- flags = idx = 0;- bc_set_flags(&flags, &idx, b->has_prototype, 1);- bc_set_flags(&flags, &idx, b->has_simple_parameter_list, 1);- bc_set_flags(&flags, &idx, b->is_derived_class_constructor, 1);- bc_set_flags(&flags, &idx, b->need_home_object, 1);- bc_set_flags(&flags, &idx, b->func_kind, 2);- bc_set_flags(&flags, &idx, b->new_target_allowed, 1);- bc_set_flags(&flags, &idx, b->super_call_allowed, 1);- bc_set_flags(&flags, &idx, b->super_allowed, 1);- bc_set_flags(&flags, &idx, b->arguments_allowed, 1);- bc_set_flags(&flags, &idx, b->has_debug, 1);- bc_set_flags(&flags, &idx, b->backtrace_barrier, 1);- assert(idx <= 16);- bc_put_u16(s, flags);- bc_put_u8(s, b->js_mode);- bc_put_atom(s, b->func_name);- - bc_put_leb128(s, b->arg_count);- bc_put_leb128(s, b->var_count);- bc_put_leb128(s, b->defined_arg_count);- bc_put_leb128(s, b->stack_size);- bc_put_leb128(s, b->closure_var_count);- bc_put_leb128(s, b->cpool_count);- bc_put_leb128(s, b->byte_code_len);- if (b->vardefs) {- bc_put_leb128(s, b->arg_count + b->var_count);- for(i = 0; i < b->arg_count + b->var_count; i++) {- JSVarDef *vd = &b->vardefs[i];- bc_put_atom(s, vd->var_name);- bc_put_leb128(s, vd->scope_level);- bc_put_leb128(s, vd->scope_next + 1);- flags = idx = 0;- bc_set_flags(&flags, &idx, vd->var_kind, 4);- bc_set_flags(&flags, &idx, vd->is_func_var, 1);- bc_set_flags(&flags, &idx, vd->is_const, 1);- bc_set_flags(&flags, &idx, vd->is_lexical, 1);- bc_set_flags(&flags, &idx, vd->is_captured, 1);- assert(idx <= 8);- bc_put_u8(s, flags);- }- } else {- bc_put_leb128(s, 0);- }- - for(i = 0; i < b->closure_var_count; i++) {- JSClosureVar *cv = &b->closure_var[i];- bc_put_atom(s, cv->var_name);- bc_put_leb128(s, cv->var_idx);- flags = idx = 0;- bc_set_flags(&flags, &idx, cv->is_local, 1);- bc_set_flags(&flags, &idx, cv->is_arg, 1);- bc_set_flags(&flags, &idx, cv->is_const, 1);- bc_set_flags(&flags, &idx, cv->is_lexical, 1);- bc_set_flags(&flags, &idx, cv->var_kind, 4);- assert(idx <= 8);- bc_put_u8(s, flags);- }- - if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len))- goto fail;- - if (b->has_debug) {- bc_put_atom(s, b->debug.filename);- bc_put_leb128(s, b->debug.line_num);- bc_put_leb128(s, b->debug.pc2line_len);- dbuf_put(&s->dbuf, b->debug.pc2line_buf, b->debug.pc2line_len);- }- - for(i = 0; i < b->cpool_count; i++) {- if (JS_WriteObjectRec(s, b->cpool[i]))- goto fail;- }- return 0;- fail:- return -1;-}--static int JS_WriteModule(BCWriterState *s, JSValueConst obj)-{- JSModuleDef *m = JS_VALUE_GET_PTR(obj);- int i;- - bc_put_u8(s, BC_TAG_MODULE);- bc_put_atom(s, m->module_name);- - bc_put_leb128(s, m->req_module_entries_count);- for(i = 0; i < m->req_module_entries_count; i++) {- JSReqModuleEntry *rme = &m->req_module_entries[i];- bc_put_atom(s, rme->module_name);- }- - bc_put_leb128(s, m->export_entries_count);- for(i = 0; i < m->export_entries_count; i++) {- JSExportEntry *me = &m->export_entries[i];- bc_put_u8(s, me->export_type);- if (me->export_type == JS_EXPORT_TYPE_LOCAL) {- bc_put_leb128(s, me->u.local.var_idx);- } else {- bc_put_leb128(s, me->u.req_module_idx);- bc_put_atom(s, me->local_name);- }- bc_put_atom(s, me->export_name);- }- - bc_put_leb128(s, m->star_export_entries_count);- for(i = 0; i < m->star_export_entries_count; i++) {- JSStarExportEntry *se = &m->star_export_entries[i];- bc_put_leb128(s, se->req_module_idx);- }- - bc_put_leb128(s, m->import_entries_count);- for(i = 0; i < m->import_entries_count; i++) {- JSImportEntry *mi = &m->import_entries[i];- bc_put_leb128(s, mi->var_idx);- bc_put_atom(s, mi->import_name);- bc_put_leb128(s, mi->req_module_idx);- }- - if (JS_WriteObjectRec(s, m->func_obj))- goto fail;- return 0;- fail:- return -1;-}--static int JS_WriteArray(BCWriterState *s, JSValueConst obj)-{- JSObject *p = JS_VALUE_GET_OBJ(obj);- uint32_t i, len;- JSValue val;- int ret;- BOOL is_template;- - if (s->allow_bytecode && !p->extensible) {- /* not extensible array: we consider it is a- template when we are saving bytecode */- bc_put_u8(s, BC_TAG_TEMPLATE_OBJECT);- is_template = TRUE;- } else {- bc_put_u8(s, BC_TAG_ARRAY);- is_template = FALSE;- }- if (js_get_length32(s->ctx, &len, obj))- goto fail1;- bc_put_leb128(s, len);- for(i = 0; i < len; i++) {- val = JS_GetPropertyUint32(s->ctx, obj, i);- if (JS_IsException(val))- goto fail1;- ret = JS_WriteObjectRec(s, val);- JS_FreeValue(s->ctx, val);- if (ret)- goto fail1;- }- if (is_template) {- val = JS_GetProperty(s->ctx, obj, JS_ATOM_raw);- if (JS_IsException(val))- goto fail1;- ret = JS_WriteObjectRec(s, val);- JS_FreeValue(s->ctx, val);- if (ret)- goto fail1;- }- return 0;- fail1:- return -1;-}--static int JS_WriteObjectTag(BCWriterState *s, JSValueConst obj)-{- JSObject *p = JS_VALUE_GET_OBJ(obj);- uint32_t i, prop_count;- JSShape *sh;- JSShapeProperty *pr;- int pass;- JSAtom atom;-- bc_put_u8(s, BC_TAG_OBJECT);- prop_count = 0;- sh = p->shape;- for(pass = 0; pass < 2; pass++) {- if (pass == 1)- bc_put_leb128(s, prop_count);- for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {- atom = pr->atom;- if (atom != JS_ATOM_NULL &&- JS_AtomIsString(s->ctx, atom) &&- (pr->flags & JS_PROP_ENUMERABLE)) {- if (pr->flags & JS_PROP_TMASK) {- JS_ThrowTypeError(s->ctx, "only value properties are supported");- goto fail;- }- if (pass == 0) {- prop_count++;- } else {- bc_put_atom(s, atom);- if (JS_WriteObjectRec(s, p->prop[i].u.value))- goto fail;- }- }- }- }- return 0;- fail:- return -1;-}--static int JS_WriteTypedArray(BCWriterState *s, JSValueConst obj)-{- JSObject *p = JS_VALUE_GET_OBJ(obj);- JSTypedArray *ta = p->u.typed_array;-- bc_put_u8(s, BC_TAG_TYPED_ARRAY);- bc_put_u8(s, p->class_id - JS_CLASS_UINT8C_ARRAY);- bc_put_leb128(s, p->u.array.count);- bc_put_leb128(s, ta->offset);- if (JS_WriteObjectRec(s, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)))- return -1;- return 0;-}--static int JS_WriteArrayBuffer(BCWriterState *s, JSValueConst obj)-{- JSObject *p = JS_VALUE_GET_OBJ(obj);- JSArrayBuffer *abuf = p->u.array_buffer;- if (abuf->detached) {- JS_ThrowTypeErrorDetachedArrayBuffer(s->ctx);- return -1;- }- bc_put_u8(s, BC_TAG_ARRAY_BUFFER);- bc_put_leb128(s, abuf->byte_length);- dbuf_put(&s->dbuf, abuf->data, abuf->byte_length);- return 0;-}--static int JS_WriteSharedArrayBuffer(BCWriterState *s, JSValueConst obj)-{- JSObject *p = JS_VALUE_GET_OBJ(obj);- JSArrayBuffer *abuf = p->u.array_buffer;- assert(!abuf->detached); /* SharedArrayBuffer are never detached */- bc_put_u8(s, BC_TAG_SHARED_ARRAY_BUFFER);- bc_put_leb128(s, abuf->byte_length);- bc_put_u64(s, (uintptr_t)abuf->data);- if (js_resize_array(s->ctx, (void **)&s->sab_tab, sizeof(s->sab_tab[0]),- &s->sab_tab_size, s->sab_tab_len + 1))- return -1;- /* keep the SAB pointer so that the user can clone it or free it */- s->sab_tab[s->sab_tab_len++] = abuf->data;- return 0;-}--static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj)-{- uint32_t tag;-- if (js_check_stack_overflow(s->ctx->rt, 0)) {- JS_ThrowStackOverflow(s->ctx);- return -1;- }-- tag = JS_VALUE_GET_NORM_TAG(obj);- switch(tag) {- case JS_TAG_NULL:- bc_put_u8(s, BC_TAG_NULL);- break;- case JS_TAG_UNDEFINED:- bc_put_u8(s, BC_TAG_UNDEFINED);- break;- case JS_TAG_BOOL:- bc_put_u8(s, BC_TAG_BOOL_FALSE + JS_VALUE_GET_INT(obj));- break;- case JS_TAG_INT:- bc_put_u8(s, BC_TAG_INT32);- bc_put_sleb128(s, JS_VALUE_GET_INT(obj));- break;- case JS_TAG_FLOAT64:- {- JSFloat64Union u;- bc_put_u8(s, BC_TAG_FLOAT64);- u.d = JS_VALUE_GET_FLOAT64(obj);- bc_put_u64(s, u.u64);- }- break;- case JS_TAG_STRING:- {- JSString *p = JS_VALUE_GET_STRING(obj);- bc_put_u8(s, BC_TAG_STRING);- JS_WriteString(s, p);- }- break;- case JS_TAG_FUNCTION_BYTECODE:- if (!s->allow_bytecode)- goto invalid_tag;- if (JS_WriteFunctionTag(s, obj))- goto fail;- break;- case JS_TAG_MODULE:- if (!s->allow_bytecode)- goto invalid_tag;- if (JS_WriteModule(s, obj))- goto fail;- break;- case JS_TAG_OBJECT:- {- JSObject *p = JS_VALUE_GET_OBJ(obj);- int ret, idx;- - if (s->allow_reference) {- idx = js_object_list_find(s->ctx, &s->object_list, p);- if (idx >= 0) {- bc_put_u8(s, BC_TAG_OBJECT_REFERENCE);- bc_put_leb128(s, idx);- break;- } else {- if (js_object_list_add(s->ctx, &s->object_list, p))- goto fail;- }- } else {- if (p->tmp_mark) {- JS_ThrowTypeError(s->ctx, "circular reference");- goto fail;- }- p->tmp_mark = 1;- }- switch(p->class_id) {- case JS_CLASS_ARRAY:- ret = JS_WriteArray(s, obj);- break;- case JS_CLASS_OBJECT:- ret = JS_WriteObjectTag(s, obj);- break;- case JS_CLASS_ARRAY_BUFFER:- ret = JS_WriteArrayBuffer(s, obj);- break;- case JS_CLASS_SHARED_ARRAY_BUFFER:- if (!s->allow_sab)- goto invalid_tag;- ret = JS_WriteSharedArrayBuffer(s, obj);- break;- case JS_CLASS_DATE:- bc_put_u8(s, BC_TAG_DATE);- ret = JS_WriteObjectRec(s, p->u.object_data);- break;- case JS_CLASS_NUMBER:- case JS_CLASS_STRING:- case JS_CLASS_BOOLEAN:-#ifdef CONFIG_BIGNUM- case JS_CLASS_BIG_INT:- case JS_CLASS_BIG_FLOAT:- case JS_CLASS_BIG_DECIMAL:-#endif- bc_put_u8(s, BC_TAG_OBJECT_VALUE);- ret = JS_WriteObjectRec(s, p->u.object_data);- break;- default:- if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&- p->class_id <= JS_CLASS_FLOAT64_ARRAY) {- ret = JS_WriteTypedArray(s, obj);- } else {- JS_ThrowTypeError(s->ctx, "unsupported object class");- ret = -1;- }- break;- }- p->tmp_mark = 0;- if (ret)- goto fail;- }- break;-#ifdef CONFIG_BIGNUM- case JS_TAG_BIG_INT:- case JS_TAG_BIG_FLOAT:- case JS_TAG_BIG_DECIMAL:- if (JS_WriteBigNum(s, obj))- goto fail;- break;-#endif- default:- invalid_tag:- JS_ThrowInternalError(s->ctx, "unsupported tag (%d)", tag);- goto fail;- }- return 0;-- fail:- return -1;-}--/* create the atom table */-static int JS_WriteObjectAtoms(BCWriterState *s)-{- JSRuntime *rt = s->ctx->rt;- DynBuf dbuf1;- int i, atoms_size;- uint8_t version;-- dbuf1 = s->dbuf;- js_dbuf_init(s->ctx, &s->dbuf);-- version = BC_VERSION;- if (s->byte_swap)- version ^= BC_BE_VERSION;- bc_put_u8(s, version);-- bc_put_leb128(s, s->idx_to_atom_count);- for(i = 0; i < s->idx_to_atom_count; i++) {- JSAtomStruct *p = rt->atom_array[s->idx_to_atom[i]];- JS_WriteString(s, p);- }- /* XXX: should check for OOM in above phase */-- /* move the atoms at the start */- /* XXX: could just append dbuf1 data, but it uses more memory if- dbuf1 is larger than dbuf */- atoms_size = s->dbuf.size;- if (dbuf_realloc(&dbuf1, dbuf1.size + atoms_size))- goto fail;- memmove(dbuf1.buf + atoms_size, dbuf1.buf, dbuf1.size);- memcpy(dbuf1.buf, s->dbuf.buf, atoms_size);- dbuf1.size += atoms_size;- dbuf_free(&s->dbuf);- s->dbuf = dbuf1;- return 0;- fail:- dbuf_free(&dbuf1);- return -1;-}--uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj,- int flags, uint8_t ***psab_tab, size_t *psab_tab_len)-{- BCWriterState ss, *s = &ss;-- memset(s, 0, sizeof(*s));- s->ctx = ctx;- /* XXX: byte swapped output is untested */- s->byte_swap = ((flags & JS_WRITE_OBJ_BSWAP) != 0);- s->allow_bytecode = ((flags & JS_WRITE_OBJ_BYTECODE) != 0);- s->allow_sab = ((flags & JS_WRITE_OBJ_SAB) != 0);- s->allow_reference = ((flags & JS_WRITE_OBJ_REFERENCE) != 0);- /* XXX: could use a different version when bytecode is included */- if (s->allow_bytecode)- s->first_atom = JS_ATOM_END;- else- s->first_atom = 1;- js_dbuf_init(ctx, &s->dbuf);- js_object_list_init(&s->object_list);- - if (JS_WriteObjectRec(s, obj))- goto fail;- if (JS_WriteObjectAtoms(s))- goto fail;- js_object_list_end(ctx, &s->object_list);- js_free(ctx, s->atom_to_idx);- js_free(ctx, s->idx_to_atom);- *psize = s->dbuf.size;- if (psab_tab)- *psab_tab = s->sab_tab;- if (psab_tab_len)- *psab_tab_len = s->sab_tab_len;- return s->dbuf.buf;- fail:- js_object_list_end(ctx, &s->object_list);- js_free(ctx, s->atom_to_idx);- js_free(ctx, s->idx_to_atom);- dbuf_free(&s->dbuf);- *psize = 0;- if (psab_tab)- *psab_tab = NULL;- if (psab_tab_len)- *psab_tab_len = 0;- return NULL;-}--uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj,- int flags)-{- return JS_WriteObject2(ctx, psize, obj, flags, NULL, NULL);-}--typedef struct BCReaderState {- JSContext *ctx;- const uint8_t *buf_start, *ptr, *buf_end;- uint32_t first_atom;- uint32_t idx_to_atom_count;- JSAtom *idx_to_atom;- int error_state;- BOOL allow_sab : 8;- BOOL allow_bytecode : 8;- BOOL is_rom_data : 8;- BOOL allow_reference : 8;- /* object references */- JSObject **objects;- int objects_count;- int objects_size;- -#ifdef DUMP_READ_OBJECT- const uint8_t *ptr_last;- int level;-#endif-} BCReaderState;--#ifdef DUMP_READ_OBJECT-static void __attribute__((format(printf, 2, 3))) bc_read_trace(BCReaderState *s, const char *fmt, ...) {- va_list ap;- int i, n, n0;-- if (!s->ptr_last)- s->ptr_last = s->buf_start;-- n = n0 = 0;- if (s->ptr > s->ptr_last || s->ptr == s->buf_start) {- n0 = printf("%04x: ", (int)(s->ptr_last - s->buf_start));- n += n0;- }- for (i = 0; s->ptr_last < s->ptr; i++) {- if ((i & 7) == 0 && i > 0) {- printf("\n%*s", n0, "");- n = n0;- }- n += printf(" %02x", *s->ptr_last++);- }- if (*fmt == '}')- s->level--;- if (n < 32 + s->level * 2) {- printf("%*s", 32 + s->level * 2 - n, "");- }- va_start(ap, fmt);- vfprintf(stdout, fmt, ap);- va_end(ap);- if (strchr(fmt, '{'))- s->level++;-}-#else-#define bc_read_trace(...)-#endif--static int bc_read_error_end(BCReaderState *s)-{- if (!s->error_state) {- JS_ThrowSyntaxError(s->ctx, "read after the end of the buffer");- }- return s->error_state = -1;-}--static int bc_get_u8(BCReaderState *s, uint8_t *pval)-{- if (unlikely(s->buf_end - s->ptr < 1)) {- *pval = 0; /* avoid warning */- return bc_read_error_end(s);- }- *pval = *s->ptr++;- return 0;-}--static int bc_get_u16(BCReaderState *s, uint16_t *pval)-{- if (unlikely(s->buf_end - s->ptr < 2)) {- *pval = 0; /* avoid warning */- return bc_read_error_end(s);- }- *pval = get_u16(s->ptr);- s->ptr += 2;- return 0;-}--static __maybe_unused int bc_get_u32(BCReaderState *s, uint32_t *pval)-{- if (unlikely(s->buf_end - s->ptr < 4)) {- *pval = 0; /* avoid warning */- return bc_read_error_end(s);- }- *pval = get_u32(s->ptr);- s->ptr += 4;- return 0;-}--static int bc_get_u64(BCReaderState *s, uint64_t *pval)-{- if (unlikely(s->buf_end - s->ptr < 8)) {- *pval = 0; /* avoid warning */- return bc_read_error_end(s);- }- *pval = get_u64(s->ptr);- s->ptr += 8;- return 0;-}--static int bc_get_leb128(BCReaderState *s, uint32_t *pval)-{- int ret;- ret = get_leb128(pval, s->ptr, s->buf_end);- if (unlikely(ret < 0))- return bc_read_error_end(s);- s->ptr += ret;- return 0;-}--static int bc_get_sleb128(BCReaderState *s, int32_t *pval)-{- int ret;- ret = get_sleb128(pval, s->ptr, s->buf_end);- if (unlikely(ret < 0))- return bc_read_error_end(s);- s->ptr += ret;- return 0;-}--/* XXX: used to read an `int` with a positive value */-static int bc_get_leb128_int(BCReaderState *s, int *pval)-{- return bc_get_leb128(s, (uint32_t *)pval);-}--static int bc_get_leb128_u16(BCReaderState *s, uint16_t *pval)-{- uint32_t val;- if (bc_get_leb128(s, &val)) {- *pval = 0;- return -1;- }- *pval = val;- return 0;-}--static int bc_get_buf(BCReaderState *s, uint8_t *buf, uint32_t buf_len)-{- if (buf_len != 0) {- if (unlikely(!buf || s->buf_end - s->ptr < buf_len))- return bc_read_error_end(s);- memcpy(buf, s->ptr, buf_len);- s->ptr += buf_len;- }- return 0;-}--static int bc_idx_to_atom(BCReaderState *s, JSAtom *patom, uint32_t idx)-{- JSAtom atom;-- if (__JS_AtomIsTaggedInt(idx)) {- atom = idx;- } else if (idx < s->first_atom) {- atom = JS_DupAtom(s->ctx, idx);- } else {- idx -= s->first_atom;- if (idx >= s->idx_to_atom_count) {- JS_ThrowSyntaxError(s->ctx, "invalid atom index (pos=%u)",- (unsigned int)(s->ptr - s->buf_start));- *patom = JS_ATOM_NULL;- return s->error_state = -1;- }- atom = JS_DupAtom(s->ctx, s->idx_to_atom[idx]);- }- *patom = atom;- return 0;-}--static int bc_get_atom(BCReaderState *s, JSAtom *patom)-{- uint32_t v;- if (bc_get_leb128(s, &v))- return -1;- if (v & 1) {- *patom = __JS_AtomFromUInt32(v >> 1);- return 0;- } else {- return bc_idx_to_atom(s, patom, v >> 1);- }-}--static JSString *JS_ReadString(BCReaderState *s)-{- uint32_t len;- size_t size;- BOOL is_wide_char;- JSString *p;-- if (bc_get_leb128(s, &len))- return NULL;- is_wide_char = len & 1;- len >>= 1;- p = js_alloc_string(s->ctx, len, is_wide_char);- if (!p) {- s->error_state = -1;- return NULL;- }- size = (size_t)len << is_wide_char;- if ((s->buf_end - s->ptr) < size) {- bc_read_error_end(s);- js_free_string(s->ctx->rt, p);- return NULL;- }- memcpy(p->u.str8, s->ptr, size);- s->ptr += size;- if (!is_wide_char) {- p->u.str8[size] = '\0'; /* add the trailing zero for 8 bit strings */- }-#ifdef DUMP_READ_OBJECT- JS_DumpString(s->ctx->rt, p); printf("\n");-#endif- return p;-}--static uint32_t bc_get_flags(uint32_t flags, int *pidx, int n)-{- uint32_t val;- /* XXX: this does not work for n == 32 */- val = (flags >> *pidx) & ((1U << n) - 1);- *pidx += n;- return val;-}--static int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b,- int byte_code_offset, uint32_t bc_len)-{- uint8_t *bc_buf;- int pos, len, op;- JSAtom atom;- uint32_t idx;-- if (s->is_rom_data) {- /* directly use the input buffer */- if (unlikely(s->buf_end - s->ptr < bc_len))- return bc_read_error_end(s);- bc_buf = (uint8_t *)s->ptr;- s->ptr += bc_len;- } else {- bc_buf = (void *)((uint8_t*)b + byte_code_offset);- if (bc_get_buf(s, bc_buf, bc_len))- return -1;- }- b->byte_code_buf = bc_buf;-- pos = 0;- while (pos < bc_len) {- op = bc_buf[pos];- len = short_opcode_info(op).size;- switch(short_opcode_info(op).fmt) {- case OP_FMT_atom:- case OP_FMT_atom_u8:- case OP_FMT_atom_u16:- case OP_FMT_atom_label_u8:- case OP_FMT_atom_label_u16:- idx = get_u32(bc_buf + pos + 1);- if (s->is_rom_data) {- /* just increment the reference count of the atom */- JS_DupAtom(s->ctx, (JSAtom)idx);- } else {- if (bc_idx_to_atom(s, &atom, idx)) {- /* Note: the atoms will be freed up to this position */- b->byte_code_len = pos;- return -1;- }- put_u32(bc_buf + pos + 1, atom);-#ifdef DUMP_READ_OBJECT- bc_read_trace(s, "at %d, fixup atom: ", pos + 1); print_atom(s->ctx, atom); printf("\n");-#endif- }- break;- default:- break;- }- pos += len;- }- return 0;-}--#ifdef CONFIG_BIGNUM-static JSValue JS_ReadBigNum(BCReaderState *s, int tag)-{- JSValue obj = JS_UNDEFINED;- uint8_t v8;- int32_t e;- uint32_t len;- limb_t l, i, n, j;- JSBigFloat *p;- limb_t v;- bf_t *a;- int bpos, d;- - p = js_new_bf(s->ctx);- if (!p)- goto fail;- switch(tag) {- case BC_TAG_BIG_INT:- obj = JS_MKPTR(JS_TAG_BIG_INT, p);- break;- case BC_TAG_BIG_FLOAT:- obj = JS_MKPTR(JS_TAG_BIG_FLOAT, p);- break;- case BC_TAG_BIG_DECIMAL:- obj = JS_MKPTR(JS_TAG_BIG_DECIMAL, p);- break;- default:- abort();- }-- /* sign + exponent */- if (bc_get_sleb128(s, &e))- goto fail;-- a = &p->num;- a->sign = e & 1;- e >>= 1;- if (e == 0)- a->expn = BF_EXP_ZERO;- else if (e == 1)- a->expn = BF_EXP_INF;- else if (e == 2)- a->expn = BF_EXP_NAN;- else if (e >= 3)- a->expn = e - 3;- else- a->expn = e;-- /* mantissa */- if (a->expn != BF_EXP_ZERO &&- a->expn != BF_EXP_INF &&- a->expn != BF_EXP_NAN) {- if (bc_get_leb128(s, &len))- goto fail;- bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len);- if (len == 0) {- JS_ThrowInternalError(s->ctx, "invalid bignum length");- goto fail;- }- if (tag != BC_TAG_BIG_DECIMAL)- l = (len + sizeof(limb_t) - 1) / sizeof(limb_t);- else- l = (len + LIMB_DIGITS - 1) / LIMB_DIGITS;- if (bf_resize(a, l)) {- JS_ThrowOutOfMemory(s->ctx);- goto fail;- }- if (tag != BC_TAG_BIG_DECIMAL) {- n = len & (sizeof(limb_t) - 1);- if (n != 0) {- v = 0;- for(i = 0; i < n; i++) {- if (bc_get_u8(s, &v8))- goto fail;- v |= (limb_t)v8 << ((sizeof(limb_t) - n + i) * 8);- }- a->tab[0] = v;- i = 1;- } else {- i = 0;- }- for(; i < l; i++) {-#if LIMB_BITS == 32- if (bc_get_u32(s, &v))- goto fail;-#ifdef WORDS_BIGENDIAN- v = bswap32(v);-#endif-#else- if (bc_get_u64(s, &v))- goto fail;-#ifdef WORDS_BIGENDIAN- v = bswap64(v);-#endif-#endif- a->tab[i] = v;- }- } else {- bpos = 0;- for(i = 0; i < l; i++) {- if (i == 0 && (n = len % LIMB_DIGITS) != 0) {- j = LIMB_DIGITS - n;- } else {- j = 0;- }- v = 0;- for(; j < LIMB_DIGITS; j++) {- if (bpos == 0) {- if (bc_get_u8(s, &v8))- goto fail;- d = v8 & 0xf;- bpos = 1;- } else {- d = v8 >> 4;- bpos = 0;- }- if (d >= 10) {- JS_ThrowInternalError(s->ctx, "invalid digit");- goto fail;- }- v += mp_pow_dec[j] * d;- }- a->tab[i] = v;- }- }- }- bc_read_trace(s, "}\n");- return obj;- fail:- JS_FreeValue(s->ctx, obj);- return JS_EXCEPTION;-}-#endif /* CONFIG_BIGNUM */--static JSValue JS_ReadObjectRec(BCReaderState *s);--static int BC_add_object_ref1(BCReaderState *s, JSObject *p)-{- if (s->allow_reference) {- if (js_resize_array(s->ctx, (void *)&s->objects,- sizeof(s->objects[0]),- &s->objects_size, s->objects_count + 1))- return -1;- s->objects[s->objects_count++] = p;- }- return 0;-}--static int BC_add_object_ref(BCReaderState *s, JSValueConst obj)-{- return BC_add_object_ref1(s, JS_VALUE_GET_OBJ(obj));-}--static JSValue JS_ReadFunctionTag(BCReaderState *s)-{- JSContext *ctx = s->ctx;- JSFunctionBytecode bc, *b;- JSValue obj = JS_UNDEFINED;- uint16_t v16;- uint8_t v8;- int idx, i, local_count;- int function_size, cpool_offset, byte_code_offset;- int closure_var_offset, vardefs_offset;-- memset(&bc, 0, sizeof(bc));- bc.header.ref_count = 1;- //bc.gc_header.mark = 0;-- if (bc_get_u16(s, &v16))- goto fail;- idx = 0;- bc.has_prototype = bc_get_flags(v16, &idx, 1);- bc.has_simple_parameter_list = bc_get_flags(v16, &idx, 1);- bc.is_derived_class_constructor = bc_get_flags(v16, &idx, 1);- bc.need_home_object = bc_get_flags(v16, &idx, 1);- bc.func_kind = bc_get_flags(v16, &idx, 2);- bc.new_target_allowed = bc_get_flags(v16, &idx, 1);- bc.super_call_allowed = bc_get_flags(v16, &idx, 1);- bc.super_allowed = bc_get_flags(v16, &idx, 1);- bc.arguments_allowed = bc_get_flags(v16, &idx, 1);- bc.has_debug = bc_get_flags(v16, &idx, 1);- bc.backtrace_barrier = bc_get_flags(v16, &idx, 1);- bc.read_only_bytecode = s->is_rom_data;- if (bc_get_u8(s, &v8))- goto fail;- bc.js_mode = v8;- if (bc_get_atom(s, &bc.func_name)) //@ atom leak if failure- goto fail;- if (bc_get_leb128_u16(s, &bc.arg_count))- goto fail;- if (bc_get_leb128_u16(s, &bc.var_count))- goto fail;- if (bc_get_leb128_u16(s, &bc.defined_arg_count))- goto fail;- if (bc_get_leb128_u16(s, &bc.stack_size))- goto fail;- if (bc_get_leb128_int(s, &bc.closure_var_count))- goto fail;- if (bc_get_leb128_int(s, &bc.cpool_count))- goto fail;- if (bc_get_leb128_int(s, &bc.byte_code_len))- goto fail;- if (bc_get_leb128_int(s, &local_count))- goto fail;-- if (bc.has_debug) {- function_size = sizeof(*b);- } else {- function_size = offsetof(JSFunctionBytecode, debug);- }- cpool_offset = function_size;- function_size += bc.cpool_count * sizeof(*bc.cpool);- vardefs_offset = function_size;- function_size += local_count * sizeof(*bc.vardefs);- closure_var_offset = function_size;- function_size += bc.closure_var_count * sizeof(*bc.closure_var);- byte_code_offset = function_size;- if (!bc.read_only_bytecode) {- function_size += bc.byte_code_len;- }-- b = js_mallocz(ctx, function_size);- if (!b)- return JS_EXCEPTION;- - memcpy(b, &bc, offsetof(JSFunctionBytecode, debug));- b->header.ref_count = 1;- add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);- - obj = JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b);--#ifdef DUMP_READ_OBJECT- bc_read_trace(s, "name: "); print_atom(s->ctx, b->func_name); printf("\n");-#endif- bc_read_trace(s, "args=%d vars=%d defargs=%d closures=%d cpool=%d\n",- b->arg_count, b->var_count, b->defined_arg_count,- b->closure_var_count, b->cpool_count);- bc_read_trace(s, "stack=%d bclen=%d locals=%d\n",- b->stack_size, b->byte_code_len, local_count);-- if (local_count != 0) {- bc_read_trace(s, "vars {\n");- b->vardefs = (void *)((uint8_t*)b + vardefs_offset);- for(i = 0; i < local_count; i++) {- JSVarDef *vd = &b->vardefs[i];- if (bc_get_atom(s, &vd->var_name))- goto fail;- if (bc_get_leb128_int(s, &vd->scope_level))- goto fail;- if (bc_get_leb128_int(s, &vd->scope_next))- goto fail;- vd->scope_next--;- if (bc_get_u8(s, &v8))- goto fail;- idx = 0;- vd->var_kind = bc_get_flags(v8, &idx, 4);- vd->is_func_var = bc_get_flags(v8, &idx, 1);- vd->is_const = bc_get_flags(v8, &idx, 1);- vd->is_lexical = bc_get_flags(v8, &idx, 1);- vd->is_captured = bc_get_flags(v8, &idx, 1);-#ifdef DUMP_READ_OBJECT- bc_read_trace(s, "name: "); print_atom(s->ctx, vd->var_name); printf("\n");-#endif- }- bc_read_trace(s, "}\n");- }- if (b->closure_var_count != 0) {- bc_read_trace(s, "closure vars {\n");- b->closure_var = (void *)((uint8_t*)b + closure_var_offset);- for(i = 0; i < b->closure_var_count; i++) {- JSClosureVar *cv = &b->closure_var[i];- int var_idx;- if (bc_get_atom(s, &cv->var_name))- goto fail;- if (bc_get_leb128_int(s, &var_idx))- goto fail;- cv->var_idx = var_idx;- if (bc_get_u8(s, &v8))- goto fail;- idx = 0;- cv->is_local = bc_get_flags(v8, &idx, 1);- cv->is_arg = bc_get_flags(v8, &idx, 1);- cv->is_const = bc_get_flags(v8, &idx, 1);- cv->is_lexical = bc_get_flags(v8, &idx, 1);- cv->var_kind = bc_get_flags(v8, &idx, 4);-#ifdef DUMP_READ_OBJECT- bc_read_trace(s, "name: "); print_atom(s->ctx, cv->var_name); printf("\n");-#endif- }- bc_read_trace(s, "}\n");- }- {- bc_read_trace(s, "bytecode {\n");- if (JS_ReadFunctionBytecode(s, b, byte_code_offset, b->byte_code_len))- goto fail;- bc_read_trace(s, "}\n");- }- if (b->has_debug) {- /* read optional debug information */- bc_read_trace(s, "debug {\n");- if (bc_get_atom(s, &b->debug.filename))- goto fail;- if (bc_get_leb128_int(s, &b->debug.line_num))- goto fail;- if (bc_get_leb128_int(s, &b->debug.pc2line_len))- goto fail;- if (b->debug.pc2line_len) {- b->debug.pc2line_buf = js_mallocz(ctx, b->debug.pc2line_len);- if (!b->debug.pc2line_buf)- goto fail;- if (bc_get_buf(s, b->debug.pc2line_buf, b->debug.pc2line_len))- goto fail;- }-#ifdef DUMP_READ_OBJECT- bc_read_trace(s, "filename: "); print_atom(s->ctx, b->debug.filename); printf("\n");-#endif- bc_read_trace(s, "}\n");- }- if (b->cpool_count != 0) {- bc_read_trace(s, "cpool {\n");- b->cpool = (void *)((uint8_t*)b + cpool_offset);- for(i = 0; i < b->cpool_count; i++) {- JSValue val;- val = JS_ReadObjectRec(s);- if (JS_IsException(val))- goto fail;- b->cpool[i] = val;- }- bc_read_trace(s, "}\n");- }- b->realm = JS_DupContext(ctx);- return obj;- fail:- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--static JSValue JS_ReadModule(BCReaderState *s)-{- JSContext *ctx = s->ctx;- JSValue obj;- JSModuleDef *m = NULL;- JSAtom module_name;- int i;- uint8_t v8;- - if (bc_get_atom(s, &module_name))- goto fail;-#ifdef DUMP_READ_OBJECT- bc_read_trace(s, "name: "); print_atom(s->ctx, module_name); printf("\n");-#endif- m = js_new_module_def(ctx, module_name);- if (!m)- goto fail;- obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));- if (bc_get_leb128_int(s, &m->req_module_entries_count))- goto fail;- if (m->req_module_entries_count != 0) {- m->req_module_entries_size = m->req_module_entries_count;- m->req_module_entries = js_mallocz(ctx, sizeof(m->req_module_entries[0]) * m->req_module_entries_size);- if (!m->req_module_entries)- goto fail;- for(i = 0; i < m->req_module_entries_count; i++) {- JSReqModuleEntry *rme = &m->req_module_entries[i];- if (bc_get_atom(s, &rme->module_name))- goto fail;- }- }-- if (bc_get_leb128_int(s, &m->export_entries_count))- goto fail;- if (m->export_entries_count != 0) {- m->export_entries_size = m->export_entries_count;- m->export_entries = js_mallocz(ctx, sizeof(m->export_entries[0]) * m->export_entries_size);- if (!m->export_entries)- goto fail;- for(i = 0; i < m->export_entries_count; i++) {- JSExportEntry *me = &m->export_entries[i];- if (bc_get_u8(s, &v8))- goto fail;- me->export_type = v8;- if (me->export_type == JS_EXPORT_TYPE_LOCAL) {- if (bc_get_leb128_int(s, &me->u.local.var_idx))- goto fail;- } else {- if (bc_get_leb128_int(s, &me->u.req_module_idx))- goto fail;- if (bc_get_atom(s, &me->local_name))- goto fail;- }- if (bc_get_atom(s, &me->export_name))- goto fail;- }- }-- if (bc_get_leb128_int(s, &m->star_export_entries_count))- goto fail;- if (m->star_export_entries_count != 0) {- m->star_export_entries_size = m->star_export_entries_count;- m->star_export_entries = js_mallocz(ctx, sizeof(m->star_export_entries[0]) * m->star_export_entries_size);- if (!m->star_export_entries)- goto fail;- for(i = 0; i < m->star_export_entries_count; i++) {- JSStarExportEntry *se = &m->star_export_entries[i];- if (bc_get_leb128_int(s, &se->req_module_idx))- goto fail;- }- }-- if (bc_get_leb128_int(s, &m->import_entries_count))- goto fail;- if (m->import_entries_count != 0) {- m->import_entries_size = m->import_entries_count;- m->import_entries = js_mallocz(ctx, sizeof(m->import_entries[0]) * m->import_entries_size);- if (!m->import_entries)- goto fail;- for(i = 0; i < m->import_entries_count; i++) {- JSImportEntry *mi = &m->import_entries[i];- if (bc_get_leb128_int(s, &mi->var_idx))- goto fail;- if (bc_get_atom(s, &mi->import_name))- goto fail;- if (bc_get_leb128_int(s, &mi->req_module_idx))- goto fail;- }- }-- m->func_obj = JS_ReadObjectRec(s);- if (JS_IsException(m->func_obj))- goto fail;- return obj;- fail:- if (m) {- js_free_module_def(ctx, m);- }- return JS_EXCEPTION;-}--static JSValue JS_ReadObjectTag(BCReaderState *s)-{- JSContext *ctx = s->ctx;- JSValue obj;- uint32_t prop_count, i;- JSAtom atom;- JSValue val;- int ret;- - obj = JS_NewObject(ctx);- if (BC_add_object_ref(s, obj))- goto fail;- if (bc_get_leb128(s, &prop_count))- goto fail;- for(i = 0; i < prop_count; i++) {- if (bc_get_atom(s, &atom))- goto fail;-#ifdef DUMP_READ_OBJECT- bc_read_trace(s, "propname: "); print_atom(s->ctx, atom); printf("\n");-#endif- val = JS_ReadObjectRec(s);- if (JS_IsException(val)) {- JS_FreeAtom(ctx, atom);- goto fail;- }- ret = JS_DefinePropertyValue(ctx, obj, atom, val, JS_PROP_C_W_E);- JS_FreeAtom(ctx, atom);- if (ret < 0)- goto fail;- }- return obj;- fail:- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--static JSValue JS_ReadArray(BCReaderState *s, int tag)-{- JSContext *ctx = s->ctx;- JSValue obj;- uint32_t len, i;- JSValue val;- int ret, prop_flags;- BOOL is_template;-- obj = JS_NewArray(ctx);- if (BC_add_object_ref(s, obj))- goto fail;- is_template = (tag == BC_TAG_TEMPLATE_OBJECT);- if (bc_get_leb128(s, &len))- goto fail;- for(i = 0; i < len; i++) {- val = JS_ReadObjectRec(s);- if (JS_IsException(val))- goto fail;- if (is_template)- prop_flags = JS_PROP_ENUMERABLE;- else- prop_flags = JS_PROP_C_W_E;- ret = JS_DefinePropertyValueUint32(ctx, obj, i, val,- prop_flags);- if (ret < 0)- goto fail;- }- if (is_template) {- val = JS_ReadObjectRec(s);- if (JS_IsException(val))- goto fail;- if (!JS_IsUndefined(val)) {- ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_raw, val, 0);- if (ret < 0)- goto fail;- }- JS_PreventExtensions(ctx, obj);- }- return obj;- fail:- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--static JSValue JS_ReadTypedArray(BCReaderState *s)-{- JSContext *ctx = s->ctx;- JSValue obj = JS_UNDEFINED, array_buffer = JS_UNDEFINED;- uint8_t array_tag;- JSValueConst args[3];- uint32_t offset, len, idx;- - if (bc_get_u8(s, &array_tag))- return JS_EXCEPTION;- if (array_tag >= JS_TYPED_ARRAY_COUNT)- return JS_ThrowTypeError(ctx, "invalid typed array");- if (bc_get_leb128(s, &len))- return JS_EXCEPTION;- if (bc_get_leb128(s, &offset))- return JS_EXCEPTION;- /* XXX: this hack could be avoided if the typed array could be- created before the array buffer */- idx = s->objects_count;- if (BC_add_object_ref1(s, NULL))- goto fail;- array_buffer = JS_ReadObjectRec(s);- if (JS_IsException(array_buffer))- return JS_EXCEPTION;- if (!js_get_array_buffer(ctx, array_buffer)) {- JS_FreeValue(ctx, array_buffer);- return JS_EXCEPTION;- }- args[0] = array_buffer;- args[1] = JS_NewInt64(ctx, offset);- args[2] = JS_NewInt64(ctx, len);- obj = js_typed_array_constructor(ctx, JS_UNDEFINED,- 3, args,- JS_CLASS_UINT8C_ARRAY + array_tag);- if (JS_IsException(obj))- goto fail;- if (s->allow_reference) {- s->objects[idx] = JS_VALUE_GET_OBJ(obj);- }- JS_FreeValue(ctx, array_buffer);- return obj;- fail:- JS_FreeValue(ctx, array_buffer);- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--static JSValue JS_ReadArrayBuffer(BCReaderState *s)-{- JSContext *ctx = s->ctx;- uint32_t byte_length;- JSValue obj;- - if (bc_get_leb128(s, &byte_length))- return JS_EXCEPTION;- if (unlikely(s->buf_end - s->ptr < byte_length)) {- bc_read_error_end(s);- return JS_EXCEPTION;- }- obj = JS_NewArrayBufferCopy(ctx, s->ptr, byte_length);- if (JS_IsException(obj))- goto fail;- if (BC_add_object_ref(s, obj))- goto fail;- s->ptr += byte_length;- return obj;- fail:- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--static JSValue JS_ReadSharedArrayBuffer(BCReaderState *s)-{- JSContext *ctx = s->ctx;- uint32_t byte_length;- uint8_t *data_ptr;- JSValue obj;- uint64_t u64;- - if (bc_get_leb128(s, &byte_length))- return JS_EXCEPTION;- if (bc_get_u64(s, &u64))- return JS_EXCEPTION;- data_ptr = (uint8_t *)(uintptr_t)u64;- /* the SharedArrayBuffer is cloned */- obj = js_array_buffer_constructor3(ctx, JS_UNDEFINED, byte_length,- JS_CLASS_SHARED_ARRAY_BUFFER,- data_ptr,- NULL, NULL, FALSE);- if (JS_IsException(obj))- goto fail;- if (BC_add_object_ref(s, obj))- goto fail;- return obj;- fail:- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--static JSValue JS_ReadDate(BCReaderState *s)-{- JSContext *ctx = s->ctx;- JSValue val, obj = JS_UNDEFINED;-- val = JS_ReadObjectRec(s);- if (JS_IsException(val))- goto fail;- if (!JS_IsNumber(val)) {- JS_ThrowTypeError(ctx, "Number tag expected for date");- goto fail;- }- obj = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_DATE],- JS_CLASS_DATE);- if (JS_IsException(obj))- goto fail;- if (BC_add_object_ref(s, obj))- goto fail;- JS_SetObjectData(ctx, obj, val);- return obj;- fail:- JS_FreeValue(ctx, val);- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--static JSValue JS_ReadObjectValue(BCReaderState *s)-{- JSContext *ctx = s->ctx;- JSValue val, obj = JS_UNDEFINED;-- val = JS_ReadObjectRec(s);- if (JS_IsException(val))- goto fail;- obj = JS_ToObject(ctx, val);- if (JS_IsException(obj))- goto fail;- if (BC_add_object_ref(s, obj))- goto fail;- JS_FreeValue(ctx, val);- return obj;- fail:- JS_FreeValue(ctx, val);- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--static JSValue JS_ReadObjectRec(BCReaderState *s)-{- JSContext *ctx = s->ctx;- uint8_t tag;- JSValue obj = JS_UNDEFINED;-- if (js_check_stack_overflow(ctx->rt, 0))- return JS_ThrowStackOverflow(ctx);-- if (bc_get_u8(s, &tag))- return JS_EXCEPTION;-- bc_read_trace(s, "%s {\n", bc_tag_str[tag]);-- switch(tag) {- case BC_TAG_NULL:- obj = JS_NULL;- break;- case BC_TAG_UNDEFINED:- obj = JS_UNDEFINED;- break;- case BC_TAG_BOOL_FALSE:- case BC_TAG_BOOL_TRUE:- obj = JS_NewBool(ctx, tag - BC_TAG_BOOL_FALSE);- break;- case BC_TAG_INT32:- {- int32_t val;- if (bc_get_sleb128(s, &val))- return JS_EXCEPTION;- bc_read_trace(s, "%d\n", val);- obj = JS_NewInt32(ctx, val);- }- break;- case BC_TAG_FLOAT64:- {- JSFloat64Union u;- if (bc_get_u64(s, &u.u64))- return JS_EXCEPTION;- bc_read_trace(s, "%g\n", u.d);- obj = __JS_NewFloat64(ctx, u.d);- }- break;- case BC_TAG_STRING:- {- JSString *p;- p = JS_ReadString(s);- if (!p)- return JS_EXCEPTION;- obj = JS_MKPTR(JS_TAG_STRING, p);- }- break;- case BC_TAG_FUNCTION_BYTECODE:- if (!s->allow_bytecode)- goto invalid_tag;- obj = JS_ReadFunctionTag(s);- break;- case BC_TAG_MODULE:- if (!s->allow_bytecode)- goto invalid_tag;- obj = JS_ReadModule(s);- break;- case BC_TAG_OBJECT:- obj = JS_ReadObjectTag(s);- break;- case BC_TAG_ARRAY:- case BC_TAG_TEMPLATE_OBJECT:- obj = JS_ReadArray(s, tag);- break;- case BC_TAG_TYPED_ARRAY:- obj = JS_ReadTypedArray(s);- break;- case BC_TAG_ARRAY_BUFFER:- obj = JS_ReadArrayBuffer(s);- break;- case BC_TAG_SHARED_ARRAY_BUFFER:- if (!s->allow_sab || !ctx->rt->sab_funcs.sab_dup)- goto invalid_tag;- obj = JS_ReadSharedArrayBuffer(s);- break;- case BC_TAG_DATE:- obj = JS_ReadDate(s);- break;- case BC_TAG_OBJECT_VALUE:- obj = JS_ReadObjectValue(s);- break;-#ifdef CONFIG_BIGNUM- case BC_TAG_BIG_INT:- case BC_TAG_BIG_FLOAT:- case BC_TAG_BIG_DECIMAL:- obj = JS_ReadBigNum(s, tag);- break;-#endif- case BC_TAG_OBJECT_REFERENCE:- {- uint32_t val;- if (!s->allow_reference)- return JS_ThrowSyntaxError(ctx, "object references are not allowed");- if (bc_get_leb128(s, &val))- return JS_EXCEPTION;- bc_read_trace(s, "%u\n", val);- if (val >= s->objects_count) {- return JS_ThrowSyntaxError(ctx, "invalid object reference (%u >= %u)",- val, s->objects_count);- }- obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, s->objects[val]));- }- break;- default:- invalid_tag:- return JS_ThrowSyntaxError(ctx, "invalid tag (tag=%d pos=%u)",- tag, (unsigned int)(s->ptr - s->buf_start));- }- bc_read_trace(s, "}\n");- return obj;-}--static int JS_ReadObjectAtoms(BCReaderState *s)-{- uint8_t v8;- JSString *p;- int i;- JSAtom atom;-- if (bc_get_u8(s, &v8))- return -1;- /* XXX: could support byte swapped input */- if (v8 != BC_VERSION) {- JS_ThrowSyntaxError(s->ctx, "invalid version (%d expected=%d)",- v8, BC_VERSION);- return -1;- }- if (bc_get_leb128(s, &s->idx_to_atom_count))- return -1;-- bc_read_trace(s, "%d atom indexes {\n", s->idx_to_atom_count);-- if (s->idx_to_atom_count != 0) {- s->idx_to_atom = js_mallocz(s->ctx, s->idx_to_atom_count *- sizeof(s->idx_to_atom[0]));- if (!s->idx_to_atom)- return s->error_state = -1;- }- for(i = 0; i < s->idx_to_atom_count; i++) {- p = JS_ReadString(s);- if (!p)- return -1;- atom = JS_NewAtomStr(s->ctx, p);- if (atom == JS_ATOM_NULL)- return s->error_state = -1;- s->idx_to_atom[i] = atom;- if (s->is_rom_data && (atom != (i + s->first_atom)))- s->is_rom_data = FALSE; /* atoms must be relocated */- }- bc_read_trace(s, "}\n");- return 0;-}--static void bc_reader_free(BCReaderState *s)-{- int i;- if (s->idx_to_atom) {- for(i = 0; i < s->idx_to_atom_count; i++) {- JS_FreeAtom(s->ctx, s->idx_to_atom[i]);- }- js_free(s->ctx, s->idx_to_atom);- }- js_free(s->ctx, s->objects);-}--JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len,- int flags)-{- BCReaderState ss, *s = &ss;- JSValue obj;-- ctx->binary_object_count += 1;- ctx->binary_object_size += buf_len;-- memset(s, 0, sizeof(*s));- s->ctx = ctx;- s->buf_start = buf;- s->buf_end = buf + buf_len;- s->ptr = buf;- s->allow_bytecode = ((flags & JS_READ_OBJ_BYTECODE) != 0);- s->is_rom_data = ((flags & JS_READ_OBJ_ROM_DATA) != 0);- s->allow_sab = ((flags & JS_READ_OBJ_SAB) != 0);- s->allow_reference = ((flags & JS_READ_OBJ_REFERENCE) != 0);- if (s->allow_bytecode)- s->first_atom = JS_ATOM_END;- else- s->first_atom = 1;- if (JS_ReadObjectAtoms(s)) {- obj = JS_EXCEPTION;- } else {- obj = JS_ReadObjectRec(s);- }- bc_reader_free(s);- return obj;-}--/*******************************************************************/-/* runtime functions & objects */--static JSValue js_string_constructor(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv);-static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv);-static JSValue js_number_constructor(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv);--static int check_function(JSContext *ctx, JSValueConst obj)-{- if (likely(JS_IsFunction(ctx, obj)))- return 0;- JS_ThrowTypeError(ctx, "not a function");- return -1;-}--static int check_exception_free(JSContext *ctx, JSValue obj)-{- JS_FreeValue(ctx, obj);- return JS_IsException(obj);-}--static JSAtom find_atom(JSContext *ctx, const char *name)-{- JSAtom atom;- int len;-- if (*name == '[') {- name++;- len = strlen(name) - 1;- /* We assume 8 bit non null strings, which is the case for these- symbols */- for(atom = JS_ATOM_Symbol_toPrimitive; atom < JS_ATOM_END; atom++) {- JSAtomStruct *p = ctx->rt->atom_array[atom];- JSString *str = p;- if (str->len == len && !memcmp(str->u.str8, name, len))- return JS_DupAtom(ctx, atom);- }- abort();- } else {- atom = JS_NewAtom(ctx, name);- }- return atom;-}--static int JS_InstantiateFunctionListItem(JSContext *ctx, JSObject *p,- JSAtom atom, void *opaque)-{- const JSCFunctionListEntry *e = opaque;- JSValueConst obj = JS_MKPTR(JS_TAG_OBJECT, p);- JSValue val;- int prop_flags = e->prop_flags;-- switch(e->def_type) {- case JS_DEF_ALIAS:- {- JSAtom atom1 = find_atom(ctx, e->u.alias.name);- switch (e->u.alias.base) {- case -1:- val = JS_GetProperty(ctx, obj, atom1);- break;- case 0:- val = JS_GetProperty(ctx, ctx->global_obj, atom1);- break;- case 1:- val = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], atom1);- break;- default:- abort();- }- JS_FreeAtom(ctx, atom1);- goto setval;- }- case JS_DEF_CFUNC:- val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic,- e->name, e->u.func.length, e->u.func.cproto, e->magic);- setval:- if (atom == JS_ATOM_Symbol_toPrimitive) {- /* Symbol.toPrimitive functions are not writable */- prop_flags = JS_PROP_CONFIGURABLE;- } else if (atom == JS_ATOM_Symbol_hasInstance) {- /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */- prop_flags = 0;- }- break;- case JS_DEF_CGETSET:- case JS_DEF_CGETSET_MAGIC:- {- JSValue getter, setter;- char buf[64];-- getter = JS_UNDEFINED;- if (e->u.getset.get.generic) {- snprintf(buf, sizeof(buf), "get %s", e->name);- getter = JS_NewCFunction2(ctx, e->u.getset.get.generic,- buf, 0, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_getter_magic : JS_CFUNC_getter,- e->magic);- }- setter = JS_UNDEFINED;- if (e->u.getset.set.generic) {- snprintf(buf, sizeof(buf), "set %s", e->name);- setter = JS_NewCFunction2(ctx, e->u.getset.set.generic,- buf, 1, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_setter_magic : JS_CFUNC_setter,- e->magic);- }- JS_DefinePropertyGetSet(ctx, obj, atom, getter, setter, prop_flags);- return 0;- }- break;- case JS_DEF_PROP_STRING:- val = JS_NewAtomString(ctx, e->u.str);- break;- case JS_DEF_PROP_INT32:- val = JS_NewInt32(ctx, e->u.i32);- break;- case JS_DEF_PROP_INT64:- val = JS_NewInt64(ctx, e->u.i64);- break;- case JS_DEF_PROP_DOUBLE:- val = __JS_NewFloat64(ctx, e->u.f64);- break;- case JS_DEF_PROP_UNDEFINED:- val = JS_UNDEFINED;- break;- case JS_DEF_OBJECT:- val = JS_NewObject(ctx);- JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len);- break;- default:- abort();- }- JS_DefinePropertyValue(ctx, obj, atom, val, prop_flags);- return 0;-}--void JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj,- const JSCFunctionListEntry *tab, int len)-{- int i, prop_flags;-- for (i = 0; i < len; i++) {- const JSCFunctionListEntry *e = &tab[i];- JSAtom atom = find_atom(ctx, e->name);-- switch (e->def_type) {- case JS_DEF_CFUNC:- case JS_DEF_CGETSET:- case JS_DEF_CGETSET_MAGIC:- case JS_DEF_PROP_STRING:- case JS_DEF_ALIAS:- case JS_DEF_OBJECT:- prop_flags = JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE | (e->prop_flags & JS_PROP_ENUMERABLE);- JS_DefineAutoInitProperty(ctx, obj, atom,- JS_AUTOINIT_ID_PROP,- (void *)e, prop_flags);- break;- case JS_DEF_PROP_INT32:- case JS_DEF_PROP_INT64:- case JS_DEF_PROP_DOUBLE:- case JS_DEF_PROP_UNDEFINED:- {- JSObject *p = JS_VALUE_GET_OBJ(obj);- JS_InstantiateFunctionListItem(ctx, p, atom, (void *)e);- }- break;- default:- abort();- }- JS_FreeAtom(ctx, atom);- }-}--int JS_AddModuleExportList(JSContext *ctx, JSModuleDef *m,- const JSCFunctionListEntry *tab, int len)-{- int i;- for(i = 0; i < len; i++) {- if (JS_AddModuleExport(ctx, m, tab[i].name))- return -1;- }- return 0;-}--int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m,- const JSCFunctionListEntry *tab, int len)-{- int i;- JSValue val;-- for(i = 0; i < len; i++) {- const JSCFunctionListEntry *e = &tab[i];- switch(e->def_type) {- case JS_DEF_CFUNC:- val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic,- e->name, e->u.func.length, e->u.func.cproto, e->magic);- break;- case JS_DEF_PROP_STRING:- val = JS_NewString(ctx, e->u.str);- break;- case JS_DEF_PROP_INT32:- val = JS_NewInt32(ctx, e->u.i32);- break;- case JS_DEF_PROP_INT64:- val = JS_NewInt64(ctx, e->u.i64);- break;- case JS_DEF_PROP_DOUBLE:- val = __JS_NewFloat64(ctx, e->u.f64);- break;- case JS_DEF_OBJECT:- val = JS_NewObject(ctx);- JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len);- break;- default:- abort();- }- if (JS_SetModuleExport(ctx, m, e->name, val))- return -1;- }- return 0;-}--/* Note: 'func_obj' is not necessarily a constructor */-static void JS_SetConstructor2(JSContext *ctx,- JSValueConst func_obj,- JSValueConst proto,- int proto_flags, int ctor_flags)-{- JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype,- JS_DupValue(ctx, proto), proto_flags);- JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor,- JS_DupValue(ctx, func_obj),- ctor_flags);- set_cycle_flag(ctx, func_obj);- set_cycle_flag(ctx, proto);-}--void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj, - JSValueConst proto)-{- JS_SetConstructor2(ctx, func_obj, proto,- 0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);-}--static void JS_NewGlobalCConstructor2(JSContext *ctx,- JSValue func_obj,- const char *name,- JSValueConst proto)-{- JS_DefinePropertyValueStr(ctx, ctx->global_obj, name,- JS_DupValue(ctx, func_obj),- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);- JS_SetConstructor(ctx, func_obj, proto);- JS_FreeValue(ctx, func_obj);-}--static JSValueConst JS_NewGlobalCConstructor(JSContext *ctx, const char *name,- JSCFunction *func, int length,- JSValueConst proto)-{- JSValue func_obj;- func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor_or_func, 0);- JS_NewGlobalCConstructor2(ctx, func_obj, name, proto);- return func_obj;-}--static JSValueConst JS_NewGlobalCConstructorOnly(JSContext *ctx, const char *name,- JSCFunction *func, int length,- JSValueConst proto)-{- JSValue func_obj;- func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor, 0);- JS_NewGlobalCConstructor2(ctx, func_obj, name, proto);- return func_obj;-}--static JSValue js_global_eval(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return JS_EvalObject(ctx, ctx->global_obj, argv[0], JS_EVAL_TYPE_INDIRECT, -1);-}--static JSValue js_global_isNaN(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- double d;-- /* XXX: does this work for bigfloat? */- if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))- return JS_EXCEPTION;- return JS_NewBool(ctx, isnan(d));-}--static JSValue js_global_isFinite(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- BOOL res;- double d;- if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))- return JS_EXCEPTION;- res = isfinite(d);- return JS_NewBool(ctx, res);-}--/* Object class */--static JSValue JS_ToObject(JSContext *ctx, JSValueConst val)-{- int tag = JS_VALUE_GET_NORM_TAG(val);- JSValue obj;-- switch(tag) {- default:- case JS_TAG_NULL:- case JS_TAG_UNDEFINED:- return JS_ThrowTypeError(ctx, "cannot convert to object");- case JS_TAG_OBJECT:- case JS_TAG_EXCEPTION:- return JS_DupValue(ctx, val);-#ifdef CONFIG_BIGNUM- case JS_TAG_BIG_INT:- obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT);- goto set_value;- case JS_TAG_BIG_FLOAT:- obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_FLOAT);- goto set_value;- case JS_TAG_BIG_DECIMAL:- obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_DECIMAL);- goto set_value;-#endif- case JS_TAG_INT:- case JS_TAG_FLOAT64:- obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER);- goto set_value;- case JS_TAG_STRING:- /* XXX: should call the string constructor */- {- JSString *p1 = JS_VALUE_GET_STRING(val);- obj = JS_NewObjectClass(ctx, JS_CLASS_STRING);- JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0);- }- goto set_value;- case JS_TAG_BOOL:- obj = JS_NewObjectClass(ctx, JS_CLASS_BOOLEAN);- goto set_value;- case JS_TAG_SYMBOL:- obj = JS_NewObjectClass(ctx, JS_CLASS_SYMBOL);- set_value:- if (!JS_IsException(obj))- JS_SetObjectData(ctx, obj, JS_DupValue(ctx, val));- return obj;- }-}--static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val)-{- JSValue obj = JS_ToObject(ctx, val);- JS_FreeValue(ctx, val);- return obj;-}--static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d,- JSValueConst desc)-{- JSValue val, getter, setter;- int flags;-- if (!JS_IsObject(desc)) {- JS_ThrowTypeErrorNotAnObject(ctx);- return -1;- }- flags = 0;- val = JS_UNDEFINED;- getter = JS_UNDEFINED;- setter = JS_UNDEFINED;- if (JS_HasProperty(ctx, desc, JS_ATOM_configurable)) {- JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_configurable);- if (JS_IsException(prop))- goto fail;- flags |= JS_PROP_HAS_CONFIGURABLE;- if (JS_ToBoolFree(ctx, prop))- flags |= JS_PROP_CONFIGURABLE;- }- if (JS_HasProperty(ctx, desc, JS_ATOM_writable)) {- JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_writable);- if (JS_IsException(prop))- goto fail;- flags |= JS_PROP_HAS_WRITABLE;- if (JS_ToBoolFree(ctx, prop))- flags |= JS_PROP_WRITABLE;- }- if (JS_HasProperty(ctx, desc, JS_ATOM_enumerable)) {- JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_enumerable);- if (JS_IsException(prop))- goto fail;- flags |= JS_PROP_HAS_ENUMERABLE;- if (JS_ToBoolFree(ctx, prop))- flags |= JS_PROP_ENUMERABLE;- }- if (JS_HasProperty(ctx, desc, JS_ATOM_value)) {- flags |= JS_PROP_HAS_VALUE;- val = JS_GetProperty(ctx, desc, JS_ATOM_value);- if (JS_IsException(val))- goto fail;- }- if (JS_HasProperty(ctx, desc, JS_ATOM_get)) {- flags |= JS_PROP_HAS_GET;- getter = JS_GetProperty(ctx, desc, JS_ATOM_get);- if (JS_IsException(getter) ||- !(JS_IsUndefined(getter) || JS_IsFunction(ctx, getter))) {- JS_ThrowTypeError(ctx, "invalid getter");- goto fail;- }- }- if (JS_HasProperty(ctx, desc, JS_ATOM_set)) {- flags |= JS_PROP_HAS_SET;- setter = JS_GetProperty(ctx, desc, JS_ATOM_set);- if (JS_IsException(setter) ||- !(JS_IsUndefined(setter) || JS_IsFunction(ctx, setter))) {- JS_ThrowTypeError(ctx, "invalid setter");- goto fail;- }- }- if ((flags & (JS_PROP_HAS_SET | JS_PROP_HAS_GET)) &&- (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE))) {- JS_ThrowTypeError(ctx, "cannot have setter/getter and value or writable");- goto fail;- }- d->flags = flags;- d->value = val;- d->getter = getter;- d->setter = setter;- return 0;- fail:- JS_FreeValue(ctx, val);- JS_FreeValue(ctx, getter);- JS_FreeValue(ctx, setter);- return -1;-}--static __exception int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj,- JSAtom prop, JSValueConst desc,- int flags)-{- JSPropertyDescriptor d;- int ret;-- if (js_obj_to_desc(ctx, &d, desc) < 0)- return -1;-- ret = JS_DefineProperty(ctx, obj, prop,- d.value, d.getter, d.setter, d.flags | flags);- js_free_desc(ctx, &d);- return ret;-}--static __exception int JS_ObjectDefineProperties(JSContext *ctx,- JSValueConst obj,- JSValueConst properties)-{- JSValue props, desc;- JSObject *p;- JSPropertyEnum *atoms;- uint32_t len, i;- int ret = -1;-- if (!JS_IsObject(obj)) {- JS_ThrowTypeErrorNotAnObject(ctx);- return -1;- }- desc = JS_UNDEFINED;- props = JS_ToObject(ctx, properties);- if (JS_IsException(props))- return -1;- p = JS_VALUE_GET_OBJ(props);- if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK) < 0)- goto exception;- for(i = 0; i < len; i++) {- JS_FreeValue(ctx, desc);- desc = JS_GetProperty(ctx, props, atoms[i].atom);- if (JS_IsException(desc))- goto exception;- if (JS_DefinePropertyDesc(ctx, obj, atoms[i].atom, desc, JS_PROP_THROW) < 0)- goto exception;- }- ret = 0;--exception:- js_free_prop_enum(ctx, atoms, len);- JS_FreeValue(ctx, props);- JS_FreeValue(ctx, desc);- return ret;-}--static JSValue js_object_constructor(JSContext *ctx, JSValueConst new_target,- int argc, JSValueConst *argv)-{- JSValue ret;- if (!JS_IsUndefined(new_target) &&- JS_VALUE_GET_OBJ(new_target) !=- JS_VALUE_GET_OBJ(JS_GetActiveFunction(ctx))) {- ret = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);- } else {- int tag = JS_VALUE_GET_NORM_TAG(argv[0]);- switch(tag) {- case JS_TAG_NULL:- case JS_TAG_UNDEFINED:- ret = JS_NewObject(ctx);- break;- default:- ret = JS_ToObject(ctx, argv[0]);- break;- }- }- return ret;-}--static JSValue js_object_create(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValueConst proto, props;- JSValue obj;-- proto = argv[0];- if (!JS_IsObject(proto) && !JS_IsNull(proto))- return JS_ThrowTypeError(ctx, "not a prototype");- obj = JS_NewObjectProto(ctx, proto);- if (JS_IsException(obj))- return JS_EXCEPTION;- props = argv[1];- if (!JS_IsUndefined(props)) {- if (JS_ObjectDefineProperties(ctx, obj, props)) {- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;- }- }- return obj;-}--static JSValue js_object_getPrototypeOf(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- JSValueConst val;-- val = argv[0];- if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) {- /* ES6 feature non compatible with ES5.1: primitive types are- accepted */- if (magic || JS_VALUE_GET_TAG(val) == JS_TAG_NULL ||- JS_VALUE_GET_TAG(val) == JS_TAG_UNDEFINED)- return JS_ThrowTypeErrorNotAnObject(ctx);- }- return JS_GetPrototype(ctx, val);-}--static JSValue js_object_setPrototypeOf(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValueConst obj;- obj = argv[0];- if (JS_SetPrototypeInternal(ctx, obj, argv[1], TRUE) < 0)- return JS_EXCEPTION;- return JS_DupValue(ctx, obj);-}--/* magic = 1 if called as Reflect.defineProperty */-static JSValue js_object_defineProperty(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- JSValueConst obj, prop, desc;- int ret, flags;- JSAtom atom;-- obj = argv[0];- prop = argv[1];- desc = argv[2];-- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)- return JS_ThrowTypeErrorNotAnObject(ctx);- atom = JS_ValueToAtom(ctx, prop);- if (unlikely(atom == JS_ATOM_NULL))- return JS_EXCEPTION;- flags = 0;- if (!magic)- flags |= JS_PROP_THROW;- ret = JS_DefinePropertyDesc(ctx, obj, atom, desc, flags);- JS_FreeAtom(ctx, atom);- if (ret < 0) {- return JS_EXCEPTION;- } else if (magic) {- return JS_NewBool(ctx, ret);- } else {- return JS_DupValue(ctx, obj);- }-}--static JSValue js_object_defineProperties(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- // defineProperties(obj, properties)- JSValueConst obj = argv[0];-- if (JS_ObjectDefineProperties(ctx, obj, argv[1]))- return JS_EXCEPTION;- else- return JS_DupValue(ctx, obj);-}--/* magic = 1 if called as __defineSetter__ */-static JSValue js_object___defineGetter__(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- JSValue obj;- JSValueConst prop, value, get, set;- int ret, flags;- JSAtom atom;-- prop = argv[0];- value = argv[1];-- obj = JS_ToObject(ctx, this_val);- if (JS_IsException(obj))- return JS_EXCEPTION;-- if (check_function(ctx, value)) {- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;- }- atom = JS_ValueToAtom(ctx, prop);- if (unlikely(atom == JS_ATOM_NULL)) {- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;- }- flags = JS_PROP_THROW |- JS_PROP_HAS_ENUMERABLE | JS_PROP_ENUMERABLE |- JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE;- if (magic) {- get = JS_UNDEFINED;- set = value;- flags |= JS_PROP_HAS_SET;- } else {- get = value;- set = JS_UNDEFINED;- flags |= JS_PROP_HAS_GET;- }- ret = JS_DefineProperty(ctx, obj, atom, JS_UNDEFINED, get, set, flags);- JS_FreeValue(ctx, obj);- JS_FreeAtom(ctx, atom);- if (ret < 0) {- return JS_EXCEPTION;- } else {- return JS_UNDEFINED;- }-}--static JSValue js_object_getOwnPropertyDescriptor(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- JSValueConst prop;- JSAtom atom;- JSValue ret, obj;- JSPropertyDescriptor desc;- int res, flags;-- if (magic) {- /* Reflect.getOwnPropertyDescriptor case */- if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT)- return JS_ThrowTypeErrorNotAnObject(ctx);- obj = JS_DupValue(ctx, argv[0]);- } else {- obj = JS_ToObject(ctx, argv[0]);- if (JS_IsException(obj))- return obj;- }- prop = argv[1];- atom = JS_ValueToAtom(ctx, prop);- if (unlikely(atom == JS_ATOM_NULL))- goto exception;- ret = JS_UNDEFINED;- if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {- res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), atom);- if (res < 0)- goto exception;- if (res) {- ret = JS_NewObject(ctx);- if (JS_IsException(ret))- goto exception1;- flags = JS_PROP_C_W_E | JS_PROP_THROW;- if (desc.flags & JS_PROP_GETSET) {- if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, desc.getter), flags) < 0- || JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, desc.setter), flags) < 0)- goto exception1;- } else {- if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, desc.value), flags) < 0- || JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable,- JS_NewBool(ctx, (desc.flags & JS_PROP_WRITABLE) != 0), flags) < 0)- goto exception1;- }- if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable,- JS_NewBool(ctx, (desc.flags & JS_PROP_ENUMERABLE) != 0), flags) < 0- || JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable,- JS_NewBool(ctx, (desc.flags & JS_PROP_CONFIGURABLE) != 0), flags) < 0)- goto exception1;- js_free_desc(ctx, &desc);- }- }- JS_FreeAtom(ctx, atom);- JS_FreeValue(ctx, obj);- return ret;--exception1:- js_free_desc(ctx, &desc);- JS_FreeValue(ctx, ret);-exception:- JS_FreeAtom(ctx, atom);- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--static JSValue js_object_getOwnPropertyDescriptors(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- //getOwnPropertyDescriptors(obj)- JSValue obj, r;- JSObject *p;- JSPropertyEnum *props;- uint32_t len, i;-- r = JS_UNDEFINED;- obj = JS_ToObject(ctx, argv[0]);- if (JS_IsException(obj))- return JS_EXCEPTION;-- p = JS_VALUE_GET_OBJ(obj);- if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p,- JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK))- goto exception;- r = JS_NewObject(ctx);- if (JS_IsException(r))- goto exception;- for(i = 0; i < len; i++) {- JSValue atomValue, desc;- JSValueConst args[2];-- atomValue = JS_AtomToValue(ctx, props[i].atom);- if (JS_IsException(atomValue))- goto exception;- args[0] = obj;- args[1] = atomValue;- desc = js_object_getOwnPropertyDescriptor(ctx, JS_UNDEFINED, 2, args, 0);- JS_FreeValue(ctx, atomValue);- if (JS_IsException(desc))- goto exception;- if (!JS_IsUndefined(desc)) {- if (JS_DefinePropertyValue(ctx, r, props[i].atom, desc,- JS_PROP_C_W_E | JS_PROP_THROW) < 0)- goto exception;- }- }- js_free_prop_enum(ctx, props, len);- JS_FreeValue(ctx, obj);- return r;--exception:- js_free_prop_enum(ctx, props, len);- JS_FreeValue(ctx, obj);- JS_FreeValue(ctx, r);- return JS_EXCEPTION;-}--static JSValue JS_GetOwnPropertyNames2(JSContext *ctx, JSValueConst obj1,- int flags, int kind)-{- JSValue obj, r, val, key, value;- JSObject *p;- JSPropertyEnum *atoms;- uint32_t len, i, j;-- r = JS_UNDEFINED;- val = JS_UNDEFINED;- obj = JS_ToObject(ctx, obj1);- if (JS_IsException(obj))- return JS_EXCEPTION;- p = JS_VALUE_GET_OBJ(obj);- if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, flags & ~JS_GPN_ENUM_ONLY))- goto exception;- r = JS_NewArray(ctx);- if (JS_IsException(r))- goto exception;- for(j = i = 0; i < len; i++) {- JSAtom atom = atoms[i].atom;- if (flags & JS_GPN_ENUM_ONLY) {- JSPropertyDescriptor desc;- int res;-- /* Check if property is still enumerable */- res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);- if (res < 0)- goto exception;- if (!res)- continue;- js_free_desc(ctx, &desc);- if (!(desc.flags & JS_PROP_ENUMERABLE))- continue;- }- switch(kind) {- default:- case JS_ITERATOR_KIND_KEY:- val = JS_AtomToValue(ctx, atom);- if (JS_IsException(val))- goto exception;- break;- case JS_ITERATOR_KIND_VALUE:- val = JS_GetProperty(ctx, obj, atom);- if (JS_IsException(val))- goto exception;- break;- case JS_ITERATOR_KIND_KEY_AND_VALUE:- val = JS_NewArray(ctx);- if (JS_IsException(val))- goto exception;- key = JS_AtomToValue(ctx, atom);- if (JS_IsException(key))- goto exception1;- if (JS_CreateDataPropertyUint32(ctx, val, 0, key, JS_PROP_THROW) < 0)- goto exception1;- value = JS_GetProperty(ctx, obj, atom);- if (JS_IsException(value))- goto exception1;- if (JS_CreateDataPropertyUint32(ctx, val, 1, value, JS_PROP_THROW) < 0)- goto exception1;- break;- }- if (JS_CreateDataPropertyUint32(ctx, r, j++, val, 0) < 0)- goto exception;- }- goto done;--exception1:- JS_FreeValue(ctx, val);-exception:- JS_FreeValue(ctx, r);- r = JS_EXCEPTION;-done:- js_free_prop_enum(ctx, atoms, len);- JS_FreeValue(ctx, obj);- return r;-}--static JSValue js_object_getOwnPropertyNames(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return JS_GetOwnPropertyNames2(ctx, argv[0],- JS_GPN_STRING_MASK, JS_ITERATOR_KIND_KEY);-}--static JSValue js_object_getOwnPropertySymbols(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return JS_GetOwnPropertyNames2(ctx, argv[0],- JS_GPN_SYMBOL_MASK, JS_ITERATOR_KIND_KEY);-}--static JSValue js_object_keys(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int kind)-{- return JS_GetOwnPropertyNames2(ctx, argv[0],- JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK, kind);-}--static JSValue js_object_isExtensible(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int reflect)-{- JSValueConst obj;- int ret;-- obj = argv[0];- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {- if (reflect)- return JS_ThrowTypeErrorNotAnObject(ctx);- else- return JS_FALSE;- }- ret = JS_IsExtensible(ctx, obj);- if (ret < 0)- return JS_EXCEPTION;- else- return JS_NewBool(ctx, ret);-}--static JSValue js_object_preventExtensions(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int reflect)-{- JSValueConst obj;- int ret;-- obj = argv[0];- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {- if (reflect)- return JS_ThrowTypeErrorNotAnObject(ctx);- else- return JS_DupValue(ctx, obj);- }- ret = JS_PreventExtensions(ctx, obj);- if (ret < 0)- return JS_EXCEPTION;- if (reflect) {- return JS_NewBool(ctx, ret);- } else {- if (!ret)- return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false");- return JS_DupValue(ctx, obj);- }-}--static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue obj;- JSAtom atom;- JSObject *p;- BOOL ret;-- atom = JS_ValueToAtom(ctx, argv[0]); /* must be done first */- if (unlikely(atom == JS_ATOM_NULL))- return JS_EXCEPTION;- obj = JS_ToObject(ctx, this_val);- if (JS_IsException(obj)) {- JS_FreeAtom(ctx, atom);- return obj;- }- p = JS_VALUE_GET_OBJ(obj);- ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom);- JS_FreeAtom(ctx, atom);- JS_FreeValue(ctx, obj);- if (ret < 0)- return JS_EXCEPTION;- else- return JS_NewBool(ctx, ret);-}--static JSValue js_object_valueOf(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return JS_ToObject(ctx, this_val);-}--static JSValue js_object_toString(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue obj, tag;- int is_array;- JSAtom atom;- JSObject *p;-- if (JS_IsNull(this_val)) {- tag = JS_NewString(ctx, "Null");- } else if (JS_IsUndefined(this_val)) {- tag = JS_NewString(ctx, "Undefined");- } else {- obj = JS_ToObject(ctx, this_val);- if (JS_IsException(obj))- return obj;- is_array = JS_IsArray(ctx, obj);- if (is_array < 0) {- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;- }- if (is_array) {- atom = JS_ATOM_Array;- } else if (JS_IsFunction(ctx, obj)) {- atom = JS_ATOM_Function;- } else {- p = JS_VALUE_GET_OBJ(obj);- switch(p->class_id) {- case JS_CLASS_STRING:- case JS_CLASS_ARGUMENTS:- case JS_CLASS_MAPPED_ARGUMENTS:- case JS_CLASS_ERROR:- case JS_CLASS_BOOLEAN:- case JS_CLASS_NUMBER:- case JS_CLASS_DATE:- case JS_CLASS_REGEXP:- atom = ctx->rt->class_array[p->class_id].class_name;- break;- default:- atom = JS_ATOM_Object;- break;- }- }- tag = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_toStringTag);- JS_FreeValue(ctx, obj);- if (JS_IsException(tag))- return JS_EXCEPTION;- if (!JS_IsString(tag)) {- JS_FreeValue(ctx, tag);- tag = JS_AtomToString(ctx, atom);- }- }- return JS_ConcatString3(ctx, "[object ", tag, "]");-}--static JSValue js_object_toLocaleString(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return JS_Invoke(ctx, this_val, JS_ATOM_toString, 0, NULL);-}--static JSValue js_object_assign(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- // Object.assign(obj, source1)- JSValue obj, s;- int i;-- s = JS_UNDEFINED;- obj = JS_ToObject(ctx, argv[0]);- if (JS_IsException(obj))- goto exception;- for (i = 1; i < argc; i++) {- if (!JS_IsNull(argv[i]) && !JS_IsUndefined(argv[i])) {- s = JS_ToObject(ctx, argv[i]);- if (JS_IsException(s))- goto exception;- if (JS_CopyDataProperties(ctx, obj, s, JS_UNDEFINED, TRUE))- goto exception;- JS_FreeValue(ctx, s);- }- }- return obj;-exception:- JS_FreeValue(ctx, obj);- JS_FreeValue(ctx, s);- return JS_EXCEPTION;-}--static JSValue js_object_seal(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int freeze_flag)-{- JSValueConst obj = argv[0];- JSObject *p;- JSPropertyEnum *props;- uint32_t len, i;- int flags, desc_flags, res;-- if (!JS_IsObject(obj))- return JS_DupValue(ctx, obj);-- res = JS_PreventExtensions(ctx, obj);- if (res < 0)- return JS_EXCEPTION;- if (!res) {- return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false");- }- - p = JS_VALUE_GET_OBJ(obj);- flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK;- if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags))- return JS_EXCEPTION;-- for(i = 0; i < len; i++) {- JSPropertyDescriptor desc;- JSAtom prop = props[i].atom;-- desc_flags = JS_PROP_THROW | JS_PROP_HAS_CONFIGURABLE;- if (freeze_flag) {- res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);- if (res < 0)- goto exception;- if (res) {- if (desc.flags & JS_PROP_WRITABLE)- desc_flags |= JS_PROP_HAS_WRITABLE;- js_free_desc(ctx, &desc);- }- }- if (JS_DefineProperty(ctx, obj, prop, JS_UNDEFINED,- JS_UNDEFINED, JS_UNDEFINED, desc_flags) < 0)- goto exception;- }- js_free_prop_enum(ctx, props, len);- return JS_DupValue(ctx, obj);-- exception:- js_free_prop_enum(ctx, props, len);- return JS_EXCEPTION;-}--static JSValue js_object_isSealed(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int is_frozen)-{- JSValueConst obj = argv[0];- JSObject *p;- JSPropertyEnum *props;- uint32_t len, i;- int flags, res;- - if (!JS_IsObject(obj))- return JS_TRUE;-- p = JS_VALUE_GET_OBJ(obj);- flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK;- if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags))- return JS_EXCEPTION;-- for(i = 0; i < len; i++) {- JSPropertyDescriptor desc;- JSAtom prop = props[i].atom;-- res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);- if (res < 0)- goto exception;- if (res) {- js_free_desc(ctx, &desc);- if ((desc.flags & JS_PROP_CONFIGURABLE)- || (is_frozen && (desc.flags & JS_PROP_WRITABLE))) {- res = FALSE;- goto done;- }- }- }- res = JS_IsExtensible(ctx, obj);- if (res < 0)- return JS_EXCEPTION;- res ^= 1;-done: - js_free_prop_enum(ctx, props, len);- return JS_NewBool(ctx, res);--exception:- js_free_prop_enum(ctx, props, len);- return JS_EXCEPTION;-}--static JSValue js_object_fromEntries(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue obj, iter, next_method = JS_UNDEFINED;- JSValueConst iterable;- BOOL done;-- /* RequireObjectCoercible() not necessary because it is tested in- JS_GetIterator() by JS_GetProperty() */- iterable = argv[0];-- obj = JS_NewObject(ctx);- if (JS_IsException(obj))- return obj;- - iter = JS_GetIterator(ctx, iterable, FALSE);- if (JS_IsException(iter))- goto fail;- next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);- if (JS_IsException(next_method))- goto fail;- - for(;;) {- JSValue key, value, item;- item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);- if (JS_IsException(item))- goto fail;- if (done) {- JS_FreeValue(ctx, item);- break;- }- - key = JS_UNDEFINED;- value = JS_UNDEFINED;- if (!JS_IsObject(item)) {- JS_ThrowTypeErrorNotAnObject(ctx);- goto fail1;- }- key = JS_GetPropertyUint32(ctx, item, 0);- if (JS_IsException(key))- goto fail1;- value = JS_GetPropertyUint32(ctx, item, 1);- if (JS_IsException(value)) {- JS_FreeValue(ctx, key);- goto fail1;- }- if (JS_DefinePropertyValueValue(ctx, obj, key, value,- JS_PROP_C_W_E | JS_PROP_THROW) < 0) {- fail1:- JS_FreeValue(ctx, item);- goto fail;- }- JS_FreeValue(ctx, item);- }- JS_FreeValue(ctx, next_method);- JS_FreeValue(ctx, iter);- return obj;- fail:- if (JS_IsObject(iter)) {- /* close the iterator object, preserving pending exception */- JS_IteratorClose(ctx, iter, TRUE);- }- JS_FreeValue(ctx, next_method);- JS_FreeValue(ctx, iter);- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--#if 0-/* Note: corresponds to ECMA spec: CreateDataPropertyOrThrow() */-static JSValue js_object___setOwnProperty(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- int ret;- ret = JS_DefinePropertyValueValue(ctx, argv[0], JS_DupValue(ctx, argv[1]),- JS_DupValue(ctx, argv[2]),- JS_PROP_C_W_E | JS_PROP_THROW);- if (ret < 0)- return JS_EXCEPTION;- else- return JS_NewBool(ctx, ret);-}--static JSValue js_object___toObject(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return JS_ToObject(ctx, argv[0]);-}--static JSValue js_object___toPrimitive(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- int hint = HINT_NONE;-- if (JS_VALUE_GET_TAG(argv[1]) == JS_TAG_INT)- hint = JS_VALUE_GET_INT(argv[1]);-- return JS_ToPrimitive(ctx, argv[0], hint);-}-#endif--/* return an empty string if not an object */-static JSValue js_object___getClass(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSAtom atom;- JSObject *p;- uint32_t tag;- int class_id;-- tag = JS_VALUE_GET_NORM_TAG(argv[0]);- if (tag == JS_TAG_OBJECT) {- p = JS_VALUE_GET_OBJ(argv[0]);- class_id = p->class_id;- if (class_id == JS_CLASS_PROXY && JS_IsFunction(ctx, argv[0]))- class_id = JS_CLASS_BYTECODE_FUNCTION;- atom = ctx->rt->class_array[class_id].class_name;- } else {- atom = JS_ATOM_empty_string;- }- return JS_AtomToString(ctx, atom);-}--static JSValue js_object_is(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return JS_NewBool(ctx, js_same_value(ctx, argv[0], argv[1]));-}--#if 0-static JSValue js_object___getObjectData(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return JS_GetObjectData(ctx, argv[0]);-}--static JSValue js_object___setObjectData(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- if (JS_SetObjectData(ctx, argv[0], JS_DupValue(ctx, argv[1])))- return JS_EXCEPTION;- return JS_DupValue(ctx, argv[1]);-}--static JSValue js_object___toPropertyKey(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return JS_ToPropertyKey(ctx, argv[0]);-}--static JSValue js_object___isObject(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return JS_NewBool(ctx, JS_IsObject(argv[0]));-}--static JSValue js_object___isSameValueZero(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return JS_NewBool(ctx, js_same_value_zero(ctx, argv[0], argv[1]));-}--static JSValue js_object___isConstructor(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return JS_NewBool(ctx, JS_IsConstructor(ctx, argv[0]));-}-#endif--static JSValue JS_SpeciesConstructor(JSContext *ctx, JSValueConst obj,- JSValueConst defaultConstructor)-{- JSValue ctor, species;-- if (!JS_IsObject(obj))- return JS_ThrowTypeErrorNotAnObject(ctx);- ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor);- if (JS_IsException(ctor))- return ctor;- if (JS_IsUndefined(ctor))- return JS_DupValue(ctx, defaultConstructor);- if (!JS_IsObject(ctor)) {- JS_FreeValue(ctx, ctor);- return JS_ThrowTypeErrorNotAnObject(ctx);- }- species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species);- JS_FreeValue(ctx, ctor);- if (JS_IsException(species))- return species;- if (JS_IsUndefined(species) || JS_IsNull(species))- return JS_DupValue(ctx, defaultConstructor);- if (!JS_IsConstructor(ctx, species)) {- JS_FreeValue(ctx, species);- return JS_ThrowTypeError(ctx, "not a constructor");- }- return species;-}--#if 0-static JSValue js_object___speciesConstructor(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return JS_SpeciesConstructor(ctx, argv[0], argv[1]);-}-#endif--static JSValue js_object_get___proto__(JSContext *ctx, JSValueConst this_val)-{- JSValue val, ret;-- val = JS_ToObject(ctx, this_val);- if (JS_IsException(val))- return val;- ret = JS_GetPrototype(ctx, val);- JS_FreeValue(ctx, val);- return ret;-}--static JSValue js_object_set___proto__(JSContext *ctx, JSValueConst this_val,- JSValueConst proto)-{- if (JS_IsUndefined(this_val) || JS_IsNull(this_val))- return JS_ThrowTypeErrorNotAnObject(ctx);- if (!JS_IsObject(proto) && !JS_IsNull(proto))- return JS_UNDEFINED;- if (JS_SetPrototypeInternal(ctx, this_val, proto, TRUE) < 0)- return JS_EXCEPTION;- else- return JS_UNDEFINED;-}--static JSValue js_object_isPrototypeOf(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue obj, v1;- JSValueConst v;- int res;-- v = argv[0];- if (!JS_IsObject(v))- return JS_FALSE;- obj = JS_ToObject(ctx, this_val);- if (JS_IsException(obj))- return JS_EXCEPTION;- v1 = JS_DupValue(ctx, v);- for(;;) {- v1 = JS_GetPrototypeFree(ctx, v1);- if (JS_IsException(v1))- goto exception;- if (JS_IsNull(v1)) {- res = FALSE;- break;- }- if (JS_VALUE_GET_OBJ(obj) == JS_VALUE_GET_OBJ(v1)) {- res = TRUE;- break;- }- /* avoid infinite loop (possible with proxies) */- if (js_poll_interrupts(ctx))- goto exception;- }- JS_FreeValue(ctx, v1);- JS_FreeValue(ctx, obj);- return JS_NewBool(ctx, res);--exception:- JS_FreeValue(ctx, v1);- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--static JSValue js_object_propertyIsEnumerable(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue obj, res = JS_EXCEPTION;- JSAtom prop = JS_ATOM_NULL;- JSPropertyDescriptor desc;- int has_prop;-- obj = JS_ToObject(ctx, this_val);- if (JS_IsException(obj))- goto exception;- prop = JS_ValueToAtom(ctx, argv[0]);- if (unlikely(prop == JS_ATOM_NULL))- goto exception;-- has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop);- if (has_prop < 0)- goto exception;- if (has_prop) {- res = JS_NewBool(ctx, (desc.flags & JS_PROP_ENUMERABLE) != 0);- js_free_desc(ctx, &desc);- } else {- res = JS_FALSE;- }--exception:- JS_FreeAtom(ctx, prop);- JS_FreeValue(ctx, obj);- return res;-}--static JSValue js_object___lookupGetter__(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int setter)-{- JSValue obj, res = JS_EXCEPTION;- JSAtom prop = JS_ATOM_NULL;- JSPropertyDescriptor desc;- int has_prop;-- obj = JS_ToObject(ctx, this_val);- if (JS_IsException(obj))- goto exception;- prop = JS_ValueToAtom(ctx, argv[0]);- if (unlikely(prop == JS_ATOM_NULL))- goto exception;-- for (;;) {- has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop);- if (has_prop < 0)- goto exception;- if (has_prop) {- if (desc.flags & JS_PROP_GETSET)- res = JS_DupValue(ctx, setter ? desc.setter : desc.getter);- else- res = JS_UNDEFINED;- js_free_desc(ctx, &desc);- break;- }- obj = JS_GetPrototypeFree(ctx, obj);- if (JS_IsException(obj))- goto exception;- if (JS_IsNull(obj)) {- res = JS_UNDEFINED;- break;- }- /* avoid infinite loop (possible with proxies) */- if (js_poll_interrupts(ctx))- goto exception;- }--exception:- JS_FreeAtom(ctx, prop);- JS_FreeValue(ctx, obj);- return res;-}--static const JSCFunctionListEntry js_object_funcs[] = {- JS_CFUNC_DEF("create", 2, js_object_create ),- JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 0 ),- JS_CFUNC_DEF("setPrototypeOf", 2, js_object_setPrototypeOf ),- JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 0 ),- JS_CFUNC_DEF("defineProperties", 2, js_object_defineProperties ),- JS_CFUNC_DEF("getOwnPropertyNames", 1, js_object_getOwnPropertyNames ),- JS_CFUNC_DEF("getOwnPropertySymbols", 1, js_object_getOwnPropertySymbols ),- JS_CFUNC_MAGIC_DEF("keys", 1, js_object_keys, JS_ITERATOR_KIND_KEY ),- JS_CFUNC_MAGIC_DEF("values", 1, js_object_keys, JS_ITERATOR_KIND_VALUE ),- JS_CFUNC_MAGIC_DEF("entries", 1, js_object_keys, JS_ITERATOR_KIND_KEY_AND_VALUE ),- JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 0 ),- JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 0 ),- JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 0 ),- JS_CFUNC_DEF("getOwnPropertyDescriptors", 1, js_object_getOwnPropertyDescriptors ),- JS_CFUNC_DEF("is", 2, js_object_is ),- JS_CFUNC_DEF("assign", 2, js_object_assign ),- JS_CFUNC_MAGIC_DEF("seal", 1, js_object_seal, 0 ),- JS_CFUNC_MAGIC_DEF("freeze", 1, js_object_seal, 1 ),- JS_CFUNC_MAGIC_DEF("isSealed", 1, js_object_isSealed, 0 ),- JS_CFUNC_MAGIC_DEF("isFrozen", 1, js_object_isSealed, 1 ),- JS_CFUNC_DEF("__getClass", 1, js_object___getClass ),- //JS_CFUNC_DEF("__isObject", 1, js_object___isObject ),- //JS_CFUNC_DEF("__isConstructor", 1, js_object___isConstructor ),- //JS_CFUNC_DEF("__toObject", 1, js_object___toObject ),- //JS_CFUNC_DEF("__setOwnProperty", 3, js_object___setOwnProperty ),- //JS_CFUNC_DEF("__toPrimitive", 2, js_object___toPrimitive ),- //JS_CFUNC_DEF("__toPropertyKey", 1, js_object___toPropertyKey ),- //JS_CFUNC_DEF("__speciesConstructor", 2, js_object___speciesConstructor ),- //JS_CFUNC_DEF("__isSameValueZero", 2, js_object___isSameValueZero ),- //JS_CFUNC_DEF("__getObjectData", 1, js_object___getObjectData ),- //JS_CFUNC_DEF("__setObjectData", 2, js_object___setObjectData ),- JS_CFUNC_DEF("fromEntries", 1, js_object_fromEntries ),-};--static const JSCFunctionListEntry js_object_proto_funcs[] = {- JS_CFUNC_DEF("toString", 0, js_object_toString ),- JS_CFUNC_DEF("toLocaleString", 0, js_object_toLocaleString ),- JS_CFUNC_DEF("valueOf", 0, js_object_valueOf ),- JS_CFUNC_DEF("hasOwnProperty", 1, js_object_hasOwnProperty ),- JS_CFUNC_DEF("isPrototypeOf", 1, js_object_isPrototypeOf ),- JS_CFUNC_DEF("propertyIsEnumerable", 1, js_object_propertyIsEnumerable ),- JS_CGETSET_DEF("__proto__", js_object_get___proto__, js_object_set___proto__ ),- JS_CFUNC_MAGIC_DEF("__defineGetter__", 2, js_object___defineGetter__, 0 ),- JS_CFUNC_MAGIC_DEF("__defineSetter__", 2, js_object___defineGetter__, 1 ),- JS_CFUNC_MAGIC_DEF("__lookupGetter__", 1, js_object___lookupGetter__, 0 ),- JS_CFUNC_MAGIC_DEF("__lookupSetter__", 1, js_object___lookupGetter__, 1 ),-};--/* Function class */--static JSValue js_function_proto(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return JS_UNDEFINED;-}--/* XXX: add a specific eval mode so that Function("}), ({") is rejected */-static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target,- int argc, JSValueConst *argv, int magic)-{- JSFunctionKindEnum func_kind = magic;- int i, n, ret;- JSValue s, proto, obj = JS_UNDEFINED;- StringBuffer b_s, *b = &b_s;-- string_buffer_init(ctx, b, 0);- string_buffer_putc8(b, '(');- - if (func_kind == JS_FUNC_ASYNC || func_kind == JS_FUNC_ASYNC_GENERATOR) {- string_buffer_puts8(b, "async ");- }- string_buffer_puts8(b, "function");-- if (func_kind == JS_FUNC_GENERATOR || func_kind == JS_FUNC_ASYNC_GENERATOR) {- string_buffer_putc8(b, '*');- }- string_buffer_puts8(b, " anonymous(");-- n = argc - 1;- for(i = 0; i < n; i++) {- if (i != 0) {- string_buffer_putc8(b, ',');- }- if (string_buffer_concat_value(b, argv[i]))- goto fail;- }- string_buffer_puts8(b, "\n) {\n");- if (n >= 0) {- if (string_buffer_concat_value(b, argv[n]))- goto fail;- }- string_buffer_puts8(b, "\n})");- s = string_buffer_end(b);- if (JS_IsException(s))- goto fail1;-- obj = JS_EvalObject(ctx, ctx->global_obj, s, JS_EVAL_TYPE_INDIRECT, -1);- JS_FreeValue(ctx, s);- if (JS_IsException(obj))- goto fail1;- if (!JS_IsUndefined(new_target)) {- /* set the prototype */- proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype);- if (JS_IsException(proto))- goto fail1;- if (!JS_IsObject(proto)) {- JSContext *realm;- JS_FreeValue(ctx, proto);- realm = JS_GetFunctionRealm(ctx, new_target);- if (!realm)- goto fail1;- proto = JS_DupValue(ctx, realm->class_proto[func_kind_to_class_id[func_kind]]);- }- ret = JS_SetPrototypeInternal(ctx, obj, proto, TRUE);- JS_FreeValue(ctx, proto);- if (ret < 0)- goto fail1;- }- return obj;-- fail:- string_buffer_free(b);- fail1:- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--static __exception int js_get_length32(JSContext *ctx, uint32_t *pres,- JSValueConst obj)-{- JSValue len_val;- len_val = JS_GetProperty(ctx, obj, JS_ATOM_length);- if (JS_IsException(len_val)) {- *pres = 0;- return -1;- }- return JS_ToUint32Free(ctx, pres, len_val);-}--static __exception int js_get_length64(JSContext *ctx, int64_t *pres,- JSValueConst obj)-{- JSValue len_val;- len_val = JS_GetProperty(ctx, obj, JS_ATOM_length);- if (JS_IsException(len_val)) {- *pres = 0;- return -1;- }- return JS_ToLengthFree(ctx, pres, len_val);-}--static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len)-{- uint32_t i;- for(i = 0; i < len; i++) {- JS_FreeValue(ctx, tab[i]);- }- js_free(ctx, tab);-}--/* XXX: should use ValueArray */-static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen,- JSValueConst array_arg)-{- uint32_t len, i;- JSValue *tab, ret;- JSObject *p;-- if (JS_VALUE_GET_TAG(array_arg) != JS_TAG_OBJECT) {- JS_ThrowTypeError(ctx, "not a object");- return NULL;- }- if (js_get_length32(ctx, &len, array_arg))- return NULL;- /* avoid allocating 0 bytes */- tab = js_mallocz(ctx, sizeof(tab[0]) * max_uint32(1, len));- if (!tab)- return NULL;- p = JS_VALUE_GET_OBJ(array_arg);- if ((p->class_id == JS_CLASS_ARRAY || p->class_id == JS_CLASS_ARGUMENTS) &&- p->fast_array &&- len == p->u.array.count) {- for(i = 0; i < len; i++) {- tab[i] = JS_DupValue(ctx, p->u.array.u.values[i]);- }- } else {- for(i = 0; i < len; i++) {- ret = JS_GetPropertyUint32(ctx, array_arg, i);- if (JS_IsException(ret)) {- free_arg_list(ctx, tab, i);- return NULL;- }- tab[i] = ret;- }- }- *plen = len;- return tab;-}--static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- JSValueConst this_arg, array_arg;- uint32_t len;- JSValue *tab, ret;-- if (check_function(ctx, this_val))- return JS_EXCEPTION;- this_arg = argv[0];- array_arg = argv[1];- if (JS_VALUE_GET_TAG(array_arg) == JS_TAG_UNDEFINED ||- JS_VALUE_GET_TAG(array_arg) == JS_TAG_NULL) {- return JS_Call(ctx, this_val, this_arg, 0, NULL);- }- tab = build_arg_list(ctx, &len, array_arg);- if (!tab)- return JS_EXCEPTION;- if (magic) {- ret = JS_CallConstructor2(ctx, this_val, this_arg, len, (JSValueConst *)tab);- } else {- ret = JS_Call(ctx, this_val, this_arg, len, (JSValueConst *)tab);- }- free_arg_list(ctx, tab, len);- return ret;-}--static JSValue js_function_call(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- if (argc <= 0) {- return JS_Call(ctx, this_val, JS_UNDEFINED, 0, NULL);- } else {- return JS_Call(ctx, this_val, argv[0], argc - 1, argv + 1);- }-}--static JSValue js_function_bind(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSBoundFunction *bf;- JSValue func_obj, name1, len_val;- JSObject *p;- int arg_count, i, ret;-- if (check_function(ctx, this_val))- return JS_EXCEPTION;-- func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,- JS_CLASS_BOUND_FUNCTION);- if (JS_IsException(func_obj))- return JS_EXCEPTION;- p = JS_VALUE_GET_OBJ(func_obj);- p->is_constructor = JS_IsConstructor(ctx, this_val);- arg_count = max_int(0, argc - 1);- bf = js_malloc(ctx, sizeof(*bf) + arg_count * sizeof(JSValue));- if (!bf)- goto exception;- bf->func_obj = JS_DupValue(ctx, this_val);- bf->this_val = JS_DupValue(ctx, argv[0]);- bf->argc = arg_count;- for(i = 0; i < arg_count; i++) {- bf->argv[i] = JS_DupValue(ctx, argv[i + 1]);- }- p->u.bound_function = bf;-- /* XXX: the spec could be simpler by only using GetOwnProperty */- ret = JS_GetOwnProperty(ctx, NULL, this_val, JS_ATOM_length);- if (ret < 0)- goto exception;- if (!ret) {- len_val = JS_NewInt32(ctx, 0);- } else {- len_val = JS_GetProperty(ctx, this_val, JS_ATOM_length);- if (JS_IsException(len_val))- goto exception;- if (JS_VALUE_GET_TAG(len_val) == JS_TAG_INT) {- /* most common case */- int len1 = JS_VALUE_GET_INT(len_val);- if (len1 <= arg_count)- len1 = 0;- else- len1 -= arg_count;- len_val = JS_NewInt32(ctx, len1);- } else if (JS_VALUE_GET_NORM_TAG(len_val) == JS_TAG_FLOAT64) {- double d = JS_VALUE_GET_FLOAT64(len_val);- if (isnan(d)) {- d = 0.0;- } else {- d = trunc(d);- if (d <= (double)arg_count)- d = 0.0;- else- d -= (double)arg_count; /* also converts -0 to +0 */- }- len_val = JS_NewFloat64(ctx, d);- } else {- JS_FreeValue(ctx, len_val);- len_val = JS_NewInt32(ctx, 0);- }- }- JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length,- len_val, JS_PROP_CONFIGURABLE);-- name1 = JS_GetProperty(ctx, this_val, JS_ATOM_name);- if (JS_IsException(name1))- goto exception;- if (!JS_IsString(name1)) {- JS_FreeValue(ctx, name1);- name1 = JS_AtomToString(ctx, JS_ATOM_empty_string);- }- name1 = JS_ConcatString3(ctx, "bound ", name1, "");- if (JS_IsException(name1))- goto exception;- JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name1,- JS_PROP_CONFIGURABLE);- return func_obj;- exception:- JS_FreeValue(ctx, func_obj);- return JS_EXCEPTION;-}--static JSValue js_function_toString(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSObject *p;- JSFunctionKindEnum func_kind = JS_FUNC_NORMAL;-- if (check_function(ctx, this_val))- return JS_EXCEPTION;-- p = JS_VALUE_GET_OBJ(this_val);- if (js_class_has_bytecode(p->class_id)) {- JSFunctionBytecode *b = p->u.func.function_bytecode;- if (b->has_debug && b->debug.source) {- return JS_NewStringLen(ctx, b->debug.source, b->debug.source_len);- }- func_kind = b->func_kind;- }- {- JSValue name;- const char *pref, *suff;-- switch(func_kind) {- default:- case JS_FUNC_NORMAL:- pref = "function ";- break;- case JS_FUNC_GENERATOR:- pref = "function *";- break;- case JS_FUNC_ASYNC:- pref = "async function ";- break;- case JS_FUNC_ASYNC_GENERATOR:- pref = "async function *";- break;- }- suff = "() {\n [native code]\n}";- name = JS_GetProperty(ctx, this_val, JS_ATOM_name);- if (JS_IsUndefined(name))- name = JS_AtomToString(ctx, JS_ATOM_empty_string);- return JS_ConcatString3(ctx, pref, name, suff);- }-}--static JSValue js_function_hasInstance(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- int ret;- ret = JS_OrdinaryIsInstanceOf(ctx, argv[0], this_val);- if (ret < 0)- return JS_EXCEPTION;- else- return JS_NewBool(ctx, ret);-}--static const JSCFunctionListEntry js_function_proto_funcs[] = {- JS_CFUNC_DEF("call", 1, js_function_call ),- JS_CFUNC_MAGIC_DEF("apply", 2, js_function_apply, 0 ),- JS_CFUNC_DEF("bind", 1, js_function_bind ),- JS_CFUNC_DEF("toString", 0, js_function_toString ),- JS_CFUNC_DEF("[Symbol.hasInstance]", 1, js_function_hasInstance ),- JS_CGETSET_DEF("fileName", js_function_proto_fileName, NULL ),- JS_CGETSET_DEF("lineNumber", js_function_proto_lineNumber, NULL ),-};--/* Error class */--static JSValue iterator_to_array(JSContext *ctx, JSValueConst items)-{- JSValue iter, next_method = JS_UNDEFINED;- JSValue v, r = JS_UNDEFINED;- int64_t k;- BOOL done;- - iter = JS_GetIterator(ctx, items, FALSE);- if (JS_IsException(iter))- goto exception;- next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);- if (JS_IsException(next_method))- goto exception;- r = JS_NewArray(ctx);- if (JS_IsException(r))- goto exception;- for (k = 0;; k++) {- v = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);- if (JS_IsException(v))- goto exception_close;- if (done)- break;- if (JS_DefinePropertyValueInt64(ctx, r, k, v,- JS_PROP_C_W_E | JS_PROP_THROW) < 0)- goto exception_close;- }- done:- JS_FreeValue(ctx, next_method);- JS_FreeValue(ctx, iter);- return r;- exception_close:- JS_IteratorClose(ctx, iter, TRUE);- exception:- JS_FreeValue(ctx, r);- r = JS_EXCEPTION;- goto done;-}--static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target,- int argc, JSValueConst *argv, int magic)-{- JSValue obj, msg, proto;- JSValueConst message;-- if (JS_IsUndefined(new_target))- new_target = JS_GetActiveFunction(ctx);- proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype);- if (JS_IsException(proto))- return proto;- if (!JS_IsObject(proto)) {- JSContext *realm;- JSValueConst proto1;- - JS_FreeValue(ctx, proto);- realm = JS_GetFunctionRealm(ctx, new_target);- if (!realm)- return JS_EXCEPTION;- if (magic < 0) {- proto1 = realm->class_proto[JS_CLASS_ERROR];- } else {- proto1 = realm->native_error_proto[magic];- }- proto = JS_DupValue(ctx, proto1);- }- obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_ERROR);- JS_FreeValue(ctx, proto);- if (JS_IsException(obj))- return obj;- if (magic == JS_AGGREGATE_ERROR) {- message = argv[1];- } else {- message = argv[0];- }-- if (!JS_IsUndefined(message)) {- msg = JS_ToString(ctx, message);- if (unlikely(JS_IsException(msg)))- goto exception;- JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, msg,- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);- }-- if (magic == JS_AGGREGATE_ERROR) {- JSValue error_list = iterator_to_array(ctx, argv[0]);- if (JS_IsException(error_list))- goto exception;- JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, error_list,- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);- }-- /* skip the Error() function in the backtrace */- build_backtrace(ctx, obj, NULL, 0, JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL);- return obj;- exception:- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue name, msg;-- if (!JS_IsObject(this_val))- return JS_ThrowTypeErrorNotAnObject(ctx);- name = JS_GetProperty(ctx, this_val, JS_ATOM_name);- if (JS_IsUndefined(name))- name = JS_AtomToString(ctx, JS_ATOM_Error);- else- name = JS_ToStringFree(ctx, name);- if (JS_IsException(name))- return JS_EXCEPTION;-- msg = JS_GetProperty(ctx, this_val, JS_ATOM_message);- if (JS_IsUndefined(msg))- msg = JS_AtomToString(ctx, JS_ATOM_empty_string);- else- msg = JS_ToStringFree(ctx, msg);- if (JS_IsException(msg)) {- JS_FreeValue(ctx, name);- return JS_EXCEPTION;- }- if (!JS_IsEmptyString(name) && !JS_IsEmptyString(msg))- name = JS_ConcatString3(ctx, "", name, ": ");- return JS_ConcatString(ctx, name, msg);-}--static const JSCFunctionListEntry js_error_proto_funcs[] = {- JS_CFUNC_DEF("toString", 0, js_error_toString ),- JS_PROP_STRING_DEF("name", "Error", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),- JS_PROP_STRING_DEF("message", "", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),-};--/* AggregateError */--/* used by C code. */-static JSValue js_aggregate_error_constructor(JSContext *ctx,- JSValueConst errors)-{- JSValue obj;- - obj = JS_NewObjectProtoClass(ctx,- ctx->native_error_proto[JS_AGGREGATE_ERROR],- JS_CLASS_ERROR);- if (JS_IsException(obj))- return obj;- JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, JS_DupValue(ctx, errors),- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);- return obj;-}--/* Array */--static int JS_CopySubArray(JSContext *ctx,- JSValueConst obj, int64_t to_pos,- int64_t from_pos, int64_t count, int dir)-{- int64_t i, from, to;- JSValue val;- int fromPresent;-- /* XXX: should special case fast arrays */- for (i = 0; i < count; i++) {- if (dir < 0) {- from = from_pos + count - i - 1;- to = to_pos + count - i - 1;- } else {- from = from_pos + i;- to = to_pos + i;- }- fromPresent = JS_TryGetPropertyInt64(ctx, obj, from, &val);- if (fromPresent < 0)- goto exception;-- if (fromPresent) {- if (JS_SetPropertyInt64(ctx, obj, to, val) < 0)- goto exception;- } else {- if (JS_DeletePropertyInt64(ctx, obj, to, JS_PROP_THROW) < 0)- goto exception;- }- }- return 0;-- exception:- return -1;-}--static JSValue js_array_constructor(JSContext *ctx, JSValueConst new_target,- int argc, JSValueConst *argv)-{- JSValue obj;- int i;-- obj = js_create_from_ctor(ctx, new_target, JS_CLASS_ARRAY);- if (JS_IsException(obj))- return obj;- if (argc == 1 && JS_IsNumber(argv[0])) {- uint32_t len;- if (JS_ToArrayLengthFree(ctx, &len, JS_DupValue(ctx, argv[0])))- goto fail;- if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, len)) < 0)- goto fail;- } else {- for(i = 0; i < argc; i++) {- if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0)- goto fail;- }- }- return obj;-fail:- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--static JSValue js_array_from(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- // from(items, mapfn = void 0, this_arg = void 0)- JSValueConst items = argv[0], mapfn, this_arg;- JSValueConst args[2];- JSValue stack[2];- JSValue iter, r, v, v2, arrayLike;- int64_t k, len;- int done, mapping;-- mapping = FALSE;- mapfn = JS_UNDEFINED;- this_arg = JS_UNDEFINED;- r = JS_UNDEFINED;- arrayLike = JS_UNDEFINED;- stack[0] = JS_UNDEFINED;- stack[1] = JS_UNDEFINED;-- if (argc > 1) {- mapfn = argv[1];- if (!JS_IsUndefined(mapfn)) {- if (check_function(ctx, mapfn))- goto exception;- mapping = 1;- if (argc > 2)- this_arg = argv[2];- }- }- iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator);- if (JS_IsException(iter))- goto exception;- if (!JS_IsUndefined(iter)) {- JS_FreeValue(ctx, iter);- if (JS_IsConstructor(ctx, this_val))- r = JS_CallConstructor(ctx, this_val, 0, NULL);- else- r = JS_NewArray(ctx);- if (JS_IsException(r))- goto exception;- stack[0] = JS_DupValue(ctx, items);- if (js_for_of_start(ctx, &stack[1], FALSE))- goto exception;- for (k = 0;; k++) {- v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done);- if (JS_IsException(v))- goto exception_close;- if (done)- break;- if (mapping) {- args[0] = v;- args[1] = JS_NewInt32(ctx, k);- v2 = JS_Call(ctx, mapfn, this_arg, 2, args);- JS_FreeValue(ctx, v);- v = v2;- if (JS_IsException(v))- goto exception_close;- }- if (JS_DefinePropertyValueInt64(ctx, r, k, v,- JS_PROP_C_W_E | JS_PROP_THROW) < 0)- goto exception_close;- }- } else {- arrayLike = JS_ToObject(ctx, items);- if (JS_IsException(arrayLike))- goto exception;- if (js_get_length64(ctx, &len, arrayLike) < 0)- goto exception;- v = JS_NewInt64(ctx, len);- args[0] = v;- if (JS_IsConstructor(ctx, this_val)) {- r = JS_CallConstructor(ctx, this_val, 1, args);- } else {- r = js_array_constructor(ctx, JS_UNDEFINED, 1, args);- }- JS_FreeValue(ctx, v);- if (JS_IsException(r))- goto exception;- for(k = 0; k < len; k++) {- v = JS_GetPropertyInt64(ctx, arrayLike, k);- if (JS_IsException(v))- goto exception;- if (mapping) {- args[0] = v;- args[1] = JS_NewInt32(ctx, k);- v2 = JS_Call(ctx, mapfn, this_arg, 2, args);- JS_FreeValue(ctx, v);- v = v2;- if (JS_IsException(v))- goto exception;- }- if (JS_DefinePropertyValueInt64(ctx, r, k, v,- JS_PROP_C_W_E | JS_PROP_THROW) < 0)- goto exception;- }- }- if (JS_SetProperty(ctx, r, JS_ATOM_length, JS_NewUint32(ctx, k)) < 0)- goto exception;- goto done;-- exception_close:- if (!JS_IsUndefined(stack[0]))- JS_IteratorClose(ctx, stack[0], TRUE);- exception:- JS_FreeValue(ctx, r);- r = JS_EXCEPTION;- done:- JS_FreeValue(ctx, arrayLike);- JS_FreeValue(ctx, stack[0]);- JS_FreeValue(ctx, stack[1]);- return r;-}--static JSValue js_array_of(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue obj, args[1];- int i;-- if (JS_IsConstructor(ctx, this_val)) {- args[0] = JS_NewInt32(ctx, argc);- obj = JS_CallConstructor(ctx, this_val, 1, (JSValueConst *)args);- } else {- obj = JS_NewArray(ctx);- }- if (JS_IsException(obj))- return JS_EXCEPTION;- for(i = 0; i < argc; i++) {- if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i]),- JS_PROP_THROW) < 0) {- goto fail;- }- }- if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, argc)) < 0) {- fail:- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;- }- return obj;-}--static JSValue js_array_isArray(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- int ret;- ret = JS_IsArray(ctx, argv[0]);- if (ret < 0)- return JS_EXCEPTION;- else- return JS_NewBool(ctx, ret);-}--static JSValue js_get_this(JSContext *ctx,- JSValueConst this_val)-{- return JS_DupValue(ctx, this_val);-}--static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValueConst obj,- JSValueConst len_val)-{- JSValue ctor, ret, species;- int res;- JSContext *realm;- - res = JS_IsArray(ctx, obj);- if (res < 0)- return JS_EXCEPTION;- if (!res)- return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val);- ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor);- if (JS_IsException(ctor))- return ctor;- if (JS_IsConstructor(ctx, ctor)) {- /* legacy web compatibility */- realm = JS_GetFunctionRealm(ctx, ctor);- if (!realm) {- JS_FreeValue(ctx, ctor);- return JS_EXCEPTION;- }- if (realm != ctx &&- js_same_value(ctx, ctor, realm->array_ctor)) {- JS_FreeValue(ctx, ctor);- ctor = JS_UNDEFINED;- }- }- if (JS_IsObject(ctor)) {- species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species);- JS_FreeValue(ctx, ctor);- if (JS_IsException(species))- return species;- ctor = species;- if (JS_IsNull(ctor))- ctor = JS_UNDEFINED;- }- if (JS_IsUndefined(ctor)) {- return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val);- } else {- ret = JS_CallConstructor(ctx, ctor, 1, &len_val);- JS_FreeValue(ctx, ctor);- return ret;- }-}--static const JSCFunctionListEntry js_array_funcs[] = {- JS_CFUNC_DEF("isArray", 1, js_array_isArray ),- JS_CFUNC_DEF("from", 1, js_array_from ),- JS_CFUNC_DEF("of", 0, js_array_of ),- JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),-};--static int JS_isConcatSpreadable(JSContext *ctx, JSValueConst obj)-{- JSValue val;-- if (!JS_IsObject(obj))- return FALSE;- val = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_isConcatSpreadable);- if (JS_IsException(val))- return -1;- if (!JS_IsUndefined(val))- return JS_ToBoolFree(ctx, val);- return JS_IsArray(ctx, obj);-}--static JSValue js_array_concat(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue obj, arr, val;- JSValueConst e;- int64_t len, k, n;- int i, res;-- arr = JS_UNDEFINED;- obj = JS_ToObject(ctx, this_val);- if (JS_IsException(obj))- goto exception;-- arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));- if (JS_IsException(arr))- goto exception;- n = 0;- for (i = -1; i < argc; i++) {- if (i < 0)- e = obj;- else- e = argv[i];-- res = JS_isConcatSpreadable(ctx, e);- if (res < 0)- goto exception;- if (res) {- if (js_get_length64(ctx, &len, e))- goto exception;- if (n + len > MAX_SAFE_INTEGER) {- JS_ThrowTypeError(ctx, "Array loo long");- goto exception;- }- for (k = 0; k < len; k++, n++) {- res = JS_TryGetPropertyInt64(ctx, e, k, &val);- if (res < 0)- goto exception;- if (res) {- if (JS_DefinePropertyValueInt64(ctx, arr, n, val,- JS_PROP_C_W_E | JS_PROP_THROW) < 0)- goto exception;- }- }- } else {- if (n >= MAX_SAFE_INTEGER) {- JS_ThrowTypeError(ctx, "Array loo long");- goto exception;- }- if (JS_DefinePropertyValueInt64(ctx, arr, n, JS_DupValue(ctx, e),- JS_PROP_C_W_E | JS_PROP_THROW) < 0)- goto exception;- n++;- }- }- if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0)- goto exception;-- JS_FreeValue(ctx, obj);- return arr;--exception:- JS_FreeValue(ctx, arr);- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--#define special_every 0-#define special_some 1-#define special_forEach 2-#define special_map 3-#define special_filter 4-#define special_TA 8--static int js_typed_array_get_length_internal(JSContext *ctx, JSValueConst obj);--static JSValue js_typed_array___speciesCreate(JSContext *ctx,- JSValueConst this_val,- int argc, JSValueConst *argv);--static JSValue js_array_every(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int special)-{- JSValue obj, val, index_val, res, ret;- JSValueConst args[3];- JSValueConst func, this_arg;- int64_t len, k, n;- int present;-- ret = JS_UNDEFINED;- val = JS_UNDEFINED;- if (special & special_TA) {- obj = JS_DupValue(ctx, this_val);- len = js_typed_array_get_length_internal(ctx, obj);- if (len < 0)- goto exception;- } else {- obj = JS_ToObject(ctx, this_val);- if (js_get_length64(ctx, &len, obj))- goto exception;- }- func = argv[0];- this_arg = JS_UNDEFINED;- if (argc > 1)- this_arg = argv[1];- - if (check_function(ctx, func))- goto exception;-- switch (special) {- case special_every:- case special_every | special_TA:- ret = JS_TRUE;- break;- case special_some:- case special_some | special_TA:- ret = JS_FALSE;- break;- case special_map:- /* XXX: JS_ArraySpeciesCreate should take int64_t */- ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt64(ctx, len));- if (JS_IsException(ret))- goto exception;- break;- case special_filter:- ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));- if (JS_IsException(ret))- goto exception;- break;- case special_map | special_TA:- args[0] = obj;- args[1] = JS_NewInt32(ctx, len);- ret = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);- if (JS_IsException(ret))- goto exception;- break;- case special_filter | special_TA:- ret = JS_NewArray(ctx);- if (JS_IsException(ret))- goto exception;- break;- }- n = 0;-- for(k = 0; k < len; k++) {- present = JS_TryGetPropertyInt64(ctx, obj, k, &val);- if (present < 0)- goto exception;- if (present) {- index_val = JS_NewInt64(ctx, k);- if (JS_IsException(index_val))- goto exception;- args[0] = val;- args[1] = index_val;- args[2] = obj;- res = JS_Call(ctx, func, this_arg, 3, args);- JS_FreeValue(ctx, index_val);- if (JS_IsException(res))- goto exception;- switch (special) {- case special_every:- case special_every | special_TA:- if (!JS_ToBoolFree(ctx, res)) {- ret = JS_FALSE;- goto done;- }- break;- case special_some:- case special_some | special_TA:- if (JS_ToBoolFree(ctx, res)) {- ret = JS_TRUE;- goto done;- }- break;- case special_map:- if (JS_DefinePropertyValueInt64(ctx, ret, k, res,- JS_PROP_C_W_E | JS_PROP_THROW) < 0)- goto exception;- break;- case special_map | special_TA:- if (JS_SetPropertyValue(ctx, ret, JS_NewInt32(ctx, k), res, JS_PROP_THROW) < 0)- goto exception;- break;- case special_filter:- case special_filter | special_TA:- if (JS_ToBoolFree(ctx, res)) {- if (JS_DefinePropertyValueInt64(ctx, ret, n++, JS_DupValue(ctx, val),- JS_PROP_C_W_E | JS_PROP_THROW) < 0)- goto exception;- }- break;- default:- JS_FreeValue(ctx, res);- break;- }- JS_FreeValue(ctx, val);- val = JS_UNDEFINED;- }- }-done:- if (special == (special_filter | special_TA)) {- JSValue arr;- args[0] = obj;- args[1] = JS_NewInt32(ctx, n);- arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);- if (JS_IsException(arr))- goto exception;- args[0] = ret;- res = JS_Invoke(ctx, arr, JS_ATOM_set, 1, args);- if (check_exception_free(ctx, res))- goto exception;- JS_FreeValue(ctx, ret);- ret = arr;- }- JS_FreeValue(ctx, val);- JS_FreeValue(ctx, obj);- return ret;--exception:- JS_FreeValue(ctx, ret);- JS_FreeValue(ctx, val);- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--#define special_reduce 0-#define special_reduceRight 1--static JSValue js_array_reduce(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int special)-{- JSValue obj, val, index_val, acc, acc1;- JSValueConst args[4];- JSValueConst func;- int64_t len, k, k1;- int present;-- acc = JS_UNDEFINED;- val = JS_UNDEFINED;- if (special & special_TA) {- obj = JS_DupValue(ctx, this_val);- len = js_typed_array_get_length_internal(ctx, obj);- if (len < 0)- goto exception;- } else {- obj = JS_ToObject(ctx, this_val);- if (js_get_length64(ctx, &len, obj))- goto exception;- }- func = argv[0];-- if (check_function(ctx, func))- goto exception;-- k = 0;- if (argc > 1) {- acc = JS_DupValue(ctx, argv[1]);- } else {- for(;;) {- if (k >= len) {- JS_ThrowTypeError(ctx, "empty array");- goto exception;- }- k1 = (special & special_reduceRight) ? len - k - 1 : k;- k++;- present = JS_TryGetPropertyInt64(ctx, obj, k1, &acc);- if (present < 0)- goto exception;- if (present)- break;- }- }- for (; k < len; k++) {- k1 = (special & special_reduceRight) ? len - k - 1 : k;- present = JS_TryGetPropertyInt64(ctx, obj, k1, &val);- if (present < 0)- goto exception;- if (present) {- index_val = JS_NewInt64(ctx, k1);- if (JS_IsException(index_val))- goto exception;- args[0] = acc;- args[1] = val;- args[2] = index_val;- args[3] = obj;- acc1 = JS_Call(ctx, func, JS_UNDEFINED, 4, args);- JS_FreeValue(ctx, index_val);- JS_FreeValue(ctx, val);- val = JS_UNDEFINED;- if (JS_IsException(acc1))- goto exception;- JS_FreeValue(ctx, acc);- acc = acc1;- }- }- JS_FreeValue(ctx, obj);- return acc;--exception:- JS_FreeValue(ctx, acc);- JS_FreeValue(ctx, val);- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--static JSValue js_array_fill(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue obj;- int64_t len, start, end;-- obj = JS_ToObject(ctx, this_val);- if (js_get_length64(ctx, &len, obj))- goto exception;-- start = 0;- if (argc > 1 && !JS_IsUndefined(argv[1])) {- if (JS_ToInt64Clamp(ctx, &start, argv[1], 0, len, len))- goto exception;- }-- end = len;- if (argc > 2 && !JS_IsUndefined(argv[2])) {- if (JS_ToInt64Clamp(ctx, &end, argv[2], 0, len, len))- goto exception;- }-- /* XXX: should special case fast arrays */- while (start < end) {- if (JS_SetPropertyInt64(ctx, obj, start,- JS_DupValue(ctx, argv[0])) < 0)- goto exception;- start++;- }- return obj;-- exception:- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--static JSValue js_array_includes(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue obj, val;- int64_t len, n, res;- JSValue *arrp;- uint32_t count;-- obj = JS_ToObject(ctx, this_val);- if (js_get_length64(ctx, &len, obj))- goto exception;-- res = FALSE;- if (len > 0) {- n = 0;- if (argc > 1) {- if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len))- goto exception;- }- if (js_get_fast_array(ctx, obj, &arrp, &count)) {- for (; n < count; n++) {- if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]),- JS_DupValue(ctx, arrp[n]),- JS_EQ_SAME_VALUE_ZERO)) {- res = TRUE;- goto done;- }- }- }- for (; n < len; n++) {- val = JS_GetPropertyInt64(ctx, obj, n);- if (JS_IsException(val))- goto exception;- if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val,- JS_EQ_SAME_VALUE_ZERO)) {- res = TRUE;- break;- }- }- }- done:- JS_FreeValue(ctx, obj);- return JS_NewBool(ctx, res);-- exception:- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--static JSValue js_array_indexOf(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue obj, val;- int64_t len, n, res;- JSValue *arrp;- uint32_t count;-- obj = JS_ToObject(ctx, this_val);- if (js_get_length64(ctx, &len, obj))- goto exception;-- res = -1;- if (len > 0) {- n = 0;- if (argc > 1) {- if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len))- goto exception;- }- if (js_get_fast_array(ctx, obj, &arrp, &count)) {- for (; n < count; n++) {- if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]),- JS_DupValue(ctx, arrp[n]), JS_EQ_STRICT)) {- res = n;- goto done;- }- }- }- for (; n < len; n++) {- int present = JS_TryGetPropertyInt64(ctx, obj, n, &val);- if (present < 0)- goto exception;- if (present) {- if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) {- res = n;- break;- }- }- }- }- done:- JS_FreeValue(ctx, obj);- return JS_NewInt64(ctx, res);-- exception:- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--static JSValue js_array_lastIndexOf(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue obj, val;- int64_t len, n, res;- int present;-- obj = JS_ToObject(ctx, this_val);- if (js_get_length64(ctx, &len, obj))- goto exception;-- res = -1;- if (len > 0) {- n = len - 1;- if (argc > 1) {- if (JS_ToInt64Clamp(ctx, &n, argv[1], -1, len - 1, len))- goto exception;- }- /* XXX: should special case fast arrays */- for (; n >= 0; n--) {- present = JS_TryGetPropertyInt64(ctx, obj, n, &val);- if (present < 0)- goto exception;- if (present) {- if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) {- res = n;- break;- }- }- }- }- JS_FreeValue(ctx, obj);- return JS_NewInt64(ctx, res);-- exception:- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--static JSValue js_array_find(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int findIndex)-{- JSValueConst func, this_arg;- JSValueConst args[3];- JSValue obj, val, index_val, res;- int64_t len, k;-- index_val = JS_UNDEFINED;- val = JS_UNDEFINED;- obj = JS_ToObject(ctx, this_val);- if (js_get_length64(ctx, &len, obj))- goto exception;-- func = argv[0];- if (check_function(ctx, func))- goto exception;-- this_arg = JS_UNDEFINED;- if (argc > 1)- this_arg = argv[1];-- for(k = 0; k < len; k++) {- index_val = JS_NewInt64(ctx, k);- if (JS_IsException(index_val))- goto exception;- val = JS_GetPropertyValue(ctx, obj, index_val);- if (JS_IsException(val))- goto exception;- args[0] = val;- args[1] = index_val;- args[2] = this_val;- res = JS_Call(ctx, func, this_arg, 3, args);- if (JS_IsException(res))- goto exception;- if (JS_ToBoolFree(ctx, res)) {- if (findIndex) {- JS_FreeValue(ctx, val);- JS_FreeValue(ctx, obj);- return index_val;- } else {- JS_FreeValue(ctx, index_val);- JS_FreeValue(ctx, obj);- return val;- }- }- JS_FreeValue(ctx, val);- JS_FreeValue(ctx, index_val);- }- JS_FreeValue(ctx, obj);- if (findIndex)- return JS_NewInt32(ctx, -1);- else- return JS_UNDEFINED;--exception:- JS_FreeValue(ctx, index_val);- JS_FreeValue(ctx, val);- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--static JSValue js_array_toString(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue obj, method, ret;-- obj = JS_ToObject(ctx, this_val);- if (JS_IsException(obj))- return JS_EXCEPTION;- method = JS_GetProperty(ctx, obj, JS_ATOM_join);- if (JS_IsException(method)) {- ret = JS_EXCEPTION;- } else- if (!JS_IsFunction(ctx, method)) {- /* Use intrinsic Object.prototype.toString */- JS_FreeValue(ctx, method);- ret = js_object_toString(ctx, obj, 0, NULL);- } else {- ret = JS_CallFree(ctx, method, obj, 0, NULL);- }- JS_FreeValue(ctx, obj);- return ret;-}--static JSValue js_array_join(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int toLocaleString)-{- JSValue obj, sep = JS_UNDEFINED, el;- StringBuffer b_s, *b = &b_s;- JSString *p = NULL;- int64_t i, n;- int c;-- obj = JS_ToObject(ctx, this_val);- if (js_get_length64(ctx, &n, obj))- goto exception;-- c = ','; /* default separator */- if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) {- sep = JS_ToString(ctx, argv[0]);- if (JS_IsException(sep))- goto exception;- p = JS_VALUE_GET_STRING(sep);- if (p->len == 1 && !p->is_wide_char)- c = p->u.str8[0];- else- c = -1;- }- string_buffer_init(ctx, b, 0);-- for(i = 0; i < n; i++) {- if (i > 0) {- if (c >= 0) {- string_buffer_putc8(b, c);- } else {- string_buffer_concat(b, p, 0, p->len);- }- }- el = JS_GetPropertyUint32(ctx, obj, i);- if (JS_IsException(el))- goto fail;- if (!JS_IsNull(el) && !JS_IsUndefined(el)) {- if (toLocaleString) {- el = JS_ToLocaleStringFree(ctx, el);- }- if (string_buffer_concat_value_free(b, el))- goto fail;- }- }- JS_FreeValue(ctx, sep);- JS_FreeValue(ctx, obj);- return string_buffer_end(b);--fail:- string_buffer_free(b);- JS_FreeValue(ctx, sep);-exception:- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--static JSValue js_array_pop(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int shift)-{- JSValue obj, res = JS_UNDEFINED;- int64_t len, newLen;- JSValue *arrp;- uint32_t count32;-- obj = JS_ToObject(ctx, this_val);- if (js_get_length64(ctx, &len, obj))- goto exception;- newLen = 0;- if (len > 0) {- newLen = len - 1;- /* Special case fast arrays */- if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {- JSObject *p = JS_VALUE_GET_OBJ(obj);- if (shift) {- res = arrp[0];- memmove(arrp, arrp + 1, (count32 - 1) * sizeof(*arrp));- p->u.array.count--;- } else {- res = arrp[count32 - 1];- p->u.array.count--;- }- } else {- if (shift) {- res = JS_GetPropertyInt64(ctx, obj, 0);- if (JS_IsException(res))- goto exception;- if (JS_CopySubArray(ctx, obj, 0, 1, len - 1, +1))- goto exception;- } else {- res = JS_GetPropertyInt64(ctx, obj, newLen);- if (JS_IsException(res))- goto exception;- }- if (JS_DeletePropertyInt64(ctx, obj, newLen, JS_PROP_THROW) < 0)- goto exception;- }- }- if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0)- goto exception;-- JS_FreeValue(ctx, obj);- return res;-- exception:- JS_FreeValue(ctx, res);- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--static JSValue js_array_push(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int unshift)-{- JSValue obj;- int i;- int64_t len, from, newLen;-- obj = JS_ToObject(ctx, this_val);- if (js_get_length64(ctx, &len, obj))- goto exception;- newLen = len + argc;- if (newLen > MAX_SAFE_INTEGER) {- JS_ThrowTypeError(ctx, "Array loo long");- goto exception;- }- from = len;- if (unshift && argc > 0) {- if (JS_CopySubArray(ctx, obj, argc, 0, len, -1))- goto exception;- from = 0;- }- for(i = 0; i < argc; i++) {- if (JS_SetPropertyInt64(ctx, obj, from + i,- JS_DupValue(ctx, argv[i])) < 0)- goto exception;- }- if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0)- goto exception;-- JS_FreeValue(ctx, obj);- return JS_NewInt64(ctx, newLen);-- exception:- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--static JSValue js_array_reverse(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue obj, lval, hval;- JSValue *arrp;- int64_t len, l, h;- int l_present, h_present;- uint32_t count32;-- lval = JS_UNDEFINED;- obj = JS_ToObject(ctx, this_val);- if (js_get_length64(ctx, &len, obj))- goto exception;-- /* Special case fast arrays */- if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {- uint32_t ll, hh;-- if (count32 > 1) {- for (ll = 0, hh = count32 - 1; ll < hh; ll++, hh--) {- lval = arrp[ll];- arrp[ll] = arrp[hh];- arrp[hh] = lval;- }- }- return obj;- }-- for (l = 0, h = len - 1; l < h; l++, h--) {- l_present = JS_TryGetPropertyInt64(ctx, obj, l, &lval);- if (l_present < 0)- goto exception;- h_present = JS_TryGetPropertyInt64(ctx, obj, h, &hval);- if (h_present < 0)- goto exception;- if (h_present) {- if (JS_SetPropertyInt64(ctx, obj, l, hval) < 0)- goto exception;-- if (l_present) {- if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) {- lval = JS_UNDEFINED;- goto exception;- }- lval = JS_UNDEFINED;- } else {- if (JS_DeletePropertyInt64(ctx, obj, h, JS_PROP_THROW) < 0)- goto exception;- }- } else {- if (l_present) {- if (JS_DeletePropertyInt64(ctx, obj, l, JS_PROP_THROW) < 0)- goto exception;- if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) {- lval = JS_UNDEFINED;- goto exception;- }- lval = JS_UNDEFINED;- }- }- }- return obj;-- exception:- JS_FreeValue(ctx, lval);- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int splice)-{- JSValue obj, arr, val, len_val;- int64_t len, start, k, final, n, count, del_count, new_len;- int kPresent;- JSValue *arrp;- uint32_t count32, i, item_count;-- arr = JS_UNDEFINED;- obj = JS_ToObject(ctx, this_val);- if (js_get_length64(ctx, &len, obj))- goto exception;-- if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len))- goto exception;-- if (splice) {- if (argc == 0) {- item_count = 0;- del_count = 0;- } else- if (argc == 1) {- item_count = 0;- del_count = len - start;- } else {- item_count = argc - 2;- if (JS_ToInt64Clamp(ctx, &del_count, argv[1], 0, len - start, 0))- goto exception;- }- if (len + item_count - del_count > MAX_SAFE_INTEGER) {- JS_ThrowTypeError(ctx, "Array loo long");- goto exception;- }- count = del_count;- } else {- item_count = 0; /* avoid warning */- final = len;- if (!JS_IsUndefined(argv[1])) {- if (JS_ToInt64Clamp(ctx, &final, argv[1], 0, len, len))- goto exception;- }- count = max_int64(final - start, 0);- }- len_val = JS_NewInt64(ctx, count);- arr = JS_ArraySpeciesCreate(ctx, obj, len_val);- JS_FreeValue(ctx, len_val);- if (JS_IsException(arr))- goto exception;-- k = start;- final = start + count;- n = 0;- /* The fast array test on arr ensures that- JS_CreateDataPropertyUint32() won't modify obj in case arr is- an exotic object */- /* Special case fast arrays */- if (js_get_fast_array(ctx, obj, &arrp, &count32) &&- js_is_fast_array(ctx, arr)) {- /* XXX: should share code with fast array constructor */- for (; k < final && k < count32; k++, n++) {- if (JS_CreateDataPropertyUint32(ctx, arr, n, JS_DupValue(ctx, arrp[k]), JS_PROP_THROW) < 0)- goto exception;- }- }- /* Copy the remaining elements if any (handle case of inherited properties) */- for (; k < final; k++, n++) {- kPresent = JS_TryGetPropertyInt64(ctx, obj, k, &val);- if (kPresent < 0)- goto exception;- if (kPresent) {- if (JS_CreateDataPropertyUint32(ctx, arr, n, val, JS_PROP_THROW) < 0)- goto exception;- }- }- if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0)- goto exception;-- if (splice) {- new_len = len + item_count - del_count;- if (item_count != del_count) {- if (JS_CopySubArray(ctx, obj, start + item_count,- start + del_count, len - (start + del_count),- item_count <= del_count ? +1 : -1) < 0)- goto exception;-- for (k = len; k-- > new_len; ) {- if (JS_DeletePropertyInt64(ctx, obj, k, JS_PROP_THROW) < 0)- goto exception;- }- }- for (i = 0; i < item_count; i++) {- if (JS_SetPropertyInt64(ctx, obj, start + i, JS_DupValue(ctx, argv[i + 2])) < 0)- goto exception;- }- if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, new_len)) < 0)- goto exception;- }- JS_FreeValue(ctx, obj);- return arr;-- exception:- JS_FreeValue(ctx, obj);- JS_FreeValue(ctx, arr);- return JS_EXCEPTION;-}--static JSValue js_array_copyWithin(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue obj;- int64_t len, from, to, final, count;-- obj = JS_ToObject(ctx, this_val);- if (js_get_length64(ctx, &len, obj))- goto exception;-- if (JS_ToInt64Clamp(ctx, &to, argv[0], 0, len, len))- goto exception;-- if (JS_ToInt64Clamp(ctx, &from, argv[1], 0, len, len))- goto exception;-- final = len;- if (argc > 2 && !JS_IsUndefined(argv[2])) {- if (JS_ToInt64Clamp(ctx, &final, argv[2], 0, len, len))- goto exception;- }-- count = min_int64(final - from, len - to);-- if (JS_CopySubArray(ctx, obj, to, from, count,- (from < to && to < from + count) ? -1 : +1))- goto exception;-- return obj;-- exception:- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValueConst target,- JSValueConst source, int64_t sourceLen,- int64_t targetIndex, int depth,- JSValueConst mapperFunction,- JSValueConst thisArg)-{- JSValue element;- int64_t sourceIndex, elementLen;- int present, is_array;-- for (sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) {- present = JS_TryGetPropertyInt64(ctx, source, sourceIndex, &element);- if (present < 0)- return -1;- if (!present)- continue;- if (!JS_IsUndefined(mapperFunction)) {- JSValueConst args[3] = { element, JS_NewInt64(ctx, sourceIndex), source };- element = JS_Call(ctx, mapperFunction, thisArg, 3, args);- JS_FreeValue(ctx, (JSValue)args[0]);- JS_FreeValue(ctx, (JSValue)args[1]);- if (JS_IsException(element))- return -1;- }- if (depth > 0) {- is_array = JS_IsArray(ctx, element);- if (is_array < 0)- goto fail;- if (is_array) {- if (js_get_length64(ctx, &elementLen, element) < 0)- goto fail;- targetIndex = JS_FlattenIntoArray(ctx, target, element,- elementLen, targetIndex,- depth - 1,- JS_UNDEFINED, JS_UNDEFINED);- if (targetIndex < 0)- goto fail;- JS_FreeValue(ctx, element);- continue;- }- }- if (targetIndex >= MAX_SAFE_INTEGER) {- JS_ThrowTypeError(ctx, "Array too long");- goto fail;- }- if (JS_DefinePropertyValueInt64(ctx, target, targetIndex, element,- JS_PROP_C_W_E | JS_PROP_THROW) < 0)- return -1;- targetIndex++;- }- return targetIndex;--fail:- JS_FreeValue(ctx, element);- return -1;-}--static JSValue js_array_flatten(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int map)-{- JSValue obj, arr;- JSValueConst mapperFunction, thisArg;- int64_t sourceLen;- int depthNum;-- arr = JS_UNDEFINED;- obj = JS_ToObject(ctx, this_val);- if (js_get_length64(ctx, &sourceLen, obj))- goto exception;-- depthNum = 1;- mapperFunction = JS_UNDEFINED;- thisArg = JS_UNDEFINED;- if (map) {- mapperFunction = argv[0];- if (argc > 1) {- thisArg = argv[1];- }- if (check_function(ctx, mapperFunction))- goto exception;- } else {- if (argc > 0 && !JS_IsUndefined(argv[0])) {- if (JS_ToInt32Sat(ctx, &depthNum, argv[0]) < 0)- goto exception;- }- }- arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));- if (JS_IsException(arr))- goto exception;- if (JS_FlattenIntoArray(ctx, arr, obj, sourceLen, 0, depthNum,- mapperFunction, thisArg) < 0)- goto exception;- JS_FreeValue(ctx, obj);- return arr;--exception:- JS_FreeValue(ctx, obj);- JS_FreeValue(ctx, arr);- return JS_EXCEPTION;-}--/* Array sort */--typedef struct ValueSlot {- JSValue val;- JSString *str;- int64_t pos;-} ValueSlot;--struct array_sort_context {- JSContext *ctx;- int exception;- int has_method;- JSValueConst method;-};--static int js_array_cmp_generic(const void *a, const void *b, void *opaque) {- struct array_sort_context *psc = opaque;- JSContext *ctx = psc->ctx;- JSValueConst argv[2];- JSValue res;- ValueSlot *ap = (ValueSlot *)(void *)a;- ValueSlot *bp = (ValueSlot *)(void *)b;- int cmp;-- if (psc->exception)- return 0;-- if (psc->has_method) {- /* custom sort function is specified as returning 0 for identical- * objects: avoid method call overhead.- */- if (!memcmp(&ap->val, &bp->val, sizeof(ap->val)))- goto cmp_same;- argv[0] = ap->val;- argv[1] = bp->val;- res = JS_Call(ctx, psc->method, JS_UNDEFINED, 2, argv);- if (JS_IsException(res))- goto exception;- if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) {- int val = JS_VALUE_GET_INT(res);- cmp = (val > 0) - (val < 0);- } else {- double val;- if (JS_ToFloat64Free(ctx, &val, res) < 0)- goto exception;- cmp = (val > 0) - (val < 0);- }- } else {- /* Not supposed to bypass ToString even for identical objects as- * tested in test262/test/built-ins/Array/prototype/sort/bug_596_1.js- */- if (!ap->str) {- JSValue str = JS_ToString(ctx, ap->val);- if (JS_IsException(str))- goto exception;- ap->str = JS_VALUE_GET_STRING(str);- }- if (!bp->str) {- JSValue str = JS_ToString(ctx, bp->val);- if (JS_IsException(str))- goto exception;- bp->str = JS_VALUE_GET_STRING(str);- }- cmp = js_string_compare(ctx, ap->str, bp->str);- }- if (cmp != 0)- return cmp;-cmp_same:- /* make sort stable: compare array offsets */- return (ap->pos > bp->pos) - (ap->pos < bp->pos);--exception:- psc->exception = 1;- return 0;-}--static JSValue js_array_sort(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- struct array_sort_context asc = { ctx, 0, 0, argv[0] };- JSValue obj = JS_UNDEFINED;- ValueSlot *array = NULL;- size_t array_size = 0, pos = 0, n = 0;- int64_t i, len, undefined_count = 0;- int present;-- if (!JS_IsUndefined(asc.method)) {- if (check_function(ctx, asc.method))- goto exception;- asc.has_method = 1;- }- obj = JS_ToObject(ctx, this_val);- if (js_get_length64(ctx, &len, obj))- goto exception;-- /* XXX: should special case fast arrays */- for (i = 0; i < len; i++) {- if (pos >= array_size) {- size_t new_size, slack;- ValueSlot *new_array;- new_size = (array_size + (array_size >> 1) + 31) & ~15;- new_array = js_realloc2(ctx, array, new_size * sizeof(*array), &slack);- if (new_array == NULL)- goto exception;- new_size += slack / sizeof(*new_array);- array = new_array;- array_size = new_size;- }- present = JS_TryGetPropertyInt64(ctx, obj, i, &array[pos].val);- if (present < 0)- goto exception;- if (present == 0)- continue;- if (JS_IsUndefined(array[pos].val)) {- undefined_count++;- continue;- }- array[pos].str = NULL;- array[pos].pos = i;- pos++;- }- rqsort(array, pos, sizeof(*array), js_array_cmp_generic, &asc);- if (asc.exception)- goto exception;-- /* XXX: should special case fast arrays */- while (n < pos) {- if (array[n].str)- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str));- if (array[n].pos == n) {- JS_FreeValue(ctx, array[n].val);- } else {- if (JS_SetPropertyInt64(ctx, obj, n, array[n].val) < 0) {- n++;- goto exception;- }- }- n++;- }- js_free(ctx, array);- for (i = n; undefined_count-- > 0; i++) {- if (JS_SetPropertyInt64(ctx, obj, i, JS_UNDEFINED) < 0)- goto fail;- }- for (; i < len; i++) {- if (JS_DeletePropertyInt64(ctx, obj, i, JS_PROP_THROW) < 0)- goto fail;- }- return obj;--exception:- for (; n < pos; n++) {- JS_FreeValue(ctx, array[n].val);- if (array[n].str)- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str));- }- js_free(ctx, array);-fail:- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--typedef struct JSArrayIteratorData {- JSValue obj;- JSIteratorKindEnum kind;- uint32_t idx;-} JSArrayIteratorData;--static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val)-{- JSObject *p = JS_VALUE_GET_OBJ(val);- JSArrayIteratorData *it = p->u.array_iterator_data;- if (it) {- JS_FreeValueRT(rt, it->obj);- js_free_rt(rt, it);- }-}--static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func)-{- JSObject *p = JS_VALUE_GET_OBJ(val);- JSArrayIteratorData *it = p->u.array_iterator_data;- if (it) {- JS_MarkValue(rt, it->obj, mark_func);- }-}--static JSValue js_create_array(JSContext *ctx, int len, JSValueConst *tab)-{- JSValue obj;- int i;-- obj = JS_NewArray(ctx);- if (JS_IsException(obj))- return JS_EXCEPTION;- for(i = 0; i < len; i++) {- if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, tab[i]), 0) < 0) {- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;- }- }- return obj;-}--static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- JSValue enum_obj, arr;- JSArrayIteratorData *it;- JSIteratorKindEnum kind;- int class_id;-- kind = magic & 3;- if (magic & 4) {- /* string iterator case */- arr = JS_ToStringCheckObject(ctx, this_val);- class_id = JS_CLASS_STRING_ITERATOR;- } else {- arr = JS_ToObject(ctx, this_val);- class_id = JS_CLASS_ARRAY_ITERATOR;- }- if (JS_IsException(arr))- goto fail;- enum_obj = JS_NewObjectClass(ctx, class_id);- if (JS_IsException(enum_obj))- goto fail;- it = js_malloc(ctx, sizeof(*it));- if (!it)- goto fail1;- it->obj = arr;- it->kind = kind;- it->idx = 0;- JS_SetOpaque(enum_obj, it);- return enum_obj;- fail1:- JS_FreeValue(ctx, enum_obj);- fail:- JS_FreeValue(ctx, arr);- return JS_EXCEPTION;-}--static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv,- BOOL *pdone, int magic)-{- JSArrayIteratorData *it;- uint32_t len, idx;- JSValue val, obj;- JSObject *p;-- it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_ITERATOR);- if (!it)- goto fail1;- if (JS_IsUndefined(it->obj))- goto done;- p = JS_VALUE_GET_OBJ(it->obj);- if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&- p->class_id <= JS_CLASS_FLOAT64_ARRAY) {- if (typed_array_is_detached(ctx, p)) {- JS_ThrowTypeErrorDetachedArrayBuffer(ctx);- goto fail1;- }- len = p->u.array.count;- } else {- if (js_get_length32(ctx, &len, it->obj)) {- fail1:- *pdone = FALSE;- return JS_EXCEPTION;- }- }- idx = it->idx;- if (idx >= len) {- JS_FreeValue(ctx, it->obj);- it->obj = JS_UNDEFINED;- done:- *pdone = TRUE;- return JS_UNDEFINED;- }- it->idx = idx + 1;- *pdone = FALSE;- if (it->kind == JS_ITERATOR_KIND_KEY) {- return JS_NewUint32(ctx, idx);- } else {- val = JS_GetPropertyUint32(ctx, it->obj, idx);- if (JS_IsException(val))- return JS_EXCEPTION;- if (it->kind == JS_ITERATOR_KIND_VALUE) {- return val;- } else {- JSValueConst args[2];- JSValue num;- num = JS_NewUint32(ctx, idx);- args[0] = num;- args[1] = val;- obj = js_create_array(ctx, 2, args);- JS_FreeValue(ctx, val);- JS_FreeValue(ctx, num);- return obj;- }- }-}--static JSValue js_iterator_proto_iterator(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return JS_DupValue(ctx, this_val);-}--static const JSCFunctionListEntry js_iterator_proto_funcs[] = {- JS_CFUNC_DEF("[Symbol.iterator]", 0, js_iterator_proto_iterator ),-};--static const JSCFunctionListEntry js_array_proto_funcs[] = {- JS_CFUNC_DEF("concat", 1, js_array_concat ),- JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every ),- JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some ),- JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach ),- JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map ),- JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter ),- JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce ),- JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight ),- JS_CFUNC_DEF("fill", 1, js_array_fill ),- JS_CFUNC_MAGIC_DEF("find", 1, js_array_find, 0 ),- JS_CFUNC_MAGIC_DEF("findIndex", 1, js_array_find, 1 ),- JS_CFUNC_DEF("indexOf", 1, js_array_indexOf ),- JS_CFUNC_DEF("lastIndexOf", 1, js_array_lastIndexOf ),- JS_CFUNC_DEF("includes", 1, js_array_includes ),- JS_CFUNC_MAGIC_DEF("join", 1, js_array_join, 0 ),- JS_CFUNC_DEF("toString", 0, js_array_toString ),- JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_array_join, 1 ),- JS_CFUNC_MAGIC_DEF("pop", 0, js_array_pop, 0 ),- JS_CFUNC_MAGIC_DEF("push", 1, js_array_push, 0 ),- JS_CFUNC_MAGIC_DEF("shift", 0, js_array_pop, 1 ),- JS_CFUNC_MAGIC_DEF("unshift", 1, js_array_push, 1 ),- JS_CFUNC_DEF("reverse", 0, js_array_reverse ),- JS_CFUNC_DEF("sort", 1, js_array_sort ),- JS_CFUNC_MAGIC_DEF("slice", 2, js_array_slice, 0 ),- JS_CFUNC_MAGIC_DEF("splice", 2, js_array_slice, 1 ),- JS_CFUNC_DEF("copyWithin", 2, js_array_copyWithin ),- JS_CFUNC_MAGIC_DEF("flatMap", 1, js_array_flatten, 1 ),- JS_CFUNC_MAGIC_DEF("flat", 0, js_array_flatten, 0 ),- JS_CFUNC_MAGIC_DEF("values", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE ),- JS_ALIAS_DEF("[Symbol.iterator]", "values" ),- JS_CFUNC_MAGIC_DEF("keys", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY ),- JS_CFUNC_MAGIC_DEF("entries", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ),-};--static const JSCFunctionListEntry js_array_iterator_proto_funcs[] = {- JS_ITERATOR_NEXT_DEF("next", 0, js_array_iterator_next, 0 ),- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Array Iterator", JS_PROP_CONFIGURABLE ),-};--/* Number */--static JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target,- int argc, JSValueConst *argv)-{- JSValue val, obj;- if (argc == 0) {- val = JS_NewInt32(ctx, 0);- } else {- val = JS_ToNumeric(ctx, argv[0]);- if (JS_IsException(val))- return val;- switch(JS_VALUE_GET_TAG(val)) {-#ifdef CONFIG_BIGNUM- case JS_TAG_BIG_INT:- case JS_TAG_BIG_FLOAT:- {- JSBigFloat *p = JS_VALUE_GET_PTR(val);- double d;- bf_get_float64(&p->num, &d, BF_RNDN);- JS_FreeValue(ctx, val);- val = __JS_NewFloat64(ctx, d);- }- break;- case JS_TAG_BIG_DECIMAL:- val = JS_ToStringFree(ctx, val);- if (JS_IsException(val))- return val;- val = JS_ToNumberFree(ctx, val);- if (JS_IsException(val))- return val;- break;-#endif- default:- break;- }- }- if (!JS_IsUndefined(new_target)) {- obj = js_create_from_ctor(ctx, new_target, JS_CLASS_NUMBER);- if (!JS_IsException(obj))- JS_SetObjectData(ctx, obj, val);- return obj;- } else {- return val;- }-}--#if 0-static JSValue js_number___toInteger(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[0]));-}--static JSValue js_number___toLength(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- int64_t v;- if (JS_ToLengthFree(ctx, &v, JS_DupValue(ctx, argv[0])))- return JS_EXCEPTION;- return JS_NewInt64(ctx, v);-}-#endif--static JSValue js_number_isNaN(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- if (!JS_IsNumber(argv[0]))- return JS_FALSE;- return js_global_isNaN(ctx, this_val, argc, argv);-}--static JSValue js_number_isFinite(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- if (!JS_IsNumber(argv[0]))- return JS_FALSE;- return js_global_isFinite(ctx, this_val, argc, argv);-}--static JSValue js_number_isInteger(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- int ret;- ret = JS_NumberIsInteger(ctx, argv[0]);- if (ret < 0)- return JS_EXCEPTION;- else- return JS_NewBool(ctx, ret);-}--static JSValue js_number_isSafeInteger(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- double d;- if (!JS_IsNumber(argv[0]))- return JS_FALSE;- if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))- return JS_EXCEPTION;- return JS_NewBool(ctx, is_safe_integer(d));-}--static const JSCFunctionListEntry js_number_funcs[] = {- /* global ParseInt and parseFloat should be defined already or delayed */- JS_ALIAS_BASE_DEF("parseInt", "parseInt", 0 ),- JS_ALIAS_BASE_DEF("parseFloat", "parseFloat", 0 ),- JS_CFUNC_DEF("isNaN", 1, js_number_isNaN ),- JS_CFUNC_DEF("isFinite", 1, js_number_isFinite ),- JS_CFUNC_DEF("isInteger", 1, js_number_isInteger ),- JS_CFUNC_DEF("isSafeInteger", 1, js_number_isSafeInteger ),- JS_PROP_DOUBLE_DEF("MAX_VALUE", 1.7976931348623157e+308, 0 ),- JS_PROP_DOUBLE_DEF("MIN_VALUE", 5e-324, 0 ),- JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),- JS_PROP_DOUBLE_DEF("NEGATIVE_INFINITY", -INFINITY, 0 ),- JS_PROP_DOUBLE_DEF("POSITIVE_INFINITY", INFINITY, 0 ),- JS_PROP_DOUBLE_DEF("EPSILON", 2.220446049250313e-16, 0 ), /* ES6 */- JS_PROP_DOUBLE_DEF("MAX_SAFE_INTEGER", 9007199254740991.0, 0 ), /* ES6 */- JS_PROP_DOUBLE_DEF("MIN_SAFE_INTEGER", -9007199254740991.0, 0 ), /* ES6 */- //JS_CFUNC_DEF("__toInteger", 1, js_number___toInteger ),- //JS_CFUNC_DEF("__toLength", 1, js_number___toLength ),-};--static JSValue js_thisNumberValue(JSContext *ctx, JSValueConst this_val)-{- if (JS_IsNumber(this_val))- return JS_DupValue(ctx, this_val);-- if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {- JSObject *p = JS_VALUE_GET_OBJ(this_val);- if (p->class_id == JS_CLASS_NUMBER) {- if (JS_IsNumber(p->u.object_data))- return JS_DupValue(ctx, p->u.object_data);- }- }- return JS_ThrowTypeError(ctx, "not a number");-}--static JSValue js_number_valueOf(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return js_thisNumberValue(ctx, this_val);-}--static int js_get_radix(JSContext *ctx, JSValueConst val)-{- int radix;- if (JS_ToInt32Sat(ctx, &radix, val))- return -1;- if (radix < 2 || radix > 36) {- JS_ThrowRangeError(ctx, "radix must be between 2 and 36");- return -1;- }- return radix;-}--static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- JSValue val;- int base;- double d;-- val = js_thisNumberValue(ctx, this_val);- if (JS_IsException(val))- return val;- if (magic || JS_IsUndefined(argv[0])) {- base = 10;- } else {- base = js_get_radix(ctx, argv[0]);- if (base < 0)- goto fail;- }- if (JS_ToFloat64Free(ctx, &d, val))- return JS_EXCEPTION;- return js_dtoa(ctx, d, base, 0, JS_DTOA_VAR_FORMAT);- fail:- JS_FreeValue(ctx, val);- return JS_EXCEPTION;-}--static JSValue js_number_toFixed(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue val;- int f;- double d;-- val = js_thisNumberValue(ctx, this_val);- if (JS_IsException(val))- return val;- if (JS_ToFloat64Free(ctx, &d, val))- return JS_EXCEPTION;- if (JS_ToInt32Sat(ctx, &f, argv[0]))- return JS_EXCEPTION;- if (f < 0 || f > 100)- return JS_ThrowRangeError(ctx, "invalid number of digits");- if (fabs(d) >= 1e21) {- return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d));- } else {- return js_dtoa(ctx, d, 10, f, JS_DTOA_FRAC_FORMAT);- }-}--static JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue val;- int f, flags;- double d;-- val = js_thisNumberValue(ctx, this_val);- if (JS_IsException(val))- return val;- if (JS_ToFloat64Free(ctx, &d, val))- return JS_EXCEPTION;- if (JS_ToInt32Sat(ctx, &f, argv[0]))- return JS_EXCEPTION;- if (!isfinite(d)) {- return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d));- }- if (JS_IsUndefined(argv[0])) {- flags = 0;- f = 0;- } else {- if (f < 0 || f > 100)- return JS_ThrowRangeError(ctx, "invalid number of digits");- f++;- flags = JS_DTOA_FIXED_FORMAT;- }- return js_dtoa(ctx, d, 10, f, flags | JS_DTOA_FORCE_EXP);-}--static JSValue js_number_toPrecision(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue val;- int p;- double d;-- val = js_thisNumberValue(ctx, this_val);- if (JS_IsException(val))- return val;- if (JS_ToFloat64Free(ctx, &d, val))- return JS_EXCEPTION;- if (JS_IsUndefined(argv[0]))- goto to_string;- if (JS_ToInt32Sat(ctx, &p, argv[0]))- return JS_EXCEPTION;- if (!isfinite(d)) {- to_string:- return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d));- }- if (p < 1 || p > 100)- return JS_ThrowRangeError(ctx, "invalid number of digits");- return js_dtoa(ctx, d, 10, p, JS_DTOA_FIXED_FORMAT);-}--static const JSCFunctionListEntry js_number_proto_funcs[] = {- JS_CFUNC_DEF("toExponential", 1, js_number_toExponential ),- JS_CFUNC_DEF("toFixed", 1, js_number_toFixed ),- JS_CFUNC_DEF("toPrecision", 1, js_number_toPrecision ),- JS_CFUNC_MAGIC_DEF("toString", 1, js_number_toString, 0 ),- JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_number_toString, 1 ),- JS_CFUNC_DEF("valueOf", 0, js_number_valueOf ),-};--static JSValue js_parseInt(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- const char *str, *p;- int radix, flags;- JSValue ret;-- str = JS_ToCString(ctx, argv[0]);- if (!str)- return JS_EXCEPTION;- if (JS_ToInt32(ctx, &radix, argv[1])) {- JS_FreeCString(ctx, str);- return JS_EXCEPTION;- }- if (radix != 0 && (radix < 2 || radix > 36)) {- ret = JS_NAN;- } else {- p = str;- p += skip_spaces(p);- flags = ATOD_INT_ONLY | ATOD_ACCEPT_PREFIX_AFTER_SIGN;- ret = js_atof(ctx, p, NULL, radix, flags);- }- JS_FreeCString(ctx, str);- return ret;-}--static JSValue js_parseFloat(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- const char *str, *p;- JSValue ret;-- str = JS_ToCString(ctx, argv[0]);- if (!str)- return JS_EXCEPTION;- p = str;- p += skip_spaces(p);- ret = js_atof(ctx, p, NULL, 10, 0);- JS_FreeCString(ctx, str);- return ret;-}--/* Boolean */-static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst new_target,- int argc, JSValueConst *argv)-{- JSValue val, obj;- val = JS_NewBool(ctx, JS_ToBool(ctx, argv[0]));- if (!JS_IsUndefined(new_target)) {- obj = js_create_from_ctor(ctx, new_target, JS_CLASS_BOOLEAN);- if (!JS_IsException(obj))- JS_SetObjectData(ctx, obj, val);- return obj;- } else {- return val;- }-}--static JSValue js_thisBooleanValue(JSContext *ctx, JSValueConst this_val)-{- if (JS_VALUE_GET_TAG(this_val) == JS_TAG_BOOL)- return JS_DupValue(ctx, this_val);-- if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {- JSObject *p = JS_VALUE_GET_OBJ(this_val);- if (p->class_id == JS_CLASS_BOOLEAN) {- if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_BOOL)- return p->u.object_data;- }- }- return JS_ThrowTypeError(ctx, "not a boolean");-}--static JSValue js_boolean_toString(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue val = js_thisBooleanValue(ctx, this_val);- if (JS_IsException(val))- return val;- return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ?- JS_ATOM_true : JS_ATOM_false);-}--static JSValue js_boolean_valueOf(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return js_thisBooleanValue(ctx, this_val);-}--static const JSCFunctionListEntry js_boolean_proto_funcs[] = {- JS_CFUNC_DEF("toString", 0, js_boolean_toString ),- JS_CFUNC_DEF("valueOf", 0, js_boolean_valueOf ),-};--/* String */--static int js_string_get_own_property(JSContext *ctx,- JSPropertyDescriptor *desc,- JSValueConst obj, JSAtom prop)-{- JSObject *p;- JSString *p1;- uint32_t idx, ch;-- /* This is a class exotic method: obj class_id is JS_CLASS_STRING */- if (__JS_AtomIsTaggedInt(prop)) {- p = JS_VALUE_GET_OBJ(obj);- if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) {- p1 = JS_VALUE_GET_STRING(p->u.object_data);- idx = __JS_AtomToUInt32(prop);- if (idx < p1->len) {- if (desc) {- if (p1->is_wide_char)- ch = p1->u.str16[idx];- else- ch = p1->u.str8[idx];- desc->flags = JS_PROP_ENUMERABLE;- desc->value = js_new_string_char(ctx, ch);- desc->getter = JS_UNDEFINED;- desc->setter = JS_UNDEFINED;- }- return TRUE;- }- }- }- return FALSE;-}--static uint32_t js_string_obj_get_length(JSContext *ctx,- JSValueConst obj)-{- JSObject *p;- JSString *p1;- uint32_t len = 0;-- /* This is a class exotic method: obj class_id is JS_CLASS_STRING */- p = JS_VALUE_GET_OBJ(obj);- if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) {- p1 = JS_VALUE_GET_STRING(p->u.object_data);- len = p1->len;- }- return len;-}--static int js_string_get_own_property_names(JSContext *ctx,- JSPropertyEnum **ptab,- uint32_t *plen,- JSValueConst obj)-{- JSPropertyEnum *tab;- uint32_t len, i;-- len = js_string_obj_get_length(ctx, obj);- tab = NULL;- if (len > 0) {- /* do not allocate 0 bytes */- tab = js_malloc(ctx, sizeof(JSPropertyEnum) * len);- if (!tab)- return -1;- for(i = 0; i < len; i++) {- tab[i].atom = __JS_AtomFromUInt32(i);- }- }- *ptab = tab;- *plen = len;- return 0;-}--static int js_string_define_own_property(JSContext *ctx,- JSValueConst this_obj,- JSAtom prop, JSValueConst val,- JSValueConst getter,- JSValueConst setter, int flags)-{- uint32_t idx;- JSObject *p;- JSString *p1, *p2;- - if (__JS_AtomIsTaggedInt(prop)) {- idx = __JS_AtomToUInt32(prop);- p = JS_VALUE_GET_OBJ(this_obj);- if (JS_VALUE_GET_TAG(p->u.object_data) != JS_TAG_STRING)- goto def;- p1 = JS_VALUE_GET_STRING(p->u.object_data);- if (idx >= p1->len)- goto def;- if (!check_define_prop_flags(JS_PROP_ENUMERABLE, flags))- goto fail;- /* check that the same value is configured */- if (flags & JS_PROP_HAS_VALUE) {- if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)- goto fail;- p2 = JS_VALUE_GET_STRING(val);- if (p2->len != 1)- goto fail;- if (string_get(p1, idx) != string_get(p2, 0)) {- fail:- return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable");- }- }- return TRUE;- } else {- def:- return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter,- flags | JS_PROP_NO_EXOTIC);- }-}--static int js_string_delete_property(JSContext *ctx,- JSValueConst obj, JSAtom prop)-{- uint32_t idx;-- if (__JS_AtomIsTaggedInt(prop)) {- idx = __JS_AtomToUInt32(prop);- if (idx < js_string_obj_get_length(ctx, obj)) {- return FALSE;- }- }- return TRUE;-}--static const JSClassExoticMethods js_string_exotic_methods = {- .get_own_property = js_string_get_own_property,- .get_own_property_names = js_string_get_own_property_names,- .define_own_property = js_string_define_own_property,- .delete_property = js_string_delete_property,-};--static JSValue js_string_constructor(JSContext *ctx, JSValueConst new_target,- int argc, JSValueConst *argv)-{- JSValue val, obj;- if (argc == 0) {- val = JS_AtomToString(ctx, JS_ATOM_empty_string);- } else {- if (JS_IsUndefined(new_target) && JS_IsSymbol(argv[0])) {- JSAtomStruct *p = JS_VALUE_GET_PTR(argv[0]);- val = JS_ConcatString3(ctx, "Symbol(", JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p)), ")");- } else {- val = JS_ToString(ctx, argv[0]);- }- if (JS_IsException(val))- return val;- }- if (!JS_IsUndefined(new_target)) {- JSString *p1 = JS_VALUE_GET_STRING(val);-- obj = js_create_from_ctor(ctx, new_target, JS_CLASS_STRING);- if (!JS_IsException(obj)) {- JS_SetObjectData(ctx, obj, val);- JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0);- }- return obj;- } else {- return val;- }-}--static JSValue js_thisStringValue(JSContext *ctx, JSValueConst this_val)-{- if (JS_VALUE_GET_TAG(this_val) == JS_TAG_STRING)- return JS_DupValue(ctx, this_val);-- if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {- JSObject *p = JS_VALUE_GET_OBJ(this_val);- if (p->class_id == JS_CLASS_STRING) {- if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING)- return JS_DupValue(ctx, p->u.object_data);- }- }- return JS_ThrowTypeError(ctx, "not a string");-}--static JSValue js_string_fromCharCode(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- int i;- StringBuffer b_s, *b = &b_s;-- string_buffer_init(ctx, b, argc);-- for(i = 0; i < argc; i++) {- int32_t c;- if (JS_ToInt32(ctx, &c, argv[i]) || string_buffer_putc16(b, c & 0xffff)) {- string_buffer_free(b);- return JS_EXCEPTION;- }- }- return string_buffer_end(b);-}--static JSValue js_string_fromCodePoint(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- double d;- int i, c;- StringBuffer b_s, *b = &b_s;-- /* XXX: could pre-compute string length if all arguments are JS_TAG_INT */-- if (string_buffer_init(ctx, b, argc))- goto fail;- for(i = 0; i < argc; i++) {- if (JS_VALUE_GET_TAG(argv[i]) == JS_TAG_INT) {- c = JS_VALUE_GET_INT(argv[i]);- if (c < 0 || c > 0x10ffff)- goto range_error;- } else {- if (JS_ToFloat64(ctx, &d, argv[i]))- goto fail;- if (d < 0 || d > 0x10ffff || (c = (int)d) != d)- goto range_error;- }- if (string_buffer_putc(b, c))- goto fail;- }- return string_buffer_end(b);-- range_error:- JS_ThrowRangeError(ctx, "invalid code point");- fail:- string_buffer_free(b);- return JS_EXCEPTION;-}--static JSValue js_string_raw(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- // raw(temp,...a)- JSValue cooked, val, raw;- StringBuffer b_s, *b = &b_s;- int64_t i, n;-- string_buffer_init(ctx, b, 0);- raw = JS_UNDEFINED;- cooked = JS_ToObject(ctx, argv[0]);- if (JS_IsException(cooked))- goto exception;- raw = JS_ToObjectFree(ctx, JS_GetProperty(ctx, cooked, JS_ATOM_raw));- if (JS_IsException(raw))- goto exception;- if (js_get_length64(ctx, &n, raw) < 0)- goto exception;- - for (i = 0; i < n; i++) {- val = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, raw, i));- if (JS_IsException(val))- goto exception;- string_buffer_concat_value_free(b, val);- if (i < n - 1 && i + 1 < argc) {- if (string_buffer_concat_value(b, argv[i + 1]))- goto exception;- }- }- JS_FreeValue(ctx, cooked);- JS_FreeValue(ctx, raw);- return string_buffer_end(b);--exception:- JS_FreeValue(ctx, cooked);- JS_FreeValue(ctx, raw);- string_buffer_free(b);- return JS_EXCEPTION;-}--/* only used in test262 */-JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- uint32_t start, end, i, n;- StringBuffer b_s, *b = &b_s;-- if (JS_ToUint32(ctx, &start, argv[0]) ||- JS_ToUint32(ctx, &end, argv[1]))- return JS_EXCEPTION;- end = min_uint32(end, 0x10ffff + 1);-- if (start > end) {- start = end;- }- n = end - start;- if (end > 0x10000) {- n += end - max_uint32(start, 0x10000);- }- if (string_buffer_init2(ctx, b, n, end >= 0x100))- return JS_EXCEPTION;- for(i = start; i < end; i++) {- string_buffer_putc(b, i);- }- return string_buffer_end(b);-}--#if 0-static JSValue js_string___isSpace(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- int c;- if (JS_ToInt32(ctx, &c, argv[0]))- return JS_EXCEPTION;- return JS_NewBool(ctx, lre_is_space(c));-}-#endif--static JSValue js_string_charCodeAt(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue val, ret;- JSString *p;- int idx, c;-- val = JS_ToStringCheckObject(ctx, this_val);- if (JS_IsException(val))- return val;- p = JS_VALUE_GET_STRING(val);- if (JS_ToInt32Sat(ctx, &idx, argv[0])) {- JS_FreeValue(ctx, val);- return JS_EXCEPTION;- }- if (idx < 0 || idx >= p->len) {- ret = JS_NAN;- } else {- if (p->is_wide_char)- c = p->u.str16[idx];- else- c = p->u.str8[idx];- ret = JS_NewInt32(ctx, c);- }- JS_FreeValue(ctx, val);- return ret;-}--static JSValue js_string_charAt(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue val, ret;- JSString *p;- int idx, c;-- val = JS_ToStringCheckObject(ctx, this_val);- if (JS_IsException(val))- return val;- p = JS_VALUE_GET_STRING(val);- if (JS_ToInt32Sat(ctx, &idx, argv[0])) {- JS_FreeValue(ctx, val);- return JS_EXCEPTION;- }- if (idx < 0 || idx >= p->len) {- ret = js_new_string8(ctx, NULL, 0);- } else {- if (p->is_wide_char)- c = p->u.str16[idx];- else- c = p->u.str8[idx];- ret = js_new_string_char(ctx, c);- }- JS_FreeValue(ctx, val);- return ret;-}--static JSValue js_string_codePointAt(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue val, ret;- JSString *p;- int idx, c;-- val = JS_ToStringCheckObject(ctx, this_val);- if (JS_IsException(val))- return val;- p = JS_VALUE_GET_STRING(val);- if (JS_ToInt32Sat(ctx, &idx, argv[0])) {- JS_FreeValue(ctx, val);- return JS_EXCEPTION;- }- if (idx < 0 || idx >= p->len) {- ret = JS_UNDEFINED;- } else {- c = string_getc(p, &idx);- ret = JS_NewInt32(ctx, c);- }- JS_FreeValue(ctx, val);- return ret;-}--static JSValue js_string_concat(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue r;- int i;-- /* XXX: Use more efficient method */- /* XXX: This method is OK if r has a single refcount */- /* XXX: should use string_buffer? */- r = JS_ToStringCheckObject(ctx, this_val);- for (i = 0; i < argc; i++) {- if (JS_IsException(r))- break;- r = JS_ConcatString(ctx, r, JS_DupValue(ctx, argv[i]));- }- return r;-}--static int string_cmp(JSString *p1, JSString *p2, int x1, int x2, int len)-{- int i, c1, c2;- for (i = 0; i < len; i++) {- if ((c1 = string_get(p1, x1 + i)) != (c2 = string_get(p2, x2 + i)))- return c1 - c2;- }- return 0;-}--static int string_indexof_char(JSString *p, int c, int from)-{- /* assuming 0 <= from <= p->len */- int i, len = p->len;- if (p->is_wide_char) {- for (i = from; i < len; i++) {- if (p->u.str16[i] == c)- return i;- }- } else {- if ((c & ~0xff) == 0) {- for (i = from; i < len; i++) {- if (p->u.str8[i] == (uint8_t)c)- return i;- }- }- }- return -1;-}--static int string_indexof(JSString *p1, JSString *p2, int from)-{- /* assuming 0 <= from <= p1->len */- int c, i, j, len1 = p1->len, len2 = p2->len;- if (len2 == 0)- return from;- for (i = from, c = string_get(p2, 0); i + len2 <= len1; i = j + 1) {- j = string_indexof_char(p1, c, i);- if (j < 0 || j + len2 > len1)- break;- if (!string_cmp(p1, p2, j + 1, 1, len2 - 1))- return j;- }- return -1;-}--static int64_t string_advance_index(JSString *p, int64_t index, BOOL unicode)-{- if (!unicode || index >= p->len || !p->is_wide_char) {- index++;- } else {- int index32 = (int)index;- string_getc(p, &index32);- index = index32;- }- return index;-}--static JSValue js_string_indexOf(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int lastIndexOf)-{- JSValue str, v;- int i, len, v_len, pos, start, stop, ret, inc;- JSString *p;- JSString *p1;-- str = JS_ToStringCheckObject(ctx, this_val);- if (JS_IsException(str))- return str;- v = JS_ToString(ctx, argv[0]);- if (JS_IsException(v))- goto fail;- p = JS_VALUE_GET_STRING(str);- p1 = JS_VALUE_GET_STRING(v);- len = p->len;- v_len = p1->len;- if (lastIndexOf) {- pos = len - v_len;- if (argc > 1) {- double d;- if (JS_ToFloat64(ctx, &d, argv[1]))- goto fail;- if (!isnan(d)) {- if (d <= 0)- pos = 0;- else if (d < pos)- pos = d;- }- }- start = pos;- stop = 0;- inc = -1;- } else {- pos = 0;- if (argc > 1) {- if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0))- goto fail;- }- start = pos;- stop = len - v_len;- inc = 1;- }- ret = -1;- if (len >= v_len && inc * (stop - start) >= 0) {- for (i = start;; i += inc) {- if (!string_cmp(p, p1, i, 0, v_len)) {- ret = i;- break;- }- if (i == stop)- break;- }- }- JS_FreeValue(ctx, str);- JS_FreeValue(ctx, v);- return JS_NewInt32(ctx, ret);--fail:- JS_FreeValue(ctx, str);- JS_FreeValue(ctx, v);- return JS_EXCEPTION;-}--/* return < 0 if exception or TRUE/FALSE */-static int js_is_regexp(JSContext *ctx, JSValueConst obj);--static JSValue js_string_includes(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- JSValue str, v = JS_UNDEFINED;- int i, len, v_len, pos, start, stop, ret;- JSString *p;- JSString *p1;-- str = JS_ToStringCheckObject(ctx, this_val);- if (JS_IsException(str))- return str;- ret = js_is_regexp(ctx, argv[0]);- if (ret) {- if (ret > 0)- JS_ThrowTypeError(ctx, "regex not supported");- goto fail;- }- v = JS_ToString(ctx, argv[0]);- if (JS_IsException(v))- goto fail;- p = JS_VALUE_GET_STRING(str);- p1 = JS_VALUE_GET_STRING(v);- len = p->len;- v_len = p1->len;- pos = (magic == 2) ? len : 0;- if (argc > 1 && !JS_IsUndefined(argv[1])) {- if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0))- goto fail;- }- len -= v_len;- ret = 0;- if (magic == 0) {- start = pos;- stop = len;- } else {- if (magic == 1) {- if (pos > len)- goto done;- } else {- pos -= v_len;- }- start = stop = pos;- }- if (start >= 0 && start <= stop) {- for (i = start;; i++) {- if (!string_cmp(p, p1, i, 0, v_len)) {- ret = 1;- break;- }- if (i == stop)- break;- }- }- done:- JS_FreeValue(ctx, str);- JS_FreeValue(ctx, v);- return JS_NewBool(ctx, ret);--fail:- JS_FreeValue(ctx, str);- JS_FreeValue(ctx, v);- return JS_EXCEPTION;-}--static int check_regexp_g_flag(JSContext *ctx, JSValueConst regexp)-{- int ret;- JSValue flags;- - ret = js_is_regexp(ctx, regexp);- if (ret < 0)- return -1;- if (ret) {- flags = JS_GetProperty(ctx, regexp, JS_ATOM_flags);- if (JS_IsException(flags))- return -1;- if (JS_IsUndefined(flags) || JS_IsNull(flags)) {- JS_ThrowTypeError(ctx, "cannot convert to object");- return -1;- }- flags = JS_ToStringFree(ctx, flags);- if (JS_IsException(flags))- return -1;- ret = string_indexof_char(JS_VALUE_GET_STRING(flags), 'g', 0);- JS_FreeValue(ctx, flags);- if (ret < 0) {- JS_ThrowTypeError(ctx, "regexp must have the 'g' flag");- return -1;- }- }- return 0;-}--static JSValue js_string_match(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int atom)-{- // match(rx), search(rx), matchAll(rx)- // atom is JS_ATOM_Symbol_match, JS_ATOM_Symbol_search, or JS_ATOM_Symbol_matchAll- JSValueConst O = this_val, regexp = argv[0], args[2];- JSValue matcher, S, rx, result, str;- int args_len;-- if (JS_IsUndefined(O) || JS_IsNull(O))- return JS_ThrowTypeError(ctx, "cannot convert to object");-- if (!JS_IsUndefined(regexp) && !JS_IsNull(regexp)) {- matcher = JS_GetProperty(ctx, regexp, atom);- if (JS_IsException(matcher))- return JS_EXCEPTION;- if (atom == JS_ATOM_Symbol_matchAll) {- if (check_regexp_g_flag(ctx, regexp) < 0) {- JS_FreeValue(ctx, matcher);- return JS_EXCEPTION;- }- }- if (!JS_IsUndefined(matcher) && !JS_IsNull(matcher)) {- return JS_CallFree(ctx, matcher, regexp, 1, &O);- }- }- S = JS_ToString(ctx, O);- if (JS_IsException(S))- return JS_EXCEPTION;- args_len = 1;- args[0] = regexp;- str = JS_UNDEFINED;- if (atom == JS_ATOM_Symbol_matchAll) {- str = JS_NewString(ctx, "g");- if (JS_IsException(str))- goto fail;- args[args_len++] = (JSValueConst)str;- }- rx = JS_CallConstructor(ctx, ctx->regexp_ctor, args_len, args);- JS_FreeValue(ctx, str);- if (JS_IsException(rx)) {- fail:- JS_FreeValue(ctx, S);- return JS_EXCEPTION;- }- result = JS_InvokeFree(ctx, rx, atom, 1, (JSValueConst *)&S);- JS_FreeValue(ctx, S);- return result;-}--static JSValue js_string___GetSubstitution(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- // GetSubstitution(matched, str, position, captures, namedCaptures, rep)- JSValueConst matched, str, captures, namedCaptures, rep;- JSValue capture, name, s;- uint32_t position, len, matched_len, captures_len;- int i, j, j0, k, k1;- int c, c1;- StringBuffer b_s, *b = &b_s;- JSString *sp, *rp;-- matched = argv[0];- str = argv[1];- captures = argv[3];- namedCaptures = argv[4];- rep = argv[5];-- if (!JS_IsString(rep) || !JS_IsString(str))- return JS_ThrowTypeError(ctx, "not a string");-- sp = JS_VALUE_GET_STRING(str);- rp = JS_VALUE_GET_STRING(rep);-- string_buffer_init(ctx, b, 0);-- captures_len = 0;- if (!JS_IsUndefined(captures)) {- if (js_get_length32(ctx, &captures_len, captures))- goto exception;- }- if (js_get_length32(ctx, &matched_len, matched))- goto exception;- if (JS_ToUint32(ctx, &position, argv[2]) < 0)- goto exception;-- len = rp->len;- i = 0;- for(;;) {- j = string_indexof_char(rp, '$', i);- if (j < 0 || j + 1 >= len)- break;- string_buffer_concat(b, rp, i, j);- j0 = j++;- c = string_get(rp, j++);- if (c == '$') {- string_buffer_putc8(b, '$');- } else if (c == '&') {- if (string_buffer_concat_value(b, matched))- goto exception;- } else if (c == '`') {- string_buffer_concat(b, sp, 0, position);- } else if (c == '\'') {- string_buffer_concat(b, sp, position + matched_len, sp->len);- } else if (c >= '0' && c <= '9') {- k = c - '0';- c1 = string_get(rp, j);- if (c1 >= '0' && c1 <= '9') {- /* This behavior is specified in ES6 and refined in ECMA 2019 */- /* ECMA 2019 does not have the extra test, but- Test262 S15.5.4.11_A3_T1..3 require this behavior */- k1 = k * 10 + c1 - '0';- if (k1 >= 1 && k1 < captures_len) {- k = k1;- j++;- }- }- if (k >= 1 && k < captures_len) {- s = JS_GetPropertyInt64(ctx, captures, k);- if (JS_IsException(s))- goto exception;- if (!JS_IsUndefined(s)) {- if (string_buffer_concat_value_free(b, s))- goto exception;- }- } else {- goto norep;- }- } else if (c == '<' && !JS_IsUndefined(namedCaptures)) {- k = string_indexof_char(rp, '>', j);- if (k < 0)- goto norep;- name = js_sub_string(ctx, rp, j, k);- if (JS_IsException(name))- goto exception;- capture = JS_GetPropertyValue(ctx, namedCaptures, name);- if (JS_IsException(capture))- goto exception;- if (!JS_IsUndefined(capture)) {- if (string_buffer_concat_value_free(b, capture))- goto exception;- }- j = k + 1;- } else {- norep:- string_buffer_concat(b, rp, j0, j);- }- i = j;- }- string_buffer_concat(b, rp, i, rp->len);- return string_buffer_end(b);-exception:- string_buffer_free(b);- return JS_EXCEPTION;-}--static JSValue js_string_replace(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv,- int is_replaceAll)-{- // replace(rx, rep)- JSValueConst O = this_val, searchValue = argv[0], replaceValue = argv[1];- JSValueConst args[6];- JSValue str, search_str, replaceValue_str, repl_str;- JSString *sp, *searchp;- StringBuffer b_s, *b = &b_s;- int pos, functionalReplace, endOfLastMatch;- BOOL is_first;-- if (JS_IsUndefined(O) || JS_IsNull(O))- return JS_ThrowTypeError(ctx, "cannot convert to object");-- search_str = JS_UNDEFINED;- replaceValue_str = JS_UNDEFINED;- repl_str = JS_UNDEFINED;-- if (!JS_IsUndefined(searchValue) && !JS_IsNull(searchValue)) {- JSValue replacer;- if (is_replaceAll) {- if (check_regexp_g_flag(ctx, searchValue) < 0)- return JS_EXCEPTION;- }- replacer = JS_GetProperty(ctx, searchValue, JS_ATOM_Symbol_replace);- if (JS_IsException(replacer))- return JS_EXCEPTION;- if (!JS_IsUndefined(replacer) && !JS_IsNull(replacer)) {- args[0] = O;- args[1] = replaceValue;- return JS_CallFree(ctx, replacer, searchValue, 2, args);- }- }- string_buffer_init(ctx, b, 0);-- str = JS_ToString(ctx, O);- if (JS_IsException(str))- goto exception;- search_str = JS_ToString(ctx, searchValue);- if (JS_IsException(search_str))- goto exception;- functionalReplace = JS_IsFunction(ctx, replaceValue);- if (!functionalReplace) {- replaceValue_str = JS_ToString(ctx, replaceValue);- if (JS_IsException(replaceValue_str))- goto exception;- }-- sp = JS_VALUE_GET_STRING(str);- searchp = JS_VALUE_GET_STRING(search_str);- endOfLastMatch = 0;- is_first = TRUE;- for(;;) {- if (unlikely(searchp->len == 0)) {- if (is_first)- pos = 0;- else if (endOfLastMatch >= sp->len)- pos = -1;- else- pos = endOfLastMatch + 1;- } else {- pos = string_indexof(sp, searchp, endOfLastMatch);- }- if (pos < 0) {- if (is_first) {- string_buffer_free(b);- JS_FreeValue(ctx, search_str);- JS_FreeValue(ctx, replaceValue_str);- return str;- } else {- break;- }- }- if (functionalReplace) {- args[0] = search_str;- args[1] = JS_NewInt32(ctx, pos);- args[2] = str;- repl_str = JS_ToStringFree(ctx, JS_Call(ctx, replaceValue, JS_UNDEFINED, 3, args));- } else {- args[0] = search_str;- args[1] = str;- args[2] = JS_NewInt32(ctx, pos);- args[3] = JS_UNDEFINED;- args[4] = JS_UNDEFINED;- args[5] = replaceValue_str;- repl_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args);- }- if (JS_IsException(repl_str))- goto exception;- - string_buffer_concat(b, sp, endOfLastMatch, pos);- string_buffer_concat_value_free(b, repl_str);- endOfLastMatch = pos + searchp->len;- is_first = FALSE;- if (!is_replaceAll)- break;- }- string_buffer_concat(b, sp, endOfLastMatch, sp->len);- JS_FreeValue(ctx, search_str);- JS_FreeValue(ctx, replaceValue_str);- JS_FreeValue(ctx, str);- return string_buffer_end(b);--exception:- string_buffer_free(b);- JS_FreeValue(ctx, search_str);- JS_FreeValue(ctx, replaceValue_str);- JS_FreeValue(ctx, str);- return JS_EXCEPTION;-}--static JSValue js_string_split(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- // split(sep, limit)- JSValueConst O = this_val, separator = argv[0], limit = argv[1];- JSValueConst args[2];- JSValue S, A, R, T;- uint32_t lim, lengthA;- int64_t p, q, s, r, e;- JSString *sp, *rp;-- if (JS_IsUndefined(O) || JS_IsNull(O))- return JS_ThrowTypeError(ctx, "cannot convert to object");-- S = JS_UNDEFINED;- A = JS_UNDEFINED;- R = JS_UNDEFINED;-- if (!JS_IsUndefined(separator) && !JS_IsNull(separator)) {- JSValue splitter;- splitter = JS_GetProperty(ctx, separator, JS_ATOM_Symbol_split);- if (JS_IsException(splitter))- return JS_EXCEPTION;- if (!JS_IsUndefined(splitter) && !JS_IsNull(splitter)) {- args[0] = O;- args[1] = limit;- return JS_CallFree(ctx, splitter, separator, 2, args);- }- }- S = JS_ToString(ctx, O);- if (JS_IsException(S))- goto exception;- A = JS_NewArray(ctx);- if (JS_IsException(A))- goto exception;- lengthA = 0;- if (JS_IsUndefined(limit)) {- lim = 0xffffffff;- } else {- if (JS_ToUint32(ctx, &lim, limit) < 0)- goto exception;- }- sp = JS_VALUE_GET_STRING(S);- s = sp->len;- R = JS_ToString(ctx, separator);- if (JS_IsException(R))- goto exception;- rp = JS_VALUE_GET_STRING(R);- r = rp->len;- p = 0;- if (lim == 0)- goto done;- if (JS_IsUndefined(separator))- goto add_tail;- if (s == 0) {- if (r != 0)- goto add_tail;- goto done;- }- q = p;- for (q = p; (q += !r) <= s - r - !r; q = p = e + r) {- e = string_indexof(sp, rp, q);- if (e < 0)- break;- T = js_sub_string(ctx, sp, p, e);- if (JS_IsException(T))- goto exception;- if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T, 0) < 0)- goto exception;- if (lengthA == lim)- goto done;- }-add_tail:- T = js_sub_string(ctx, sp, p, s);- if (JS_IsException(T))- goto exception;- if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T,0 ) < 0)- goto exception;-done:- JS_FreeValue(ctx, S);- JS_FreeValue(ctx, R);- return A;--exception:- JS_FreeValue(ctx, A);- JS_FreeValue(ctx, S);- JS_FreeValue(ctx, R);- return JS_EXCEPTION;-}--static JSValue js_string_substring(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue str, ret;- int a, b, start, end;- JSString *p;-- str = JS_ToStringCheckObject(ctx, this_val);- if (JS_IsException(str))- return str;- p = JS_VALUE_GET_STRING(str);- if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, p->len, 0)) {- JS_FreeValue(ctx, str);- return JS_EXCEPTION;- }- b = p->len;- if (!JS_IsUndefined(argv[1])) {- if (JS_ToInt32Clamp(ctx, &b, argv[1], 0, p->len, 0)) {- JS_FreeValue(ctx, str);- return JS_EXCEPTION;- }- }- if (a < b) {- start = a;- end = b;- } else {- start = b;- end = a;- }- ret = js_sub_string(ctx, p, start, end);- JS_FreeValue(ctx, str);- return ret;-}--static JSValue js_string_substr(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue str, ret;- int a, len, n;- JSString *p;-- str = JS_ToStringCheckObject(ctx, this_val);- if (JS_IsException(str))- return str;- p = JS_VALUE_GET_STRING(str);- len = p->len;- if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, len, len)) {- JS_FreeValue(ctx, str);- return JS_EXCEPTION;- }- n = len - a;- if (!JS_IsUndefined(argv[1])) {- if (JS_ToInt32Clamp(ctx, &n, argv[1], 0, len - a, 0)) {- JS_FreeValue(ctx, str);- return JS_EXCEPTION;- }- }- ret = js_sub_string(ctx, p, a, a + n);- JS_FreeValue(ctx, str);- return ret;-}--static JSValue js_string_slice(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue str, ret;- int len, start, end;- JSString *p;-- str = JS_ToStringCheckObject(ctx, this_val);- if (JS_IsException(str))- return str;- p = JS_VALUE_GET_STRING(str);- len = p->len;- if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) {- JS_FreeValue(ctx, str);- return JS_EXCEPTION;- }- end = len;- if (!JS_IsUndefined(argv[1])) {- if (JS_ToInt32Clamp(ctx, &end, argv[1], 0, len, len)) {- JS_FreeValue(ctx, str);- return JS_EXCEPTION;- }- }- ret = js_sub_string(ctx, p, start, max_int(end, start));- JS_FreeValue(ctx, str);- return ret;-}--static JSValue js_string_pad(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int padEnd)-{- JSValue str, v = JS_UNDEFINED;- StringBuffer b_s, *b = &b_s;- JSString *p, *p1 = NULL;- int n, len, c = ' ';-- str = JS_ToStringCheckObject(ctx, this_val);- if (JS_IsException(str))- goto fail1;- if (JS_ToInt32Sat(ctx, &n, argv[0]))- goto fail2;- p = JS_VALUE_GET_STRING(str);- len = p->len;- if (len >= n)- return str;- if (argc > 1 && !JS_IsUndefined(argv[1])) {- v = JS_ToString(ctx, argv[1]);- if (JS_IsException(v))- goto fail2;- p1 = JS_VALUE_GET_STRING(v);- if (p1->len == 0) {- JS_FreeValue(ctx, v);- return str;- }- if (p1->len == 1) {- c = string_get(p1, 0);- p1 = NULL;- }- }- if (n > JS_STRING_LEN_MAX) {- JS_ThrowInternalError(ctx, "string too long");- goto fail2;- }- if (string_buffer_init(ctx, b, n))- goto fail3;- n -= len;- if (padEnd) {- if (string_buffer_concat(b, p, 0, len))- goto fail;- }- if (p1) {- while (n > 0) {- int chunk = min_int(n, p1->len);- if (string_buffer_concat(b, p1, 0, chunk))- goto fail;- n -= chunk;- }- } else {- if (string_buffer_fill(b, c, n))- goto fail;- }- if (!padEnd) {- if (string_buffer_concat(b, p, 0, len))- goto fail;- }- JS_FreeValue(ctx, v);- JS_FreeValue(ctx, str);- return string_buffer_end(b);--fail:- string_buffer_free(b);-fail3:- JS_FreeValue(ctx, v);-fail2:- JS_FreeValue(ctx, str);-fail1:- return JS_EXCEPTION;-}--static JSValue js_string_repeat(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue str;- StringBuffer b_s, *b = &b_s;- JSString *p;- int64_t val;- int n, len;-- str = JS_ToStringCheckObject(ctx, this_val);- if (JS_IsException(str))- goto fail;- if (JS_ToInt64Sat(ctx, &val, argv[0]))- goto fail;- if (val < 0 || val > 2147483647) {- JS_ThrowRangeError(ctx, "invalid repeat count");- goto fail;- }- n = val;- p = JS_VALUE_GET_STRING(str);- len = p->len;- if (len == 0 || n == 1)- return str;- if (val * len > JS_STRING_LEN_MAX) {- JS_ThrowInternalError(ctx, "string too long");- goto fail;- }- if (string_buffer_init2(ctx, b, n * len, p->is_wide_char))- goto fail;- if (len == 1) {- string_buffer_fill(b, string_get(p, 0), n);- } else {- while (n-- > 0) {- string_buffer_concat(b, p, 0, len);- }- }- JS_FreeValue(ctx, str);- return string_buffer_end(b);--fail:- JS_FreeValue(ctx, str);- return JS_EXCEPTION;-}--static JSValue js_string_trim(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- JSValue str, ret;- int a, b, len;- JSString *p;-- str = JS_ToStringCheckObject(ctx, this_val);- if (JS_IsException(str))- return str;- p = JS_VALUE_GET_STRING(str);- a = 0;- b = len = p->len;- if (magic & 1) {- while (a < len && lre_is_space(string_get(p, a)))- a++;- }- if (magic & 2) {- while (b > a && lre_is_space(string_get(p, b - 1)))- b--;- }- ret = js_sub_string(ctx, p, a, b);- JS_FreeValue(ctx, str);- return ret;-}--static JSValue js_string___quote(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return JS_ToQuotedString(ctx, this_val);-}--/* return 0 if before the first char */-static int string_prevc(JSString *p, int *pidx)-{- int idx, c, c1;-- idx = *pidx;- if (idx <= 0)- return 0;- idx--;- if (p->is_wide_char) {- c = p->u.str16[idx];- if (c >= 0xdc00 && c < 0xe000 && idx > 0) {- c1 = p->u.str16[idx - 1];- if (c1 >= 0xd800 && c1 <= 0xdc00) {- c = (((c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000;- idx--;- }- }- } else {- c = p->u.str8[idx];- }- *pidx = idx;- return c;-}--static BOOL test_final_sigma(JSString *p, int sigma_pos)-{- int k, c1;-- /* before C: skip case ignorable chars and check there is- a cased letter */- k = sigma_pos;- for(;;) {- c1 = string_prevc(p, &k);- if (!lre_is_case_ignorable(c1))- break;- }- if (!lre_is_cased(c1))- return FALSE;-- /* after C: skip case ignorable chars and check there is- no cased letter */- k = sigma_pos + 1;- for(;;) {- if (k >= p->len)- return TRUE;- c1 = string_getc(p, &k);- if (!lre_is_case_ignorable(c1))- break;- }- return !lre_is_cased(c1);-}--static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue a, b;- int cmp;-- a = JS_ToStringCheckObject(ctx, this_val);- if (JS_IsException(a))- return JS_EXCEPTION;- b = JS_ToString(ctx, argv[0]);- if (JS_IsException(b)) {- JS_FreeValue(ctx, a);- return JS_EXCEPTION;- }- cmp = js_string_compare(ctx, JS_VALUE_GET_STRING(a), JS_VALUE_GET_STRING(b));- JS_FreeValue(ctx, a);- JS_FreeValue(ctx, b);- return JS_NewInt32(ctx, cmp);-}--static JSValue js_string_toLowerCase(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int to_lower)-{- JSValue val;- StringBuffer b_s, *b = &b_s;- JSString *p;- int i, c, j, l;- uint32_t res[LRE_CC_RES_LEN_MAX];-- val = JS_ToStringCheckObject(ctx, this_val);- if (JS_IsException(val))- return val;- p = JS_VALUE_GET_STRING(val);- if (p->len == 0)- return val;- if (string_buffer_init(ctx, b, p->len))- goto fail;- for(i = 0; i < p->len;) {- c = string_getc(p, &i);- if (c == 0x3a3 && to_lower && test_final_sigma(p, i - 1)) {- res[0] = 0x3c2; /* final sigma */- l = 1;- } else {- l = lre_case_conv(res, c, to_lower);- }- for(j = 0; j < l; j++) {- if (string_buffer_putc(b, res[j]))- goto fail;- }- }- JS_FreeValue(ctx, val);- return string_buffer_end(b);- fail:- JS_FreeValue(ctx, val);- string_buffer_free(b);- return JS_EXCEPTION;-}--#ifdef CONFIG_ALL_UNICODE--/* return (-1, NULL) if exception, otherwise (len, buf) */-static int JS_ToUTF32String(JSContext *ctx, uint32_t **pbuf, JSValueConst val1)-{- JSValue val;- JSString *p;- uint32_t *buf;- int i, j, len;-- val = JS_ToString(ctx, val1);- if (JS_IsException(val))- return -1;- p = JS_VALUE_GET_STRING(val);- len = p->len;- /* UTF32 buffer length is len minus the number of correct surrogates pairs */- buf = js_malloc(ctx, sizeof(buf[0]) * max_int(len, 1));- if (!buf) {- JS_FreeValue(ctx, val);- goto fail;- }- for(i = j = 0; i < len;)- buf[j++] = string_getc(p, &i);- JS_FreeValue(ctx, val);- *pbuf = buf;- return j;- fail:- *pbuf = NULL;- return -1;-}--static JSValue JS_NewUTF32String(JSContext *ctx, const uint32_t *buf, int len)-{- int i;- StringBuffer b_s, *b = &b_s;- if (string_buffer_init(ctx, b, len))- return JS_EXCEPTION;- for(i = 0; i < len; i++) {- if (string_buffer_putc(b, buf[i]))- goto fail;- }- return string_buffer_end(b);- fail:- string_buffer_free(b);- return JS_EXCEPTION;-}--static JSValue js_string_normalize(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- const char *form, *p;- size_t form_len;- int is_compat, buf_len, out_len;- UnicodeNormalizationEnum n_type;- JSValue val;- uint32_t *buf, *out_buf;-- val = JS_ToStringCheckObject(ctx, this_val);- if (JS_IsException(val))- return val;- buf_len = JS_ToUTF32String(ctx, &buf, val);- JS_FreeValue(ctx, val);- if (buf_len < 0)- return JS_EXCEPTION;-- if (argc == 0 || JS_IsUndefined(argv[0])) {- n_type = UNICODE_NFC;- } else {- form = JS_ToCStringLen(ctx, &form_len, argv[0]);- if (!form)- goto fail1;- p = form;- if (p[0] != 'N' || p[1] != 'F')- goto bad_form;- p += 2;- is_compat = FALSE;- if (*p == 'K') {- is_compat = TRUE;- p++;- }- if (*p == 'C' || *p == 'D') {- n_type = UNICODE_NFC + is_compat * 2 + (*p - 'C');- if ((p + 1 - form) != form_len)- goto bad_form;- } else {- bad_form:- JS_FreeCString(ctx, form);- JS_ThrowRangeError(ctx, "bad normalization form");- fail1:- js_free(ctx, buf);- return JS_EXCEPTION;- }- JS_FreeCString(ctx, form);- }-- out_len = unicode_normalize(&out_buf, buf, buf_len, n_type,- ctx->rt, (DynBufReallocFunc *)js_realloc_rt);- js_free(ctx, buf);- if (out_len < 0)- return JS_EXCEPTION;- val = JS_NewUTF32String(ctx, out_buf, out_len);- js_free(ctx, out_buf);- return val;-}-#endif /* CONFIG_ALL_UNICODE */--/* also used for String.prototype.valueOf */-static JSValue js_string_toString(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return js_thisStringValue(ctx, this_val);-}--#if 0-static JSValue js_string___toStringCheckObject(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return JS_ToStringCheckObject(ctx, argv[0]);-}--static JSValue js_string___toString(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return JS_ToString(ctx, argv[0]);-}--static JSValue js_string___advanceStringIndex(JSContext *ctx, JSValueConst- this_val,- int argc, JSValueConst *argv)-{- JSValue str;- int idx;- BOOL is_unicode;- JSString *p;-- str = JS_ToString(ctx, argv[0]);- if (JS_IsException(str))- return str;- if (JS_ToInt32Sat(ctx, &idx, argv[1])) {- JS_FreeValue(ctx, str);- return JS_EXCEPTION;- }- is_unicode = JS_ToBool(ctx, argv[2]);- p = JS_VALUE_GET_STRING(str);- if (!is_unicode || (unsigned)idx >= p->len || !p->is_wide_char) {- idx++;- } else {- string_getc(p, &idx);- }- JS_FreeValue(ctx, str);- return JS_NewInt32(ctx, idx);-}-#endif--/* String Iterator */--static JSValue js_string_iterator_next(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv,- BOOL *pdone, int magic)-{- JSArrayIteratorData *it;- uint32_t idx, c, start;- JSString *p;-- it = JS_GetOpaque2(ctx, this_val, JS_CLASS_STRING_ITERATOR);- if (!it) {- *pdone = FALSE;- return JS_EXCEPTION;- }- if (JS_IsUndefined(it->obj))- goto done;- p = JS_VALUE_GET_STRING(it->obj);- idx = it->idx;- if (idx >= p->len) {- JS_FreeValue(ctx, it->obj);- it->obj = JS_UNDEFINED;- done:- *pdone = TRUE;- return JS_UNDEFINED;- }-- start = idx;- c = string_getc(p, (int *)&idx);- it->idx = idx;- *pdone = FALSE;- if (c <= 0xffff) {- return js_new_string_char(ctx, c);- } else {- return js_new_string16(ctx, p->u.str16 + start, 2);- }-}--/* ES6 Annex B 2.3.2 etc. */-enum {- magic_string_anchor,- magic_string_big,- magic_string_blink,- magic_string_bold,- magic_string_fixed,- magic_string_fontcolor,- magic_string_fontsize,- magic_string_italics,- magic_string_link,- magic_string_small,- magic_string_strike,- magic_string_sub,- magic_string_sup,-};--static JSValue js_string_CreateHTML(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- JSValue str;- const JSString *p;- StringBuffer b_s, *b = &b_s;- static struct { const char *tag, *attr; } const defs[] = {- { "a", "name" }, { "big", NULL }, { "blink", NULL }, { "b", NULL },- { "tt", NULL }, { "font", "color" }, { "font", "size" }, { "i", NULL },- { "a", "href" }, { "small", NULL }, { "strike", NULL }, - { "sub", NULL }, { "sup", NULL },- };-- str = JS_ToStringCheckObject(ctx, this_val);- if (JS_IsException(str))- return JS_EXCEPTION;- string_buffer_init(ctx, b, 7);- string_buffer_putc8(b, '<');- string_buffer_puts8(b, defs[magic].tag);- if (defs[magic].attr) {- // r += " " + attr + "=\"" + value + "\"";- JSValue value;- int i;-- string_buffer_putc8(b, ' ');- string_buffer_puts8(b, defs[magic].attr);- string_buffer_puts8(b, "=\"");- value = JS_ToStringCheckObject(ctx, argv[0]);- if (JS_IsException(value)) {- JS_FreeValue(ctx, str);- string_buffer_free(b);- return JS_EXCEPTION;- }- p = JS_VALUE_GET_STRING(value);- for (i = 0; i < p->len; i++) {- int c = string_get(p, i);- if (c == '"') {- string_buffer_puts8(b, """);- } else {- string_buffer_putc16(b, c);- }- }- JS_FreeValue(ctx, value);- string_buffer_putc8(b, '\"');- }- // return r + ">" + str + "</" + tag + ">";- string_buffer_putc8(b, '>');- string_buffer_concat_value_free(b, str);- string_buffer_puts8(b, "</");- string_buffer_puts8(b, defs[magic].tag);- string_buffer_putc8(b, '>');- return string_buffer_end(b);-}--static const JSCFunctionListEntry js_string_funcs[] = {- JS_CFUNC_DEF("fromCharCode", 1, js_string_fromCharCode ),- JS_CFUNC_DEF("fromCodePoint", 1, js_string_fromCodePoint ),- JS_CFUNC_DEF("raw", 1, js_string_raw ),- //JS_CFUNC_DEF("__toString", 1, js_string___toString ),- //JS_CFUNC_DEF("__isSpace", 1, js_string___isSpace ),- //JS_CFUNC_DEF("__toStringCheckObject", 1, js_string___toStringCheckObject ),- //JS_CFUNC_DEF("__advanceStringIndex", 3, js_string___advanceStringIndex ),- //JS_CFUNC_DEF("__GetSubstitution", 6, js_string___GetSubstitution ),-};--static const JSCFunctionListEntry js_string_proto_funcs[] = {- JS_PROP_INT32_DEF("length", 0, JS_PROP_CONFIGURABLE ),- JS_CFUNC_DEF("charCodeAt", 1, js_string_charCodeAt ),- JS_CFUNC_DEF("charAt", 1, js_string_charAt ),- JS_CFUNC_DEF("concat", 1, js_string_concat ),- JS_CFUNC_DEF("codePointAt", 1, js_string_codePointAt ),- JS_CFUNC_MAGIC_DEF("indexOf", 1, js_string_indexOf, 0 ),- JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_string_indexOf, 1 ),- JS_CFUNC_MAGIC_DEF("includes", 1, js_string_includes, 0 ),- JS_CFUNC_MAGIC_DEF("endsWith", 1, js_string_includes, 2 ),- JS_CFUNC_MAGIC_DEF("startsWith", 1, js_string_includes, 1 ),- JS_CFUNC_MAGIC_DEF("match", 1, js_string_match, JS_ATOM_Symbol_match ),- JS_CFUNC_MAGIC_DEF("matchAll", 1, js_string_match, JS_ATOM_Symbol_matchAll ),- JS_CFUNC_MAGIC_DEF("search", 1, js_string_match, JS_ATOM_Symbol_search ),- JS_CFUNC_DEF("split", 2, js_string_split ),- JS_CFUNC_DEF("substring", 2, js_string_substring ),- JS_CFUNC_DEF("substr", 2, js_string_substr ),- JS_CFUNC_DEF("slice", 2, js_string_slice ),- JS_CFUNC_DEF("repeat", 1, js_string_repeat ),- JS_CFUNC_MAGIC_DEF("replace", 2, js_string_replace, 0 ),- JS_CFUNC_MAGIC_DEF("replaceAll", 2, js_string_replace, 1 ),- JS_CFUNC_MAGIC_DEF("padEnd", 1, js_string_pad, 1 ),- JS_CFUNC_MAGIC_DEF("padStart", 1, js_string_pad, 0 ),- JS_CFUNC_MAGIC_DEF("trim", 0, js_string_trim, 3 ),- JS_CFUNC_MAGIC_DEF("trimEnd", 0, js_string_trim, 2 ),- JS_ALIAS_DEF("trimRight", "trimEnd" ),- JS_CFUNC_MAGIC_DEF("trimStart", 0, js_string_trim, 1 ),- JS_ALIAS_DEF("trimLeft", "trimStart" ),- JS_CFUNC_DEF("toString", 0, js_string_toString ),- JS_CFUNC_DEF("valueOf", 0, js_string_toString ),- JS_CFUNC_DEF("__quote", 1, js_string___quote ),- JS_CFUNC_DEF("localeCompare", 1, js_string_localeCompare ),- JS_CFUNC_MAGIC_DEF("toLowerCase", 0, js_string_toLowerCase, 1 ),- JS_CFUNC_MAGIC_DEF("toUpperCase", 0, js_string_toLowerCase, 0 ),- JS_CFUNC_MAGIC_DEF("toLocaleLowerCase", 0, js_string_toLowerCase, 1 ),- JS_CFUNC_MAGIC_DEF("toLocaleUpperCase", 0, js_string_toLowerCase, 0 ),- JS_CFUNC_MAGIC_DEF("[Symbol.iterator]", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE | 4 ),- /* ES6 Annex B 2.3.2 etc. */- JS_CFUNC_MAGIC_DEF("anchor", 1, js_string_CreateHTML, magic_string_anchor ),- JS_CFUNC_MAGIC_DEF("big", 0, js_string_CreateHTML, magic_string_big ),- JS_CFUNC_MAGIC_DEF("blink", 0, js_string_CreateHTML, magic_string_blink ),- JS_CFUNC_MAGIC_DEF("bold", 0, js_string_CreateHTML, magic_string_bold ),- JS_CFUNC_MAGIC_DEF("fixed", 0, js_string_CreateHTML, magic_string_fixed ),- JS_CFUNC_MAGIC_DEF("fontcolor", 1, js_string_CreateHTML, magic_string_fontcolor ),- JS_CFUNC_MAGIC_DEF("fontsize", 1, js_string_CreateHTML, magic_string_fontsize ),- JS_CFUNC_MAGIC_DEF("italics", 0, js_string_CreateHTML, magic_string_italics ),- JS_CFUNC_MAGIC_DEF("link", 1, js_string_CreateHTML, magic_string_link ),- JS_CFUNC_MAGIC_DEF("small", 0, js_string_CreateHTML, magic_string_small ),- JS_CFUNC_MAGIC_DEF("strike", 0, js_string_CreateHTML, magic_string_strike ),- JS_CFUNC_MAGIC_DEF("sub", 0, js_string_CreateHTML, magic_string_sub ),- JS_CFUNC_MAGIC_DEF("sup", 0, js_string_CreateHTML, magic_string_sup ),-};--static const JSCFunctionListEntry js_string_iterator_proto_funcs[] = {- JS_ITERATOR_NEXT_DEF("next", 0, js_string_iterator_next, 0 ),- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "String Iterator", JS_PROP_CONFIGURABLE ),-};--#ifdef CONFIG_ALL_UNICODE-static const JSCFunctionListEntry js_string_proto_normalize[] = {- JS_CFUNC_DEF("normalize", 0, js_string_normalize ),-};-#endif--void JS_AddIntrinsicStringNormalize(JSContext *ctx)-{-#ifdef CONFIG_ALL_UNICODE- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_normalize,- countof(js_string_proto_normalize));-#endif-}--/* Math */--/* precondition: a and b are not NaN */-static double js_fmin(double a, double b)-{- if (a == 0 && b == 0) {- JSFloat64Union a1, b1;- a1.d = a;- b1.d = b;- a1.u64 |= b1.u64;- return a1.d;- } else {- return fmin(a, b);- }-}--/* precondition: a and b are not NaN */-static double js_fmax(double a, double b)-{- if (a == 0 && b == 0) {- JSFloat64Union a1, b1;- a1.d = a;- b1.d = b;- a1.u64 &= b1.u64;- return a1.d;- } else {- return fmax(a, b);- }-}--static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- BOOL is_max = magic;- double r, a;- int i;- uint32_t tag;-- if (unlikely(argc == 0)) {- return __JS_NewFloat64(ctx, is_max ? -1.0 / 0.0 : 1.0 / 0.0);- }-- tag = JS_VALUE_GET_TAG(argv[0]);- if (tag == JS_TAG_INT) {- int a1, r1 = JS_VALUE_GET_INT(argv[0]);- for(i = 1; i < argc; i++) {- tag = JS_VALUE_GET_TAG(argv[i]);- if (tag != JS_TAG_INT) {- r = r1;- goto generic_case;- }- a1 = JS_VALUE_GET_INT(argv[i]);- if (is_max)- r1 = max_int(r1, a1);- else- r1 = min_int(r1, a1);-- }- return JS_NewInt32(ctx, r1);- } else {- if (JS_ToFloat64(ctx, &r, argv[0]))- return JS_EXCEPTION;- i = 1;- generic_case:- while (i < argc) {- if (JS_ToFloat64(ctx, &a, argv[i]))- return JS_EXCEPTION;- if (!isnan(r)) {- if (isnan(a)) {- r = a;- } else {- if (is_max)- r = js_fmax(r, a);- else- r = js_fmin(r, a);- }- }- i++;- }- return JS_NewFloat64(ctx, r);- }-}--static double js_math_sign(double a)-{- if (isnan(a) || a == 0.0)- return a;- if (a < 0)- return -1;- else- return 1;-}--static double js_math_round(double a)-{- JSFloat64Union u;- uint64_t frac_mask, one;- unsigned int e, s;-- u.d = a;- e = (u.u64 >> 52) & 0x7ff;- if (e < 1023) {- /* abs(a) < 1 */- if (e == (1023 - 1) && u.u64 != 0xbfe0000000000000) {- /* abs(a) > 0.5 or a = 0.5: return +/-1.0 */- u.u64 = (u.u64 & ((uint64_t)1 << 63)) | ((uint64_t)1023 << 52);- } else {- /* return +/-0.0 */- u.u64 &= (uint64_t)1 << 63;- }- } else if (e < (1023 + 52)) {- s = u.u64 >> 63;- one = (uint64_t)1 << (52 - (e - 1023));- frac_mask = one - 1;- u.u64 += (one >> 1) - s;- u.u64 &= ~frac_mask; /* truncate to an integer */- }- /* otherwise: abs(a) >= 2^52, or NaN, +/-Infinity: no change */- return u.d;-}--static JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- double r, a;- int i;-- r = 0;- if (argc > 0) {- if (JS_ToFloat64(ctx, &r, argv[0]))- return JS_EXCEPTION;- if (argc == 1) {- r = fabs(r);- } else {- /* use the built-in function to minimize precision loss */- for (i = 1; i < argc; i++) {- if (JS_ToFloat64(ctx, &a, argv[i]))- return JS_EXCEPTION;- r = hypot(r, a);- }- }- }- return JS_NewFloat64(ctx, r);-}--static double js_math_fround(double a)-{- return (float)a;-}--static JSValue js_math_imul(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- int a, b;-- if (JS_ToInt32(ctx, &a, argv[0]))- return JS_EXCEPTION;- if (JS_ToInt32(ctx, &b, argv[1]))- return JS_EXCEPTION;- /* purposely ignoring overflow */- return JS_NewInt32(ctx, a * b);-}--static JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- uint32_t a, r;-- if (JS_ToUint32(ctx, &a, argv[0]))- return JS_EXCEPTION;- if (a == 0)- r = 32;- else- r = clz32(a);- return JS_NewInt32(ctx, r);-}--/* xorshift* random number generator by Marsaglia */-static uint64_t xorshift64star(uint64_t *pstate)-{- uint64_t x;- x = *pstate;- x ^= x >> 12;- x ^= x << 25;- x ^= x >> 27;- *pstate = x;- return x * 0x2545F4914F6CDD1D;-}--static void js_random_init(JSContext *ctx)-{- struct timeval tv;- gettimeofday(&tv, NULL);- ctx->random_state = ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec;- /* the state must be non zero */- if (ctx->random_state == 0)- ctx->random_state = 1;-}--static JSValue js_math_random(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSFloat64Union u;- uint64_t v;-- v = xorshift64star(&ctx->random_state);- /* 1.0 <= u.d < 2 */- u.u64 = ((uint64_t)0x3ff << 52) | (v >> 12);- return __JS_NewFloat64(ctx, u.d - 1.0);-}--static const JSCFunctionListEntry js_math_funcs[] = {- JS_CFUNC_MAGIC_DEF("min", 2, js_math_min_max, 0 ),- JS_CFUNC_MAGIC_DEF("max", 2, js_math_min_max, 1 ),- JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, fabs ),- JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, floor ),- JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, ceil ),- JS_CFUNC_SPECIAL_DEF("round", 1, f_f, js_math_round ),- JS_CFUNC_SPECIAL_DEF("sqrt", 1, f_f, sqrt ),-- JS_CFUNC_SPECIAL_DEF("acos", 1, f_f, acos ),- JS_CFUNC_SPECIAL_DEF("asin", 1, f_f, asin ),- JS_CFUNC_SPECIAL_DEF("atan", 1, f_f, atan ),- JS_CFUNC_SPECIAL_DEF("atan2", 2, f_f_f, atan2 ),- JS_CFUNC_SPECIAL_DEF("cos", 1, f_f, cos ),- JS_CFUNC_SPECIAL_DEF("exp", 1, f_f, exp ),- JS_CFUNC_SPECIAL_DEF("log", 1, f_f, log ),- JS_CFUNC_SPECIAL_DEF("pow", 2, f_f_f, js_pow ),- JS_CFUNC_SPECIAL_DEF("sin", 1, f_f, sin ),- JS_CFUNC_SPECIAL_DEF("tan", 1, f_f, tan ),- /* ES6 */- JS_CFUNC_SPECIAL_DEF("trunc", 1, f_f, trunc ),- JS_CFUNC_SPECIAL_DEF("sign", 1, f_f, js_math_sign ),- JS_CFUNC_SPECIAL_DEF("cosh", 1, f_f, cosh ),- JS_CFUNC_SPECIAL_DEF("sinh", 1, f_f, sinh ),- JS_CFUNC_SPECIAL_DEF("tanh", 1, f_f, tanh ),- JS_CFUNC_SPECIAL_DEF("acosh", 1, f_f, acosh ),- JS_CFUNC_SPECIAL_DEF("asinh", 1, f_f, asinh ),- JS_CFUNC_SPECIAL_DEF("atanh", 1, f_f, atanh ),- JS_CFUNC_SPECIAL_DEF("expm1", 1, f_f, expm1 ),- JS_CFUNC_SPECIAL_DEF("log1p", 1, f_f, log1p ),- JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, log2 ),- JS_CFUNC_SPECIAL_DEF("log10", 1, f_f, log10 ),- JS_CFUNC_SPECIAL_DEF("cbrt", 1, f_f, cbrt ),- JS_CFUNC_DEF("hypot", 2, js_math_hypot ),- JS_CFUNC_DEF("random", 0, js_math_random ),- JS_CFUNC_SPECIAL_DEF("fround", 1, f_f, js_math_fround ),- JS_CFUNC_DEF("imul", 2, js_math_imul ),- JS_CFUNC_DEF("clz32", 1, js_math_clz32 ),- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Math", JS_PROP_CONFIGURABLE ),- JS_PROP_DOUBLE_DEF("E", 2.718281828459045, 0 ),- JS_PROP_DOUBLE_DEF("LN10", 2.302585092994046, 0 ),- JS_PROP_DOUBLE_DEF("LN2", 0.6931471805599453, 0 ),- JS_PROP_DOUBLE_DEF("LOG2E", 1.4426950408889634, 0 ),- JS_PROP_DOUBLE_DEF("LOG10E", 0.4342944819032518, 0 ),- JS_PROP_DOUBLE_DEF("PI", 3.141592653589793, 0 ),- JS_PROP_DOUBLE_DEF("SQRT1_2", 0.7071067811865476, 0 ),- JS_PROP_DOUBLE_DEF("SQRT2", 1.4142135623730951, 0 ),-};--static const JSCFunctionListEntry js_math_obj[] = {- JS_OBJECT_DEF("Math", js_math_funcs, countof(js_math_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),-};--/* Date */--#if 0-/* OS dependent: return the UTC time in ms since 1970. */-static JSValue js___date_now(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- int64_t d;- struct timeval tv;- gettimeofday(&tv, NULL);- d = (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);- return JS_NewInt64(ctx, d);-}-#endif--/* OS dependent: return the UTC time in microseconds since 1970. */-static JSValue js___date_clock(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- int64_t d;- struct timeval tv;- gettimeofday(&tv, NULL);- d = (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;- return JS_NewInt64(ctx, d);-}--/* OS dependent. d = argv[0] is in ms from 1970. Return the difference- between local time and UTC time 'd' in minutes */-static int getTimezoneOffset(int64_t time) {-#if defined(_WIN32)- /* XXX: TODO */- return 0;-#else- time_t ti;- struct tm tm;-- time /= 1000; /* convert to seconds */- if (sizeof(time_t) == 4) {- /* on 32-bit systems, we need to clamp the time value to the- range of `time_t`. This is better than truncating values to- 32 bits and hopefully provides the same result as 64-bit- implementation of localtime_r.- */- if ((time_t)-1 < 0) {- if (time < INT32_MIN) {- time = INT32_MIN;- } else if (time > INT32_MAX) {- time = INT32_MAX;- }- } else {- if (time < 0) {- time = 0;- } else if (time > UINT32_MAX) {- time = UINT32_MAX;- }- }- }- ti = time;- localtime_r(&ti, &tm);- return -tm.tm_gmtoff / 60;-#endif-}--#if 0-static JSValue js___date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- double dd;-- if (JS_ToFloat64(ctx, &dd, argv[0]))- return JS_EXCEPTION;- if (isnan(dd))- return __JS_NewFloat64(ctx, dd);- else- return JS_NewInt32(ctx, getTimezoneOffset((int64_t)dd));-}--static JSValue js_get_prototype_from_ctor(JSContext *ctx, JSValueConst ctor,- JSValueConst def_proto)-{- JSValue proto;- proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype);- if (JS_IsException(proto))- return proto;- if (!JS_IsObject(proto)) {- JS_FreeValue(ctx, proto);- proto = JS_DupValue(ctx, def_proto);- }- return proto;-}--/* create a new date object */-static JSValue js___date_create(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue obj, proto;- proto = js_get_prototype_from_ctor(ctx, argv[0], argv[1]);- if (JS_IsException(proto))- return proto;- obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_DATE);- JS_FreeValue(ctx, proto);- if (!JS_IsException(obj))- JS_SetObjectData(ctx, obj, JS_DupValue(ctx, argv[2]));- return obj;-}-#endif--/* RegExp */--static void js_regexp_finalizer(JSRuntime *rt, JSValue val)-{- JSObject *p = JS_VALUE_GET_OBJ(val);- JSRegExp *re = &p->u.regexp;- JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->bytecode));- JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->pattern));-}--/* create a string containing the RegExp bytecode */-static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern,- JSValueConst flags)-{- const char *str;- int re_flags, mask;- uint8_t *re_bytecode_buf;- size_t i, len;- int re_bytecode_len;- JSValue ret;- char error_msg[64];-- re_flags = 0;- if (!JS_IsUndefined(flags)) {- str = JS_ToCStringLen(ctx, &len, flags);- if (!str)- return JS_EXCEPTION;- /* XXX: re_flags = LRE_FLAG_OCTAL unless strict mode? */- for (i = 0; i < len; i++) {- switch(str[i]) {- case 'g':- mask = LRE_FLAG_GLOBAL;- break;- case 'i':- mask = LRE_FLAG_IGNORECASE;- break;- case 'm':- mask = LRE_FLAG_MULTILINE;- break;- case 's':- mask = LRE_FLAG_DOTALL;- break;- case 'u':- mask = LRE_FLAG_UTF16;- break;- case 'y':- mask = LRE_FLAG_STICKY;- break;- default:- goto bad_flags;- }- if ((re_flags & mask) != 0) {- bad_flags:- JS_FreeCString(ctx, str);- return JS_ThrowSyntaxError(ctx, "invalid regular expression flags");- }- re_flags |= mask;- }- JS_FreeCString(ctx, str);- }-- str = JS_ToCStringLen2(ctx, &len, pattern, !(re_flags & LRE_FLAG_UTF16));- if (!str)- return JS_EXCEPTION;- re_bytecode_buf = lre_compile(&re_bytecode_len, error_msg,- sizeof(error_msg), str, len, re_flags, ctx);- JS_FreeCString(ctx, str);- if (!re_bytecode_buf) {- JS_ThrowSyntaxError(ctx, "%s", error_msg);- return JS_EXCEPTION;- }-- ret = js_new_string8(ctx, re_bytecode_buf, re_bytecode_len);- js_free(ctx, re_bytecode_buf);- return ret;-}--/* create a RegExp object from a string containing the RegExp bytecode- and the source pattern */-static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor,- JSValue pattern, JSValue bc)-{- JSValue obj;- JSObject *p;- JSRegExp *re;-- /* sanity check */- if (JS_VALUE_GET_TAG(bc) != JS_TAG_STRING ||- JS_VALUE_GET_TAG(pattern) != JS_TAG_STRING) {- JS_ThrowTypeError(ctx, "string expected");- fail:- JS_FreeValue(ctx, bc);- JS_FreeValue(ctx, pattern);- return JS_EXCEPTION;- }-- obj = js_create_from_ctor(ctx, ctor, JS_CLASS_REGEXP);- if (JS_IsException(obj))- goto fail;- p = JS_VALUE_GET_OBJ(obj);- re = &p->u.regexp;- re->pattern = JS_VALUE_GET_STRING(pattern);- re->bytecode = JS_VALUE_GET_STRING(bc);- JS_DefinePropertyValue(ctx, obj, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0),- JS_PROP_WRITABLE);- return obj;-}--static JSRegExp *js_get_regexp(JSContext *ctx, JSValueConst obj, BOOL throw_error)-{- if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {- JSObject *p = JS_VALUE_GET_OBJ(obj);- if (p->class_id == JS_CLASS_REGEXP)- return &p->u.regexp;- }- if (throw_error) {- JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP);- }- return NULL;-}--/* return < 0 if exception or TRUE/FALSE */-static int js_is_regexp(JSContext *ctx, JSValueConst obj)-{- JSValue m;-- if (!JS_IsObject(obj))- return FALSE;- m = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_match);- if (JS_IsException(m))- return -1;- if (!JS_IsUndefined(m))- return JS_ToBoolFree(ctx, m);- return js_get_regexp(ctx, obj, FALSE) != NULL;-}--static JSValue js_regexp_constructor(JSContext *ctx, JSValueConst new_target,- int argc, JSValueConst *argv)-{- JSValue pattern, flags, bc, val;- JSValueConst pat, flags1;- JSRegExp *re;- int pat_is_regexp;-- pat = argv[0];- flags1 = argv[1];- pat_is_regexp = js_is_regexp(ctx, pat);- if (pat_is_regexp < 0)- return JS_EXCEPTION;- if (JS_IsUndefined(new_target)) {- /* called as a function */- new_target = JS_GetActiveFunction(ctx);- if (pat_is_regexp && JS_IsUndefined(flags1)) {- JSValue ctor;- BOOL res;- ctor = JS_GetProperty(ctx, pat, JS_ATOM_constructor);- if (JS_IsException(ctor))- return ctor;- res = js_same_value(ctx, ctor, new_target);- JS_FreeValue(ctx, ctor);- if (res)- return JS_DupValue(ctx, pat);- }- }- re = js_get_regexp(ctx, pat, FALSE);- if (re) {- pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));- if (JS_IsUndefined(flags1)) {- bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode));- goto no_compilation;- } else {- flags = JS_ToString(ctx, flags1);- if (JS_IsException(flags))- goto fail;- }- } else {- flags = JS_UNDEFINED;- if (pat_is_regexp) {- pattern = JS_GetProperty(ctx, pat, JS_ATOM_source);- if (JS_IsException(pattern))- goto fail;- if (JS_IsUndefined(flags1)) {- flags = JS_GetProperty(ctx, pat, JS_ATOM_flags);- if (JS_IsException(flags))- goto fail;- } else {- flags = JS_DupValue(ctx, flags1);- }- } else {- pattern = JS_DupValue(ctx, pat);- flags = JS_DupValue(ctx, flags1);- }- if (JS_IsUndefined(pattern)) {- pattern = JS_AtomToString(ctx, JS_ATOM_empty_string);- } else {- val = pattern;- pattern = JS_ToString(ctx, val);- JS_FreeValue(ctx, val);- if (JS_IsException(pattern))- goto fail;- }- }- bc = js_compile_regexp(ctx, pattern, flags);- if (JS_IsException(bc))- goto fail;- JS_FreeValue(ctx, flags);- no_compilation:- return js_regexp_constructor_internal(ctx, new_target, pattern, bc);- fail:- JS_FreeValue(ctx, pattern);- JS_FreeValue(ctx, flags);- return JS_EXCEPTION;-}--static JSValue js_regexp_compile(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSRegExp *re1, *re;- JSValueConst pattern1, flags1;- JSValue bc, pattern;-- re = js_get_regexp(ctx, this_val, TRUE);- if (!re)- return JS_EXCEPTION;- pattern1 = argv[0];- flags1 = argv[1];- re1 = js_get_regexp(ctx, pattern1, FALSE);- if (re1) {- if (!JS_IsUndefined(flags1))- return JS_ThrowTypeError(ctx, "flags must be undefined");- pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->pattern));- bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->bytecode));- } else {- bc = JS_UNDEFINED;- if (JS_IsUndefined(pattern1))- pattern = JS_AtomToString(ctx, JS_ATOM_empty_string);- else- pattern = JS_ToString(ctx, pattern1);- if (JS_IsException(pattern))- goto fail;- bc = js_compile_regexp(ctx, pattern, flags1);- if (JS_IsException(bc))- goto fail;- }- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode));- re->pattern = JS_VALUE_GET_STRING(pattern);- re->bytecode = JS_VALUE_GET_STRING(bc);- if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,- JS_NewInt32(ctx, 0)) < 0)- return JS_EXCEPTION;- return JS_DupValue(ctx, this_val);- fail:- JS_FreeValue(ctx, pattern);- JS_FreeValue(ctx, bc);- return JS_EXCEPTION;-}--#if 0-static JSValue js_regexp_get___source(JSContext *ctx, JSValueConst this_val)-{- JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);- if (!re)- return JS_EXCEPTION;- return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));-}--static JSValue js_regexp_get___flags(JSContext *ctx, JSValueConst this_val)-{- JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);- int flags;-- if (!re)- return JS_EXCEPTION;- flags = lre_get_flags(re->bytecode->u.str8);- return JS_NewInt32(ctx, flags);-}-#endif--static JSValue js_regexp_get_source(JSContext *ctx, JSValueConst this_val)-{- JSRegExp *re;- JSString *p;- StringBuffer b_s, *b = &b_s;- int i, n, c, c2, bra;-- if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)- return JS_ThrowTypeErrorNotAnObject(ctx);-- if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP]))- goto empty_regex;-- re = js_get_regexp(ctx, this_val, TRUE);- if (!re)- return JS_EXCEPTION;-- p = re->pattern;-- if (p->len == 0) {- empty_regex:- return JS_NewString(ctx, "(?:)");- } - string_buffer_init2(ctx, b, p->len, p->is_wide_char);-- /* Escape '/' and newline sequences as needed */- bra = 0;- for (i = 0, n = p->len; i < n;) {- c2 = -1;- switch (c = string_get(p, i++)) {- case '\\':- if (i < n)- c2 = string_get(p, i++);- break;- case ']':- bra = 0;- break;- case '[':- if (!bra) {- if (i < n && string_get(p, i) == ']')- c2 = string_get(p, i++);- bra = 1;- }- break;- case '\n':- c = '\\';- c2 = 'n';- break;- case '\r':- c = '\\';- c2 = 'r';- break;- case '/':- if (!bra) {- c = '\\';- c2 = '/';- }- break;- }- string_buffer_putc16(b, c);- if (c2 >= 0)- string_buffer_putc16(b, c2);- }- return string_buffer_end(b);-}--static JSValue js_regexp_get_flag(JSContext *ctx, JSValueConst this_val, int mask)-{- JSRegExp *re;- int flags;-- if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)- return JS_ThrowTypeErrorNotAnObject(ctx);-- re = js_get_regexp(ctx, this_val, FALSE);- if (!re) {- if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP]))- return JS_UNDEFINED;- else- return JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP);- }- - flags = lre_get_flags(re->bytecode->u.str8);- return JS_NewBool(ctx, (flags & mask) != 0);-}--static JSValue js_regexp_get_flags(JSContext *ctx, JSValueConst this_val)-{- char str[8], *p = str;- int res;-- if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)- return JS_ThrowTypeErrorNotAnObject(ctx);-- res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_global));- if (res < 0)- goto exception;- if (res)- *p++ = 'g';- res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "ignoreCase"));- if (res < 0)- goto exception;- if (res)- *p++ = 'i';- res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "multiline"));- if (res < 0)- goto exception;- if (res)- *p++ = 'm';- res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "dotAll"));- if (res < 0)- goto exception;- if (res)- *p++ = 's';- res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_unicode));- if (res < 0)- goto exception;- if (res)- *p++ = 'u';- res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "sticky"));- if (res < 0)- goto exception;- if (res)- *p++ = 'y';- return JS_NewStringLen(ctx, str, p - str);--exception:- return JS_EXCEPTION;-}--static JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue pattern, flags;- StringBuffer b_s, *b = &b_s;-- if (!JS_IsObject(this_val))- return JS_ThrowTypeErrorNotAnObject(ctx);-- string_buffer_init(ctx, b, 0);- string_buffer_putc8(b, '/');- pattern = JS_GetProperty(ctx, this_val, JS_ATOM_source);- if (string_buffer_concat_value_free(b, pattern))- goto fail;- string_buffer_putc8(b, '/');- flags = JS_GetProperty(ctx, this_val, JS_ATOM_flags);- if (string_buffer_concat_value_free(b, flags))- goto fail;- return string_buffer_end(b);--fail:- string_buffer_free(b);- return JS_EXCEPTION;-}--BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size)-{- JSContext *ctx = opaque;- return js_check_stack_overflow(ctx->rt, alloca_size);-}--void *lre_realloc(void *opaque, void *ptr, size_t size)-{- JSContext *ctx = opaque;- /* No JS exception is raised here */- return js_realloc_rt(ctx->rt, ptr, size);-}--static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);- JSString *str;- JSValue str_val, obj, val, groups = JS_UNDEFINED;- uint8_t *re_bytecode;- int ret;- uint8_t **capture, *str_buf;- int capture_count, shift, i, re_flags;- int64_t last_index;- const char *group_name_ptr;-- if (!re)- return JS_EXCEPTION;- str_val = JS_ToString(ctx, argv[0]);- if (JS_IsException(str_val))- return str_val;- val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex);- if (JS_IsException(val) ||- JS_ToLengthFree(ctx, &last_index, val)) {- JS_FreeValue(ctx, str_val);- return JS_EXCEPTION;- }- re_bytecode = re->bytecode->u.str8;- re_flags = lre_get_flags(re_bytecode);- if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) {- last_index = 0;- }- str = JS_VALUE_GET_STRING(str_val);- capture_count = lre_get_capture_count(re_bytecode);- capture = NULL;- if (capture_count > 0) {- capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2);- if (!capture) {- JS_FreeValue(ctx, str_val);- return JS_EXCEPTION;- }- }- shift = str->is_wide_char;- str_buf = str->u.str8;- if (last_index > str->len) {- ret = 2;- } else {- ret = lre_exec(capture, re_bytecode,- str_buf, last_index, str->len,- shift, ctx);- }- obj = JS_NULL;- if (ret != 1) {- if (ret >= 0) {- if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) {- if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,- JS_NewInt32(ctx, 0)) < 0)- goto fail;- }- } else {- JS_ThrowInternalError(ctx, "out of memory in regexp execution");- goto fail;- }- JS_FreeValue(ctx, str_val);- } else {- int prop_flags;- if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) {- if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,- JS_NewInt32(ctx, (capture[1] - str_buf) >> shift)) < 0)- goto fail;- }- obj = JS_NewArray(ctx);- if (JS_IsException(obj))- goto fail;- prop_flags = JS_PROP_C_W_E | JS_PROP_THROW;- group_name_ptr = lre_get_groupnames(re_bytecode);- if (group_name_ptr) {- groups = JS_NewObjectProto(ctx, JS_NULL);- if (JS_IsException(groups))- goto fail;- }-- for(i = 0; i < capture_count; i++) {- int start, end;- JSValue val;- if (capture[2 * i] == NULL ||- capture[2 * i + 1] == NULL) {- val = JS_UNDEFINED;- } else {- start = (capture[2 * i] - str_buf) >> shift;- end = (capture[2 * i + 1] - str_buf) >> shift;- val = js_sub_string(ctx, str, start, end);- if (JS_IsException(val))- goto fail;- }- if (group_name_ptr && i > 0) {- if (*group_name_ptr) {- if (JS_DefinePropertyValueStr(ctx, groups, group_name_ptr,- JS_DupValue(ctx, val),- prop_flags) < 0) {- JS_FreeValue(ctx, val);- goto fail;- }- }- group_name_ptr += strlen(group_name_ptr) + 1;- }- if (JS_DefinePropertyValueUint32(ctx, obj, i, val, prop_flags) < 0)- goto fail;- }- if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_groups,- groups, prop_flags) < 0)- goto fail;- if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_index,- JS_NewInt32(ctx, (capture[0] - str_buf) >> shift), prop_flags) < 0)- goto fail;- if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_input, str_val, prop_flags) < 0)- goto fail1;- }- js_free(ctx, capture);- return obj;-fail:- JS_FreeValue(ctx, groups);- JS_FreeValue(ctx, str_val);-fail1:- JS_FreeValue(ctx, obj);- js_free(ctx, capture);- return JS_EXCEPTION;-}--/* delete portions of a string that match a given regex */-static JSValue JS_RegExpDelete(JSContext *ctx, JSValueConst this_val, JSValueConst arg)-{- JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);- JSString *str;- JSValue str_val, val;- uint8_t *re_bytecode;- int ret;- uint8_t **capture, *str_buf;- int capture_count, shift, re_flags;- int next_src_pos, start, end;- int64_t last_index;- StringBuffer b_s, *b = &b_s;-- if (!re)- return JS_EXCEPTION;-- string_buffer_init(ctx, b, 0);-- capture = NULL;- str_val = JS_ToString(ctx, arg);- if (JS_IsException(str_val))- goto fail;- str = JS_VALUE_GET_STRING(str_val);- re_bytecode = re->bytecode->u.str8;- re_flags = lre_get_flags(re_bytecode);- if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) {- last_index = 0;- } else {- val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex);- if (JS_IsException(val) || JS_ToLengthFree(ctx, &last_index, val))- goto fail;- }- capture_count = lre_get_capture_count(re_bytecode);- if (capture_count > 0) {- capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2);- if (!capture)- goto fail;- }- shift = str->is_wide_char;- str_buf = str->u.str8;- next_src_pos = 0;- for (;;) {- if (last_index > str->len)- break;-- ret = lre_exec(capture, re_bytecode,- str_buf, last_index, str->len, shift, ctx);- if (ret != 1) {- if (ret >= 0) {- if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) {- if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,- JS_NewInt32(ctx, 0)) < 0)- goto fail;- }- } else {- JS_ThrowInternalError(ctx, "out of memory in regexp execution");- goto fail;- }- break;- }- start = (capture[0] - str_buf) >> shift;- end = (capture[1] - str_buf) >> shift;- last_index = end;- if (next_src_pos < start) {- if (string_buffer_concat(b, str, next_src_pos, start))- goto fail;- }- next_src_pos = end;- if (!(re_flags & LRE_FLAG_GLOBAL)) {- if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,- JS_NewInt32(ctx, end)) < 0)- goto fail;- break;- }- if (end == start) {- if (!(re_flags & LRE_FLAG_UTF16) || (unsigned)end >= str->len || !str->is_wide_char) {- end++;- } else {- string_getc(str, &end);- }- }- last_index = end;- }- if (string_buffer_concat(b, str, next_src_pos, str->len))- goto fail;- JS_FreeValue(ctx, str_val);- js_free(ctx, capture);- return string_buffer_end(b);-fail:- JS_FreeValue(ctx, str_val);- js_free(ctx, capture);- string_buffer_free(b);- return JS_EXCEPTION;-}--static JSValue JS_RegExpExec(JSContext *ctx, JSValueConst r, JSValueConst s)-{- JSValue method, ret;-- method = JS_GetProperty(ctx, r, JS_ATOM_exec);- if (JS_IsException(method))- return method;- if (JS_IsFunction(ctx, method)) {- ret = JS_CallFree(ctx, method, r, 1, &s);- if (JS_IsException(ret))- return ret;- if (!JS_IsObject(ret) && !JS_IsNull(ret)) {- JS_FreeValue(ctx, ret);- return JS_ThrowTypeError(ctx, "RegExp exec method must return an object or null");- }- return ret;- }- JS_FreeValue(ctx, method);- return js_regexp_exec(ctx, r, 1, &s);-}--#if 0-static JSValue js_regexp___RegExpExec(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return JS_RegExpExec(ctx, argv[0], argv[1]);-}-static JSValue js_regexp___RegExpDelete(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return JS_RegExpDelete(ctx, argv[0], argv[1]);-}-#endif--static JSValue js_regexp_test(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue val;- BOOL ret;-- val = JS_RegExpExec(ctx, this_val, argv[0]);- if (JS_IsException(val))- return JS_EXCEPTION;- ret = !JS_IsNull(val);- JS_FreeValue(ctx, val);- return JS_NewBool(ctx, ret);-}--static JSValue js_regexp_Symbol_match(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- // [Symbol.match](str)- JSValueConst rx = this_val;- JSValue A, S, result, matchStr;- int global, n, fullUnicode, isEmpty;- JSString *p;-- if (!JS_IsObject(rx))- return JS_ThrowTypeErrorNotAnObject(ctx);-- A = JS_UNDEFINED;- result = JS_UNDEFINED;- matchStr = JS_UNDEFINED;- S = JS_ToString(ctx, argv[0]);- if (JS_IsException(S))- goto exception;-- global = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_global));- if (global < 0)- goto exception;-- if (!global) {- A = JS_RegExpExec(ctx, rx, S);- } else {- fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode));- if (fullUnicode < 0)- goto exception;-- if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0)- goto exception;- A = JS_NewArray(ctx);- if (JS_IsException(A))- goto exception;- n = 0;- for(;;) {- JS_FreeValue(ctx, result);- result = JS_RegExpExec(ctx, rx, S);- if (JS_IsException(result))- goto exception;- if (JS_IsNull(result))- break;- matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));- if (JS_IsException(matchStr))- goto exception;- isEmpty = JS_IsEmptyString(matchStr);- if (JS_SetPropertyInt64(ctx, A, n++, matchStr) < 0)- goto exception;- if (isEmpty) {- int64_t thisIndex, nextIndex;- if (JS_ToLengthFree(ctx, &thisIndex,- JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0)- goto exception;- p = JS_VALUE_GET_STRING(S);- nextIndex = string_advance_index(p, thisIndex, fullUnicode);- if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0)- goto exception;- }- }- if (n == 0) {- JS_FreeValue(ctx, A);- A = JS_NULL;- }- }- JS_FreeValue(ctx, result);- JS_FreeValue(ctx, S);- return A;--exception:- JS_FreeValue(ctx, A);- JS_FreeValue(ctx, result);- JS_FreeValue(ctx, S);- return JS_EXCEPTION;-}--typedef struct JSRegExpStringIteratorData {- JSValue iterating_regexp;- JSValue iterated_string;- BOOL global;- BOOL unicode;- BOOL done;-} JSRegExpStringIteratorData;--static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val)-{- JSObject *p = JS_VALUE_GET_OBJ(val);- JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data;- if (it) {- JS_FreeValueRT(rt, it->iterating_regexp);- JS_FreeValueRT(rt, it->iterated_string);- js_free_rt(rt, it);- }-}--static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func)-{- JSObject *p = JS_VALUE_GET_OBJ(val);- JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data;- if (it) {- JS_MarkValue(rt, it->iterating_regexp, mark_func);- JS_MarkValue(rt, it->iterated_string, mark_func);- }-}--static JSValue js_regexp_string_iterator_next(JSContext *ctx,- JSValueConst this_val,- int argc, JSValueConst *argv,- BOOL *pdone, int magic)-{- JSRegExpStringIteratorData *it;- JSValueConst R, S;- JSValue matchStr = JS_UNDEFINED, match = JS_UNDEFINED;- JSString *sp;-- it = JS_GetOpaque2(ctx, this_val, JS_CLASS_REGEXP_STRING_ITERATOR);- if (!it)- goto exception;- if (it->done) {- *pdone = TRUE;- return JS_UNDEFINED;- }- R = it->iterating_regexp;- S = it->iterated_string;- match = JS_RegExpExec(ctx, R, S);- if (JS_IsException(match))- goto exception;- if (JS_IsNull(match)) {- it->done = TRUE;- *pdone = TRUE;- return JS_UNDEFINED;- } else if (it->global) {- matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, match, 0));- if (JS_IsException(matchStr))- goto exception;- if (JS_IsEmptyString(matchStr)) {- int64_t thisIndex, nextIndex;- if (JS_ToLengthFree(ctx, &thisIndex,- JS_GetProperty(ctx, R, JS_ATOM_lastIndex)) < 0)- goto exception;- sp = JS_VALUE_GET_STRING(S);- nextIndex = string_advance_index(sp, thisIndex, it->unicode);- if (JS_SetProperty(ctx, R, JS_ATOM_lastIndex,- JS_NewInt64(ctx, nextIndex)) < 0)- goto exception;- }- JS_FreeValue(ctx, matchStr);- } else {- it->done = TRUE;- }- *pdone = FALSE;- return match;- exception:- JS_FreeValue(ctx, match);- JS_FreeValue(ctx, matchStr);- *pdone = FALSE;- return JS_EXCEPTION;-}--static JSValue js_regexp_Symbol_matchAll(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- // [Symbol.matchAll](str)- JSValueConst R = this_val;- JSValue S, C, flags, matcher, iter;- JSValueConst args[2];- JSString *strp;- int64_t lastIndex;- JSRegExpStringIteratorData *it;- - if (!JS_IsObject(R))- return JS_ThrowTypeErrorNotAnObject(ctx);-- C = JS_UNDEFINED;- flags = JS_UNDEFINED;- matcher = JS_UNDEFINED;- iter = JS_UNDEFINED;- - S = JS_ToString(ctx, argv[0]);- if (JS_IsException(S))- goto exception;- C = JS_SpeciesConstructor(ctx, R, ctx->regexp_ctor);- if (JS_IsException(C))- goto exception;- flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, R, JS_ATOM_flags));- if (JS_IsException(flags))- goto exception;- args[0] = R;- args[1] = flags;- matcher = JS_CallConstructor(ctx, C, 2, args);- if (JS_IsException(matcher))- goto exception;- if (JS_ToLengthFree(ctx, &lastIndex,- JS_GetProperty(ctx, R, JS_ATOM_lastIndex)))- goto exception;- if (JS_SetProperty(ctx, matcher, JS_ATOM_lastIndex,- JS_NewInt64(ctx, lastIndex)) < 0)- goto exception;- - iter = JS_NewObjectClass(ctx, JS_CLASS_REGEXP_STRING_ITERATOR);- if (JS_IsException(iter))- goto exception;- it = js_malloc(ctx, sizeof(*it));- if (!it)- goto exception;- it->iterating_regexp = matcher;- it->iterated_string = S;- strp = JS_VALUE_GET_STRING(flags);- it->global = string_indexof_char(strp, 'g', 0) >= 0;- it->unicode = string_indexof_char(strp, 'u', 0) >= 0;- it->done = FALSE;- JS_SetOpaque(iter, it);-- JS_FreeValue(ctx, C);- JS_FreeValue(ctx, flags);- return iter;- exception:- JS_FreeValue(ctx, S);- JS_FreeValue(ctx, C);- JS_FreeValue(ctx, flags);- JS_FreeValue(ctx, matcher);- JS_FreeValue(ctx, iter);- return JS_EXCEPTION;-}--typedef struct ValueBuffer {- JSContext *ctx;- JSValue *arr;- JSValue def[4];- int len;- int size;- int error_status;-} ValueBuffer;--static int value_buffer_init(JSContext *ctx, ValueBuffer *b)-{- b->ctx = ctx;- b->len = 0;- b->size = 4;- b->error_status = 0;- b->arr = b->def;- return 0;-}--static void value_buffer_free(ValueBuffer *b)-{- while (b->len > 0)- JS_FreeValue(b->ctx, b->arr[--b->len]);- if (b->arr != b->def)- js_free(b->ctx, b->arr);- b->arr = b->def;- b->size = 4;-}--static int value_buffer_append(ValueBuffer *b, JSValue val)-{- if (b->error_status)- return -1;-- if (b->len >= b->size) {- int new_size = (b->len + (b->len >> 1) + 31) & ~16;- size_t slack;- JSValue *new_arr;-- if (b->arr == b->def) {- new_arr = js_realloc2(b->ctx, NULL, sizeof(*b->arr) * new_size, &slack);- if (new_arr)- memcpy(new_arr, b->def, sizeof b->def);- } else {- new_arr = js_realloc2(b->ctx, b->arr, sizeof(*b->arr) * new_size, &slack);- }- if (!new_arr) {- value_buffer_free(b);- JS_FreeValue(b->ctx, val);- b->error_status = -1;- return -1;- }- new_size += slack / sizeof(*new_arr);- b->arr = new_arr;- b->size = new_size;- }- b->arr[b->len++] = val;- return 0;-}--static int js_is_standard_regexp(JSContext *ctx, JSValueConst rx)-{- JSValue val;- int res;-- val = JS_GetProperty(ctx, rx, JS_ATOM_constructor);- if (JS_IsException(val))- return -1;- // rx.constructor === RegExp- res = js_same_value(ctx, val, ctx->regexp_ctor);- JS_FreeValue(ctx, val);- if (res) {- val = JS_GetProperty(ctx, rx, JS_ATOM_exec);- if (JS_IsException(val))- return -1;- // rx.exec === RE_exec- res = JS_IsCFunction(ctx, val, js_regexp_exec, 0);- JS_FreeValue(ctx, val);- }- return res;-}--static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- // [Symbol.replace](str, rep)- JSValueConst rx = this_val, rep = argv[1];- JSValueConst args[6];- JSValue str, rep_val, matched, tab, rep_str, namedCaptures, res;- JSString *sp, *rp;- StringBuffer b_s, *b = &b_s;- ValueBuffer v_b, *results = &v_b;- int nextSourcePosition, n, j, functionalReplace, is_global, fullUnicode;- uint32_t nCaptures;- int64_t position;-- if (!JS_IsObject(rx))- return JS_ThrowTypeErrorNotAnObject(ctx);-- string_buffer_init(ctx, b, 0);- value_buffer_init(ctx, results);-- rep_val = JS_UNDEFINED;- matched = JS_UNDEFINED;- tab = JS_UNDEFINED;- rep_str = JS_UNDEFINED;- namedCaptures = JS_UNDEFINED;-- str = JS_ToString(ctx, argv[0]);- if (JS_IsException(str))- goto exception;- - sp = JS_VALUE_GET_STRING(str);- rp = NULL;- functionalReplace = JS_IsFunction(ctx, rep);- if (!functionalReplace) {- rep_val = JS_ToString(ctx, rep);- if (JS_IsException(rep_val))- goto exception;- rp = JS_VALUE_GET_STRING(rep_val);- }- fullUnicode = 0;- is_global = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_global));- if (is_global < 0)- goto exception;- if (is_global) {- fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode));- if (fullUnicode < 0)- goto exception;- if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0)- goto exception;- }-- if (rp && rp->len == 0 && is_global && js_is_standard_regexp(ctx, rx)) {- /* use faster version for simple cases */- res = JS_RegExpDelete(ctx, rx, str);- goto done;- }- for(;;) {- JSValue result;- result = JS_RegExpExec(ctx, rx, str);- if (JS_IsException(result))- goto exception;- if (JS_IsNull(result))- break;- if (value_buffer_append(results, result) < 0)- goto exception;- if (!is_global)- break;- JS_FreeValue(ctx, matched);- matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));- if (JS_IsException(matched))- goto exception;- if (JS_IsEmptyString(matched)) {- /* always advance of at least one char */- int64_t thisIndex, nextIndex;- if (JS_ToLengthFree(ctx, &thisIndex, JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0)- goto exception;- nextIndex = string_advance_index(sp, thisIndex, fullUnicode);- if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0)- goto exception;- }- }- nextSourcePosition = 0;- for(j = 0; j < results->len; j++) {- JSValueConst result;- result = results->arr[j];- if (js_get_length32(ctx, &nCaptures, result) < 0)- goto exception;- JS_FreeValue(ctx, matched);- matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));- if (JS_IsException(matched))- goto exception;- if (JS_ToLengthFree(ctx, &position, JS_GetProperty(ctx, result, JS_ATOM_index)))- goto exception;- if (position > sp->len)- position = sp->len;- else if (position < 0)- position = 0;- /* ignore substition if going backward (can happen- with custom regexp object) */- JS_FreeValue(ctx, tab);- tab = JS_NewArray(ctx);- if (JS_IsException(tab))- goto exception;- if (JS_SetPropertyInt64(ctx, tab, 0, JS_DupValue(ctx, matched)) < 0)- goto exception;- for(n = 1; n < nCaptures; n++) {- JSValue capN;- capN = JS_GetPropertyInt64(ctx, result, n);- if (JS_IsException(capN))- goto exception;- if (!JS_IsUndefined(capN)) {- capN = JS_ToStringFree(ctx, capN);- if (JS_IsException(capN))- goto exception;- }- if (JS_SetPropertyInt64(ctx, tab, n, capN) < 0)- goto exception;- }- JS_FreeValue(ctx, namedCaptures);- namedCaptures = JS_GetProperty(ctx, result, JS_ATOM_groups);- if (JS_IsException(namedCaptures))- goto exception;- if (functionalReplace) {- if (JS_SetPropertyInt64(ctx, tab, n++, JS_NewInt32(ctx, position)) < 0)- goto exception;- if (JS_SetPropertyInt64(ctx, tab, n++, JS_DupValue(ctx, str)) < 0)- goto exception;- if (!JS_IsUndefined(namedCaptures)) {- if (JS_SetPropertyInt64(ctx, tab, n++, JS_DupValue(ctx, namedCaptures)) < 0)- goto exception;- }- args[0] = JS_UNDEFINED;- args[1] = tab;- JS_FreeValue(ctx, rep_str);- rep_str = JS_ToStringFree(ctx, js_function_apply(ctx, rep, 2, args, 0));- } else {- JSValue namedCaptures1;- if (!JS_IsUndefined(namedCaptures)) {- namedCaptures1 = JS_ToObject(ctx, namedCaptures);- if (JS_IsException(namedCaptures1))- goto exception;- } else {- namedCaptures1 = JS_UNDEFINED;- }- args[0] = matched;- args[1] = str;- args[2] = JS_NewInt32(ctx, position);- args[3] = tab;- args[4] = namedCaptures1;- args[5] = rep_val;- JS_FreeValue(ctx, rep_str);- rep_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args);- JS_FreeValue(ctx, namedCaptures1);- }- if (JS_IsException(rep_str))- goto exception;- if (position >= nextSourcePosition) {- string_buffer_concat(b, sp, nextSourcePosition, position);- string_buffer_concat_value(b, rep_str);- nextSourcePosition = position + JS_VALUE_GET_STRING(matched)->len;- }- }- string_buffer_concat(b, sp, nextSourcePosition, sp->len);- res = string_buffer_end(b);- goto done1;--exception:- res = JS_EXCEPTION;-done:- string_buffer_free(b);-done1:- value_buffer_free(results);- JS_FreeValue(ctx, rep_val);- JS_FreeValue(ctx, matched);- JS_FreeValue(ctx, tab);- JS_FreeValue(ctx, rep_str);- JS_FreeValue(ctx, namedCaptures);- JS_FreeValue(ctx, str);- return res;-}--static JSValue js_regexp_Symbol_search(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValueConst rx = this_val;- JSValue str, previousLastIndex, currentLastIndex, result, index;-- if (!JS_IsObject(rx))- return JS_ThrowTypeErrorNotAnObject(ctx);-- result = JS_UNDEFINED;- currentLastIndex = JS_UNDEFINED;- previousLastIndex = JS_UNDEFINED;- str = JS_ToString(ctx, argv[0]);- if (JS_IsException(str))- goto exception;-- previousLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex);- if (JS_IsException(previousLastIndex))- goto exception;-- if (!js_same_value(ctx, previousLastIndex, JS_NewInt32(ctx, 0))) {- if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) {- goto exception;- }- }- result = JS_RegExpExec(ctx, rx, str);- if (JS_IsException(result))- goto exception;- currentLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex);- if (JS_IsException(currentLastIndex))- goto exception;- if (js_same_value(ctx, currentLastIndex, previousLastIndex)) {- JS_FreeValue(ctx, previousLastIndex);- } else {- if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, previousLastIndex) < 0)- goto exception;- }- JS_FreeValue(ctx, str);- JS_FreeValue(ctx, currentLastIndex);-- if (JS_IsNull(result)) {- return JS_NewInt32(ctx, -1);- } else {- index = JS_GetProperty(ctx, result, JS_ATOM_index);- JS_FreeValue(ctx, result);- return index;- }--exception:- JS_FreeValue(ctx, result);- JS_FreeValue(ctx, str);- JS_FreeValue(ctx, currentLastIndex);- JS_FreeValue(ctx, previousLastIndex);- return JS_EXCEPTION;-}--static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- // [Symbol.split](str, limit)- JSValueConst rx = this_val;- JSValueConst args[2];- JSValue str, ctor, splitter, A, flags, z, sub;- JSString *strp;- uint32_t lim, size, p, q;- int unicodeMatching;- int64_t lengthA, e, numberOfCaptures, i;-- if (!JS_IsObject(rx))- return JS_ThrowTypeErrorNotAnObject(ctx);-- ctor = JS_UNDEFINED;- splitter = JS_UNDEFINED;- A = JS_UNDEFINED;- flags = JS_UNDEFINED;- z = JS_UNDEFINED;- str = JS_ToString(ctx, argv[0]);- if (JS_IsException(str))- goto exception;- ctor = JS_SpeciesConstructor(ctx, rx, ctx->regexp_ctor);- if (JS_IsException(ctor))- goto exception;- flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_flags));- if (JS_IsException(flags))- goto exception;- strp = JS_VALUE_GET_STRING(flags);- unicodeMatching = string_indexof_char(strp, 'u', 0) >= 0;- if (string_indexof_char(strp, 'y', 0) < 0) {- flags = JS_ConcatString3(ctx, "", flags, "y");- if (JS_IsException(flags))- goto exception;- }- args[0] = rx;- args[1] = flags;- splitter = JS_CallConstructor(ctx, ctor, 2, args);- if (JS_IsException(splitter))- goto exception;- A = JS_NewArray(ctx);- if (JS_IsException(A))- goto exception;- lengthA = 0;- if (JS_IsUndefined(argv[1])) {- lim = 0xffffffff;- } else {- if (JS_ToUint32(ctx, &lim, argv[1]) < 0)- goto exception;- if (lim == 0)- goto done;- }- strp = JS_VALUE_GET_STRING(str);- p = q = 0;- size = strp->len;- if (size == 0) {- z = JS_RegExpExec(ctx, splitter, str);- if (JS_IsException(z))- goto exception;- if (JS_IsNull(z))- goto add_tail;- goto done;- }- while (q < size) {- if (JS_SetProperty(ctx, splitter, JS_ATOM_lastIndex, JS_NewInt32(ctx, q)) < 0)- goto exception;- JS_FreeValue(ctx, z); - z = JS_RegExpExec(ctx, splitter, str);- if (JS_IsException(z))- goto exception;- if (JS_IsNull(z)) {- q = string_advance_index(strp, q, unicodeMatching);- } else {- if (JS_ToLengthFree(ctx, &e, JS_GetProperty(ctx, splitter, JS_ATOM_lastIndex)))- goto exception;- if (e > size)- e = size;- if (e == p) {- q = string_advance_index(strp, q, unicodeMatching);- } else {- sub = js_sub_string(ctx, strp, p, q);- if (JS_IsException(sub))- goto exception;- if (JS_SetPropertyInt64(ctx, A, lengthA++, sub) < 0)- goto exception;- if (lengthA == lim)- goto done;- p = e;- if (js_get_length64(ctx, &numberOfCaptures, z))- goto exception;- for(i = 1; i < numberOfCaptures; i++) {- sub = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, z, i));- if (JS_IsException(sub))- goto exception;- if (JS_SetPropertyInt64(ctx, A, lengthA++, sub) < 0)- goto exception;- if (lengthA == lim)- goto done;- }- q = p;- }- }- }-add_tail:- if (p > size)- p = size;- sub = js_sub_string(ctx, strp, p, size);- if (JS_IsException(sub))- goto exception;- if (JS_SetPropertyInt64(ctx, A, lengthA++, sub) < 0)- goto exception;- goto done;-exception:- JS_FreeValue(ctx, A);- A = JS_EXCEPTION;-done:- JS_FreeValue(ctx, str);- JS_FreeValue(ctx, ctor);- JS_FreeValue(ctx, splitter);- JS_FreeValue(ctx, flags);- JS_FreeValue(ctx, z); - return A;-}--static const JSCFunctionListEntry js_regexp_funcs[] = {- JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),- //JS_CFUNC_DEF("__RegExpExec", 2, js_regexp___RegExpExec ),- //JS_CFUNC_DEF("__RegExpDelete", 2, js_regexp___RegExpDelete ),-};--static const JSCFunctionListEntry js_regexp_proto_funcs[] = {- JS_CGETSET_DEF("flags", js_regexp_get_flags, NULL ),- JS_CGETSET_DEF("source", js_regexp_get_source, NULL ),- JS_CGETSET_MAGIC_DEF("global", js_regexp_get_flag, NULL, 1 ),- JS_CGETSET_MAGIC_DEF("ignoreCase", js_regexp_get_flag, NULL, 2 ),- JS_CGETSET_MAGIC_DEF("multiline", js_regexp_get_flag, NULL, 4 ),- JS_CGETSET_MAGIC_DEF("dotAll", js_regexp_get_flag, NULL, 8 ),- JS_CGETSET_MAGIC_DEF("unicode", js_regexp_get_flag, NULL, 16 ),- JS_CGETSET_MAGIC_DEF("sticky", js_regexp_get_flag, NULL, 32 ),- JS_CFUNC_DEF("exec", 1, js_regexp_exec ),- JS_CFUNC_DEF("compile", 2, js_regexp_compile ),- JS_CFUNC_DEF("test", 1, js_regexp_test ),- JS_CFUNC_DEF("toString", 0, js_regexp_toString ),- JS_CFUNC_DEF("[Symbol.replace]", 2, js_regexp_Symbol_replace ),- JS_CFUNC_DEF("[Symbol.match]", 1, js_regexp_Symbol_match ),- JS_CFUNC_DEF("[Symbol.matchAll]", 1, js_regexp_Symbol_matchAll ),- JS_CFUNC_DEF("[Symbol.search]", 1, js_regexp_Symbol_search ),- JS_CFUNC_DEF("[Symbol.split]", 2, js_regexp_Symbol_split ),- //JS_CGETSET_DEF("__source", js_regexp_get___source, NULL ),- //JS_CGETSET_DEF("__flags", js_regexp_get___flags, NULL ),-};--static const JSCFunctionListEntry js_regexp_string_iterator_proto_funcs[] = {- JS_ITERATOR_NEXT_DEF("next", 0, js_regexp_string_iterator_next, 0 ),- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "RegExp String Iterator", JS_PROP_CONFIGURABLE ),-};--void JS_AddIntrinsicRegExpCompiler(JSContext *ctx)-{- ctx->compile_regexp = js_compile_regexp;-}--void JS_AddIntrinsicRegExp(JSContext *ctx)-{- JSValueConst obj;-- JS_AddIntrinsicRegExpCompiler(ctx);-- ctx->class_proto[JS_CLASS_REGEXP] = JS_NewObject(ctx);- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP], js_regexp_proto_funcs,- countof(js_regexp_proto_funcs));- obj = JS_NewGlobalCConstructor(ctx, "RegExp", js_regexp_constructor, 2,- ctx->class_proto[JS_CLASS_REGEXP]);- ctx->regexp_ctor = JS_DupValue(ctx, obj);- JS_SetPropertyFunctionList(ctx, obj, js_regexp_funcs, countof(js_regexp_funcs));-- ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR] =- JS_NewObjectProto(ctx, ctx->iterator_proto);- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR],- js_regexp_string_iterator_proto_funcs,- countof(js_regexp_string_iterator_proto_funcs));-}--/* JSON */--static int json_parse_expect(JSParseState *s, int tok)-{- if (s->token.val != tok) {- /* XXX: dump token correctly in all cases */- return js_parse_error(s, "expecting '%c'", tok);- }- return json_next_token(s);-}--static JSValue json_parse_value(JSParseState *s)-{- JSContext *ctx = s->ctx;- JSValue val = JS_NULL;- int ret;-- switch(s->token.val) {- case '{':- {- JSValue prop_val;- JSAtom prop_name;- - if (json_next_token(s))- goto fail;- val = JS_NewObject(ctx);- if (JS_IsException(val))- goto fail;- if (s->token.val != '}') {- for(;;) {- if (s->token.val == TOK_STRING) {- prop_name = JS_ValueToAtom(ctx, s->token.u.str.str);- if (prop_name == JS_ATOM_NULL)- goto fail;- } else if (s->ext_json && s->token.val == TOK_IDENT) {- prop_name = JS_DupAtom(ctx, s->token.u.ident.atom);- } else {- js_parse_error(s, "expecting property name");- goto fail;- }- if (json_next_token(s))- goto fail1;- if (json_parse_expect(s, ':'))- goto fail1;- prop_val = json_parse_value(s);- if (JS_IsException(prop_val)) {- fail1:- JS_FreeAtom(ctx, prop_name);- goto fail;- }- ret = JS_DefinePropertyValue(ctx, val, prop_name,- prop_val, JS_PROP_C_W_E);- JS_FreeAtom(ctx, prop_name);- if (ret < 0)- goto fail;-- if (s->token.val != ',')- break;- if (json_next_token(s))- goto fail;- if (s->ext_json && s->token.val == '}')- break;- }- }- if (json_parse_expect(s, '}'))- goto fail;- }- break;- case '[':- {- JSValue el;- uint32_t idx;-- if (json_next_token(s))- goto fail;- val = JS_NewArray(ctx);- if (JS_IsException(val))- goto fail;- if (s->token.val != ']') {- idx = 0;- for(;;) {- el = json_parse_value(s);- if (JS_IsException(el))- goto fail;- ret = JS_DefinePropertyValueUint32(ctx, val, idx, el, JS_PROP_C_W_E);- if (ret < 0)- goto fail;- if (s->token.val != ',')- break;- if (json_next_token(s))- goto fail;- idx++;- if (s->ext_json && s->token.val == ']')- break;- }- }- if (json_parse_expect(s, ']'))- goto fail;- }- break;- case TOK_STRING:- val = JS_DupValue(ctx, s->token.u.str.str);- if (json_next_token(s))- goto fail;- break;- case TOK_NUMBER:- val = s->token.u.num.val;- if (json_next_token(s))- goto fail;- break;- case TOK_IDENT:- if (s->token.u.ident.atom == JS_ATOM_false ||- s->token.u.ident.atom == JS_ATOM_true) {- val = JS_NewBool(ctx, (s->token.u.ident.atom == JS_ATOM_true));- } else if (s->token.u.ident.atom == JS_ATOM_null) {- val = JS_NULL;- } else {- goto def_token;- }- if (json_next_token(s))- goto fail;- break;- default:- def_token:- if (s->token.val == TOK_EOF) {- js_parse_error(s, "unexpected end of input");- } else {- js_parse_error(s, "unexpected token: '%.*s'",- (int)(s->buf_ptr - s->token.ptr), s->token.ptr);- }- goto fail;- }- return val;- fail:- JS_FreeValue(ctx, val);- return JS_EXCEPTION;-}--JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len,- const char *filename, int flags)-{- JSParseState s1, *s = &s1;- JSValue val = JS_UNDEFINED;-- js_parse_init(ctx, s, buf, buf_len, filename);- s->ext_json = ((flags & JS_PARSE_JSON_EXT) != 0);- if (json_next_token(s))- goto fail;- val = json_parse_value(s);- if (JS_IsException(val))- goto fail;- if (s->token.val != TOK_EOF) {- if (js_parse_error(s, "unexpected data at the end"))- goto fail;- }- return val;- fail:- JS_FreeValue(ctx, val);- free_token(s, &s->token);- return JS_EXCEPTION;-}--JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len,- const char *filename)-{- return JS_ParseJSON2(ctx, buf, buf_len, filename, 0); -}--static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder,- JSAtom name, JSValueConst reviver)-{- JSValue val, new_el, name_val, res;- JSValueConst args[2];- int ret, is_array;- uint32_t i, len = 0;- JSAtom prop;- JSPropertyEnum *atoms = NULL;-- if (js_check_stack_overflow(ctx->rt, 0)) {- return JS_ThrowStackOverflow(ctx);- }-- val = JS_GetProperty(ctx, holder, name);- if (JS_IsException(val))- return val;- if (JS_IsObject(val)) {- is_array = JS_IsArray(ctx, val);- if (is_array < 0)- goto fail;- if (is_array) {- if (js_get_length32(ctx, &len, val))- goto fail;- } else {- ret = JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, JS_VALUE_GET_OBJ(val), JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK);- if (ret < 0)- goto fail;- }- for(i = 0; i < len; i++) {- if (is_array) {- prop = JS_NewAtomUInt32(ctx, i);- if (prop == JS_ATOM_NULL)- goto fail;- } else {- prop = JS_DupAtom(ctx, atoms[i].atom);- }- new_el = internalize_json_property(ctx, val, prop, reviver);- if (JS_IsException(new_el)) {- JS_FreeAtom(ctx, prop);- goto fail;- }- if (JS_IsUndefined(new_el)) {- ret = JS_DeleteProperty(ctx, val, prop, 0);- } else {- ret = JS_DefinePropertyValue(ctx, val, prop, new_el, JS_PROP_C_W_E);- }- JS_FreeAtom(ctx, prop);- if (ret < 0)- goto fail;- }- }- js_free_prop_enum(ctx, atoms, len);- atoms = NULL;- name_val = JS_AtomToValue(ctx, name);- if (JS_IsException(name_val))- goto fail;- args[0] = name_val;- args[1] = val;- res = JS_Call(ctx, reviver, holder, 2, args);- JS_FreeValue(ctx, name_val);- JS_FreeValue(ctx, val);- return res;- fail:- js_free_prop_enum(ctx, atoms, len);- JS_FreeValue(ctx, val);- return JS_EXCEPTION;-}--static JSValue js_json_parse(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue obj, root;- JSValueConst reviver;- const char *str;- size_t len;-- str = JS_ToCStringLen(ctx, &len, argv[0]);- if (!str)- return JS_EXCEPTION;- obj = JS_ParseJSON(ctx, str, len, "<input>");- JS_FreeCString(ctx, str);- if (JS_IsException(obj))- return obj;- if (argc > 1 && JS_IsFunction(ctx, argv[1])) {- reviver = argv[1];- root = JS_NewObject(ctx);- if (JS_IsException(root)) {- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;- }- if (JS_DefinePropertyValue(ctx, root, JS_ATOM_empty_string, obj,- JS_PROP_C_W_E) < 0) {- JS_FreeValue(ctx, root);- return JS_EXCEPTION;- }- obj = internalize_json_property(ctx, root, JS_ATOM_empty_string,- reviver);- JS_FreeValue(ctx, root);- }- return obj;-}--typedef struct JSONStringifyContext {- JSValueConst replacer_func;- JSValue stack;- JSValue property_list;- JSValue gap;- JSValue empty;- StringBuffer *b;-} JSONStringifyContext;--static JSValue JS_ToQuotedStringFree(JSContext *ctx, JSValue val) {- JSValue r = JS_ToQuotedString(ctx, val);- JS_FreeValue(ctx, val);- return r;-}--static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc,- JSValueConst holder, JSValue val, JSValueConst key)-{- JSValue v;- JSValueConst args[2];-- if (JS_IsObject(val)-#ifdef CONFIG_BIGNUM- || JS_IsBigInt(ctx, val) /* XXX: probably useless */-#endif- ) {- JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON);- if (JS_IsException(f))- goto exception;- if (JS_IsFunction(ctx, f)) {- v = JS_CallFree(ctx, f, val, 1, &key);- JS_FreeValue(ctx, val);- val = v;- if (JS_IsException(val))- goto exception;- } else {- JS_FreeValue(ctx, f);- }- }-- if (!JS_IsUndefined(jsc->replacer_func)) {- args[0] = key;- args[1] = val;- v = JS_Call(ctx, jsc->replacer_func, holder, 2, args);- JS_FreeValue(ctx, val);- val = v;- if (JS_IsException(val))- goto exception;- }-- switch (JS_VALUE_GET_NORM_TAG(val)) {- case JS_TAG_OBJECT:- if (JS_IsFunction(ctx, val))- break;- case JS_TAG_STRING:- case JS_TAG_INT:- case JS_TAG_FLOAT64:-#ifdef CONFIG_BIGNUM- case JS_TAG_BIG_FLOAT:-#endif- case JS_TAG_BOOL:- case JS_TAG_NULL:-#ifdef CONFIG_BIGNUM- case JS_TAG_BIG_INT:-#endif- case JS_TAG_EXCEPTION:- return val;- default:- break;- }- JS_FreeValue(ctx, val);- return JS_UNDEFINED;--exception:- JS_FreeValue(ctx, val);- return JS_EXCEPTION;-}--static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,- JSValueConst holder, JSValue val,- JSValueConst indent)-{- JSValue indent1, sep, sep1, tab, v, prop;- JSObject *p;- int64_t i, len;- int cl, ret;- BOOL has_content;- - indent1 = JS_UNDEFINED;- sep = JS_UNDEFINED;- sep1 = JS_UNDEFINED;- tab = JS_UNDEFINED;- prop = JS_UNDEFINED;-- switch (JS_VALUE_GET_NORM_TAG(val)) {- case JS_TAG_OBJECT:- p = JS_VALUE_GET_OBJ(val);- cl = p->class_id;- if (cl == JS_CLASS_STRING) {- val = JS_ToStringFree(ctx, val);- if (JS_IsException(val))- goto exception;- val = JS_ToQuotedStringFree(ctx, val);- if (JS_IsException(val))- goto exception;- return string_buffer_concat_value_free(jsc->b, val);- } else if (cl == JS_CLASS_NUMBER) {- val = JS_ToNumberFree(ctx, val);- if (JS_IsException(val))- goto exception;- return string_buffer_concat_value_free(jsc->b, val);- } else if (cl == JS_CLASS_BOOLEAN) {- ret = string_buffer_concat_value(jsc->b, p->u.object_data);- JS_FreeValue(ctx, val);- return ret;- }-#ifdef CONFIG_BIGNUM- else if (cl == JS_CLASS_BIG_FLOAT) {- return string_buffer_concat_value_free(jsc->b, val);- } else if (cl == JS_CLASS_BIG_INT) {- JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify");- goto exception;- }-#endif- v = js_array_includes(ctx, jsc->stack, 1, (JSValueConst *)&val);- if (JS_IsException(v))- goto exception;- if (JS_ToBoolFree(ctx, v)) {- JS_ThrowTypeError(ctx, "circular reference");- goto exception;- }- indent1 = JS_ConcatString(ctx, JS_DupValue(ctx, indent), JS_DupValue(ctx, jsc->gap));- if (JS_IsException(indent1))- goto exception;- if (!JS_IsEmptyString(jsc->gap)) {- sep = JS_ConcatString3(ctx, "\n", JS_DupValue(ctx, indent1), "");- if (JS_IsException(sep))- goto exception;- sep1 = JS_NewString(ctx, " ");- if (JS_IsException(sep1))- goto exception;- } else {- sep = JS_DupValue(ctx, jsc->empty);- sep1 = JS_DupValue(ctx, jsc->empty);- }- v = js_array_push(ctx, jsc->stack, 1, (JSValueConst *)&val, 0);- if (check_exception_free(ctx, v))- goto exception;- ret = JS_IsArray(ctx, val);- if (ret < 0)- goto exception;- if (ret) {- if (js_get_length64(ctx, &len, val))- goto exception;- string_buffer_putc8(jsc->b, '[');- for(i = 0; i < len; i++) {- if (i > 0)- string_buffer_putc8(jsc->b, ',');- string_buffer_concat_value(jsc->b, sep);- v = JS_GetPropertyInt64(ctx, val, i);- if (JS_IsException(v))- goto exception;- /* XXX: could do this string conversion only when needed */- prop = JS_ToStringFree(ctx, JS_NewInt64(ctx, i));- if (JS_IsException(prop))- goto exception;- v = js_json_check(ctx, jsc, val, v, prop);- JS_FreeValue(ctx, prop);- prop = JS_UNDEFINED;- if (JS_IsException(v))- goto exception;- if (JS_IsUndefined(v))- v = JS_NULL;- if (js_json_to_str(ctx, jsc, val, v, indent1))- goto exception;- }- if (len > 0 && !JS_IsEmptyString(jsc->gap)) {- string_buffer_putc8(jsc->b, '\n');- string_buffer_concat_value(jsc->b, indent);- }- string_buffer_putc8(jsc->b, ']');- } else {- if (!JS_IsUndefined(jsc->property_list))- tab = JS_DupValue(ctx, jsc->property_list);- else- tab = js_object_keys(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val, JS_ITERATOR_KIND_KEY);- if (JS_IsException(tab))- goto exception;- if (js_get_length64(ctx, &len, tab))- goto exception;- string_buffer_putc8(jsc->b, '{');- has_content = FALSE;- for(i = 0; i < len; i++) {- JS_FreeValue(ctx, prop);- prop = JS_GetPropertyInt64(ctx, tab, i);- if (JS_IsException(prop))- goto exception;- v = JS_GetPropertyValue(ctx, val, JS_DupValue(ctx, prop));- if (JS_IsException(v))- goto exception;- v = js_json_check(ctx, jsc, val, v, prop);- if (JS_IsException(v))- goto exception;- if (!JS_IsUndefined(v)) {- if (has_content)- string_buffer_putc8(jsc->b, ',');- prop = JS_ToQuotedStringFree(ctx, prop);- if (JS_IsException(prop)) {- JS_FreeValue(ctx, v);- goto exception;- }- string_buffer_concat_value(jsc->b, sep);- string_buffer_concat_value(jsc->b, prop);- string_buffer_putc8(jsc->b, ':');- string_buffer_concat_value(jsc->b, sep1);- if (js_json_to_str(ctx, jsc, val, v, indent1))- goto exception;- has_content = TRUE;- }- }- if (has_content && JS_VALUE_GET_STRING(jsc->gap)->len != 0) {- string_buffer_putc8(jsc->b, '\n');- string_buffer_concat_value(jsc->b, indent);- }- string_buffer_putc8(jsc->b, '}');- }- if (check_exception_free(ctx, js_array_pop(ctx, jsc->stack, 0, NULL, 0)))- goto exception;- JS_FreeValue(ctx, val);- JS_FreeValue(ctx, tab);- JS_FreeValue(ctx, sep);- JS_FreeValue(ctx, sep1);- JS_FreeValue(ctx, indent1);- JS_FreeValue(ctx, prop);- return 0;- case JS_TAG_STRING:- val = JS_ToQuotedStringFree(ctx, val);- if (JS_IsException(val))- goto exception;- goto concat_value;- case JS_TAG_FLOAT64:- if (!isfinite(JS_VALUE_GET_FLOAT64(val))) {- val = JS_NULL;- }- goto concat_value;- case JS_TAG_INT:-#ifdef CONFIG_BIGNUM- case JS_TAG_BIG_FLOAT:-#endif- case JS_TAG_BOOL:- case JS_TAG_NULL:- concat_value:- return string_buffer_concat_value_free(jsc->b, val);-#ifdef CONFIG_BIGNUM- case JS_TAG_BIG_INT:- JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify");- goto exception;-#endif- default:- JS_FreeValue(ctx, val);- return 0;- }- -exception:- JS_FreeValue(ctx, val);- JS_FreeValue(ctx, tab);- JS_FreeValue(ctx, sep);- JS_FreeValue(ctx, sep1);- JS_FreeValue(ctx, indent1);- JS_FreeValue(ctx, prop);- return -1;-}--JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj,- JSValueConst replacer, JSValueConst space0)-{- StringBuffer b_s;- JSONStringifyContext jsc_s, *jsc = &jsc_s;- JSValue val, v, space, ret, wrapper;- int res;- int64_t i, j, n;-- jsc->replacer_func = JS_UNDEFINED;- jsc->stack = JS_UNDEFINED;- jsc->property_list = JS_UNDEFINED;- jsc->gap = JS_UNDEFINED;- jsc->b = &b_s;- jsc->empty = JS_AtomToString(ctx, JS_ATOM_empty_string);- ret = JS_UNDEFINED;- wrapper = JS_UNDEFINED;-- string_buffer_init(ctx, jsc->b, 0);- jsc->stack = JS_NewArray(ctx);- if (JS_IsException(jsc->stack))- goto exception;- if (JS_IsFunction(ctx, replacer)) {- jsc->replacer_func = replacer;- } else {- res = JS_IsArray(ctx, replacer);- if (res < 0)- goto exception;- if (res) {- /* XXX: enumeration is not fully correct */- jsc->property_list = JS_NewArray(ctx);- if (JS_IsException(jsc->property_list))- goto exception;- if (js_get_length64(ctx, &n, replacer))- goto exception;- for (i = j = 0; i < n; i++) {- JSValue present;- v = JS_GetPropertyInt64(ctx, replacer, i);- if (JS_IsException(v))- goto exception;- if (JS_IsObject(v)) {- JSObject *p = JS_VALUE_GET_OBJ(v);- if (p->class_id == JS_CLASS_STRING ||- p->class_id == JS_CLASS_NUMBER) {- v = JS_ToStringFree(ctx, v);- if (JS_IsException(v))- goto exception;- } else {- JS_FreeValue(ctx, v);- continue;- }- } else if (JS_IsNumber(v)) {- v = JS_ToStringFree(ctx, v);- if (JS_IsException(v))- goto exception;- } else if (!JS_IsString(v)) {- JS_FreeValue(ctx, v);- continue;- }- present = js_array_includes(ctx, jsc->property_list,- 1, (JSValueConst *)&v);- if (JS_IsException(present)) {- JS_FreeValue(ctx, v);- goto exception;- }- if (!JS_ToBoolFree(ctx, present)) {- JS_SetPropertyInt64(ctx, jsc->property_list, j++, v);- } else {- JS_FreeValue(ctx, v);- }- }- }- }- space = JS_DupValue(ctx, space0);- if (JS_IsObject(space)) {- JSObject *p = JS_VALUE_GET_OBJ(space);- if (p->class_id == JS_CLASS_NUMBER) {- space = JS_ToNumberFree(ctx, space);- } else if (p->class_id == JS_CLASS_STRING) {- space = JS_ToStringFree(ctx, space);- }- if (JS_IsException(space)) {- JS_FreeValue(ctx, space);- goto exception;- }- }- if (JS_IsNumber(space)) {- int n;- if (JS_ToInt32Clamp(ctx, &n, space, 0, 10, 0))- goto exception;- jsc->gap = JS_NewStringLen(ctx, " ", n);- } else if (JS_IsString(space)) {- JSString *p = JS_VALUE_GET_STRING(space);- jsc->gap = js_sub_string(ctx, p, 0, min_int(p->len, 10));- } else {- jsc->gap = JS_DupValue(ctx, jsc->empty);- }- JS_FreeValue(ctx, space);- if (JS_IsException(jsc->gap))- goto exception;- wrapper = JS_NewObject(ctx);- if (JS_IsException(wrapper))- goto exception;- if (JS_DefinePropertyValue(ctx, wrapper, JS_ATOM_empty_string,- JS_DupValue(ctx, obj), JS_PROP_C_W_E) < 0)- goto exception;- val = JS_DupValue(ctx, obj);- - val = js_json_check(ctx, jsc, wrapper, val, jsc->empty);- if (JS_IsException(val))- goto exception;- if (JS_IsUndefined(val)) {- ret = JS_UNDEFINED;- goto done1;- }- if (js_json_to_str(ctx, jsc, wrapper, val, jsc->empty))- goto exception;-- ret = string_buffer_end(jsc->b);- goto done;--exception:- ret = JS_EXCEPTION;-done1:- string_buffer_free(jsc->b);-done:- JS_FreeValue(ctx, wrapper);- JS_FreeValue(ctx, jsc->empty);- JS_FreeValue(ctx, jsc->gap);- JS_FreeValue(ctx, jsc->property_list);- JS_FreeValue(ctx, jsc->stack);- return ret;-}--static JSValue js_json_stringify(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- // stringify(val, replacer, space)- return JS_JSONStringify(ctx, argv[0], argv[1], argv[2]);-}--static const JSCFunctionListEntry js_json_funcs[] = {- JS_CFUNC_DEF("parse", 2, js_json_parse ),- JS_CFUNC_DEF("stringify", 3, js_json_stringify ),- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "JSON", JS_PROP_CONFIGURABLE ),-};--static const JSCFunctionListEntry js_json_obj[] = {- JS_OBJECT_DEF("JSON", js_json_funcs, countof(js_json_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),-};--void JS_AddIntrinsicJSON(JSContext *ctx)-{- /* add JSON as autoinit object */- JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_json_obj, countof(js_json_obj));-}--/* Reflect */--static JSValue js_reflect_apply(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return js_function_apply(ctx, argv[0], max_int(0, argc - 1), argv + 1, 0);-}--static JSValue js_reflect_construct(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValueConst func, array_arg, new_target;- JSValue *tab, ret;- uint32_t len;-- func = argv[0];- array_arg = argv[1];- if (argc > 2) {- new_target = argv[2];- if (!JS_IsConstructor(ctx, new_target))- return JS_ThrowTypeError(ctx, "not a constructor");- } else {- new_target = func;- }- tab = build_arg_list(ctx, &len, array_arg);- if (!tab)- return JS_EXCEPTION;- ret = JS_CallConstructor2(ctx, func, new_target, len, (JSValueConst *)tab);- free_arg_list(ctx, tab, len);- return ret;-}--static JSValue js_reflect_deleteProperty(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValueConst obj;- JSAtom atom;- int ret;-- obj = argv[0];- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)- return JS_ThrowTypeErrorNotAnObject(ctx);- atom = JS_ValueToAtom(ctx, argv[1]);- if (unlikely(atom == JS_ATOM_NULL))- return JS_EXCEPTION;- ret = JS_DeleteProperty(ctx, obj, atom, 0);- JS_FreeAtom(ctx, atom);- if (ret < 0)- return JS_EXCEPTION;- else- return JS_NewBool(ctx, ret);-}--static JSValue js_reflect_get(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValueConst obj, prop, receiver;- JSAtom atom;- JSValue ret;-- obj = argv[0];- prop = argv[1];- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)- return JS_ThrowTypeErrorNotAnObject(ctx);- if (argc > 2)- receiver = argv[2];- else- receiver = obj;- atom = JS_ValueToAtom(ctx, prop);- if (unlikely(atom == JS_ATOM_NULL))- return JS_EXCEPTION;- ret = JS_GetPropertyInternal(ctx, obj, atom, receiver, FALSE);- JS_FreeAtom(ctx, atom);- return ret;-}--static JSValue js_reflect_has(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValueConst obj, prop;- JSAtom atom;- int ret;-- obj = argv[0];- prop = argv[1];- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)- return JS_ThrowTypeErrorNotAnObject(ctx);- atom = JS_ValueToAtom(ctx, prop);- if (unlikely(atom == JS_ATOM_NULL))- return JS_EXCEPTION;- ret = JS_HasProperty(ctx, obj, atom);- JS_FreeAtom(ctx, atom);- if (ret < 0)- return JS_EXCEPTION;- else- return JS_NewBool(ctx, ret);-}--static JSValue js_reflect_set(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValueConst obj, prop, val, receiver;- int ret;- JSAtom atom;-- obj = argv[0];- prop = argv[1];- val = argv[2];- if (argc > 3)- receiver = argv[3];- else- receiver = obj;- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)- return JS_ThrowTypeErrorNotAnObject(ctx);- atom = JS_ValueToAtom(ctx, prop);- if (unlikely(atom == JS_ATOM_NULL))- return JS_EXCEPTION;- ret = JS_SetPropertyGeneric(ctx, JS_VALUE_GET_OBJ(obj), atom,- JS_DupValue(ctx, val), receiver, 0);- JS_FreeAtom(ctx, atom);- if (ret < 0)- return JS_EXCEPTION;- else- return JS_NewBool(ctx, ret);-}--static JSValue js_reflect_setPrototypeOf(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- int ret;- ret = JS_SetPrototypeInternal(ctx, argv[0], argv[1], FALSE);- if (ret < 0)- return JS_EXCEPTION;- else- return JS_NewBool(ctx, ret);-}--static JSValue js_reflect_ownKeys(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT)- return JS_ThrowTypeErrorNotAnObject(ctx);- return JS_GetOwnPropertyNames2(ctx, argv[0],- JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK,- JS_ITERATOR_KIND_KEY);-}--static const JSCFunctionListEntry js_reflect_funcs[] = {- JS_CFUNC_DEF("apply", 3, js_reflect_apply ),- JS_CFUNC_DEF("construct", 2, js_reflect_construct ),- JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 1 ),- JS_CFUNC_DEF("deleteProperty", 2, js_reflect_deleteProperty ),- JS_CFUNC_DEF("get", 2, js_reflect_get ),- JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 1 ),- JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 1 ),- JS_CFUNC_DEF("has", 2, js_reflect_has ),- JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 1 ),- JS_CFUNC_DEF("ownKeys", 1, js_reflect_ownKeys ),- JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 1 ),- JS_CFUNC_DEF("set", 3, js_reflect_set ),- JS_CFUNC_DEF("setPrototypeOf", 2, js_reflect_setPrototypeOf ),- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Reflect", JS_PROP_CONFIGURABLE ),-};--static const JSCFunctionListEntry js_reflect_obj[] = {- JS_OBJECT_DEF("Reflect", js_reflect_funcs, countof(js_reflect_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),-};--/* Proxy */--static void js_proxy_finalizer(JSRuntime *rt, JSValue val)-{- JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY);- if (s) {- JS_FreeValueRT(rt, s->target);- JS_FreeValueRT(rt, s->handler);- js_free_rt(rt, s);- }-}--static void js_proxy_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func)-{- JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY);- if (s) {- JS_MarkValue(rt, s->target, mark_func);- JS_MarkValue(rt, s->handler, mark_func);- }-}--static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx)-{- return JS_ThrowTypeError(ctx, "revoked proxy");-}--static JSProxyData *get_proxy_method(JSContext *ctx, JSValue *pmethod,- JSValueConst obj, JSAtom name)-{- JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY);- JSValue method;-- /* safer to test recursion in all proxy methods */- if (js_check_stack_overflow(ctx->rt, 0)) {- JS_ThrowStackOverflow(ctx);- return NULL;- }- - /* 's' should never be NULL */- if (s->is_revoked) {- JS_ThrowTypeErrorRevokedProxy(ctx);- return NULL;- }- method = JS_GetProperty(ctx, s->handler, name);- if (JS_IsException(method))- return NULL;- if (JS_IsNull(method))- method = JS_UNDEFINED;- *pmethod = method;- return s;-}--static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj)-{- JSProxyData *s;- JSValue method, ret, proto1;- int res;-- s = get_proxy_method(ctx, &method, obj, JS_ATOM_getPrototypeOf);- if (!s)- return JS_EXCEPTION;- if (JS_IsUndefined(method))- return JS_GetPrototype(ctx, s->target);- ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);- if (JS_IsException(ret))- return ret;- if (JS_VALUE_GET_TAG(ret) != JS_TAG_NULL &&- JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {- goto fail;- }- res = JS_IsExtensible(ctx, s->target);- if (res < 0) {- JS_FreeValue(ctx, ret);- return JS_EXCEPTION;- }- if (!res) {- /* check invariant */- proto1 = JS_GetPrototype(ctx, s->target);- if (JS_IsException(proto1)) {- JS_FreeValue(ctx, ret);- return JS_EXCEPTION;- }- if (JS_VALUE_GET_OBJ(proto1) != JS_VALUE_GET_OBJ(ret)) {- JS_FreeValue(ctx, proto1);- fail:- JS_FreeValue(ctx, ret);- return JS_ThrowTypeError(ctx, "proxy: inconsistent prototype");- }- JS_FreeValue(ctx, proto1);- }- return ret;-}--static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,- JSValueConst proto_val, BOOL throw_flag)-{- JSProxyData *s;- JSValue method, ret, proto1;- JSValueConst args[2];- BOOL res;- int res2;-- s = get_proxy_method(ctx, &method, obj, JS_ATOM_setPrototypeOf);- if (!s)- return -1;- if (JS_IsUndefined(method))- return JS_SetPrototypeInternal(ctx, s->target, proto_val, throw_flag);- args[0] = s->target;- args[1] = proto_val;- ret = JS_CallFree(ctx, method, s->handler, 2, args);- if (JS_IsException(ret))- return -1;- res = JS_ToBoolFree(ctx, ret);- if (!res) {- if (throw_flag) {- JS_ThrowTypeError(ctx, "proxy: bad prototype");- return -1;- } else {- return FALSE;- }- }- res2 = JS_IsExtensible(ctx, s->target);- if (res2 < 0)- return -1;- if (!res2) {- proto1 = JS_GetPrototype(ctx, s->target);- if (JS_IsException(proto1))- return -1;- if (JS_VALUE_GET_OBJ(proto_val) != JS_VALUE_GET_OBJ(proto1)) {- JS_FreeValue(ctx, proto1);- JS_ThrowTypeError(ctx, "proxy: inconsistent prototype");- return -1;- }- JS_FreeValue(ctx, proto1);- }- return TRUE;-}--static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj)-{- JSProxyData *s;- JSValue method, ret;- BOOL res;- int res2;-- s = get_proxy_method(ctx, &method, obj, JS_ATOM_isExtensible);- if (!s)- return -1;- if (JS_IsUndefined(method))- return JS_IsExtensible(ctx, s->target);- ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);- if (JS_IsException(ret))- return -1;- res = JS_ToBoolFree(ctx, ret);- res2 = JS_IsExtensible(ctx, s->target);- if (res2 < 0)- return res2;- if (res != res2) {- JS_ThrowTypeError(ctx, "proxy: inconsistent isExtensible");- return -1;- }- return res;-}--static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj)-{- JSProxyData *s;- JSValue method, ret;- BOOL res;- int res2;-- s = get_proxy_method(ctx, &method, obj, JS_ATOM_preventExtensions);- if (!s)- return -1;- if (JS_IsUndefined(method))- return JS_PreventExtensions(ctx, s->target);- ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);- if (JS_IsException(ret))- return -1;- res = JS_ToBoolFree(ctx, ret);- if (res) {- res2 = JS_IsExtensible(ctx, s->target);- if (res2 < 0)- return res2;- if (res2) {- JS_ThrowTypeError(ctx, "proxy: inconsistent preventExtensions");- return -1;- }- }- return res;-}--static int js_proxy_has(JSContext *ctx, JSValueConst obj, JSAtom atom)-{- JSProxyData *s;- JSValue method, ret1, atom_val;- int ret, res;- JSObject *p;- JSValueConst args[2];- BOOL res2;-- s = get_proxy_method(ctx, &method, obj, JS_ATOM_has);- if (!s)- return -1;- if (JS_IsUndefined(method))- return JS_HasProperty(ctx, s->target, atom);- atom_val = JS_AtomToValue(ctx, atom);- if (JS_IsException(atom_val)) {- JS_FreeValue(ctx, method);- return -1;- }- args[0] = s->target;- args[1] = atom_val;- ret1 = JS_CallFree(ctx, method, s->handler, 2, args);- JS_FreeValue(ctx, atom_val);- if (JS_IsException(ret1))- return -1;- ret = JS_ToBoolFree(ctx, ret1);- if (!ret) {- JSPropertyDescriptor desc;- p = JS_VALUE_GET_OBJ(s->target);- res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);- if (res < 0)- return -1;- if (res) {- res2 = !(desc.flags & JS_PROP_CONFIGURABLE);- js_free_desc(ctx, &desc);- if (res2 || !p->extensible) {- JS_ThrowTypeError(ctx, "proxy: inconsistent has");- return -1;- }- }- }- return ret;-}--static JSValue js_proxy_get(JSContext *ctx, JSValueConst obj, JSAtom atom,- JSValueConst receiver)-{- JSProxyData *s;- JSValue method, ret, atom_val;- int res;- JSValueConst args[3];- JSPropertyDescriptor desc;-- s = get_proxy_method(ctx, &method, obj, JS_ATOM_get);- if (!s)- return JS_EXCEPTION;- /* Note: recursion is possible thru the prototype of s->target */- if (JS_IsUndefined(method))- return JS_GetPropertyInternal(ctx, s->target, atom, receiver, FALSE);- atom_val = JS_AtomToValue(ctx, atom);- if (JS_IsException(atom_val)) {- JS_FreeValue(ctx, method);- return JS_EXCEPTION;- }- args[0] = s->target;- args[1] = atom_val;- args[2] = receiver;- ret = JS_CallFree(ctx, method, s->handler, 3, args);- JS_FreeValue(ctx, atom_val);- if (JS_IsException(ret))- return JS_EXCEPTION;- res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);- if (res < 0)- return JS_EXCEPTION;- if (res) {- if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) {- if (!js_same_value(ctx, desc.value, ret)) {- goto fail;- }- } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET) {- if (JS_IsUndefined(desc.getter) && !JS_IsUndefined(ret)) {- fail:- js_free_desc(ctx, &desc);- JS_FreeValue(ctx, ret);- return JS_ThrowTypeError(ctx, "proxy: inconsistent get");- }- }- js_free_desc(ctx, &desc);- }- return ret;-}--static int js_proxy_set(JSContext *ctx, JSValueConst obj, JSAtom atom,- JSValueConst value, JSValueConst receiver, int flags)-{- JSProxyData *s;- JSValue method, ret1, atom_val;- int ret, res;- JSValueConst args[4];-- s = get_proxy_method(ctx, &method, obj, JS_ATOM_set);- if (!s)- return -1;- if (JS_IsUndefined(method)) {- return JS_SetPropertyGeneric(ctx, JS_VALUE_GET_OBJ(s->target), atom,- JS_DupValue(ctx, value), receiver,- flags);- }- atom_val = JS_AtomToValue(ctx, atom);- if (JS_IsException(atom_val)) {- JS_FreeValue(ctx, method);- return -1;- }- args[0] = s->target;- args[1] = atom_val;- args[2] = value;- args[3] = receiver;- ret1 = JS_CallFree(ctx, method, s->handler, 4, args);- JS_FreeValue(ctx, atom_val);- if (JS_IsException(ret1))- return -1;- ret = JS_ToBoolFree(ctx, ret1);- if (ret) {- JSPropertyDescriptor desc;- res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);- if (res < 0)- return -1;- if (res) {- if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) {- if (!js_same_value(ctx, desc.value, value)) {- goto fail;- }- } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET && JS_IsUndefined(desc.setter)) {- fail:- js_free_desc(ctx, &desc);- JS_ThrowTypeError(ctx, "proxy: inconsistent set");- return -1;- }- js_free_desc(ctx, &desc);- }- } else {- if ((flags & JS_PROP_THROW) ||- ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {- JS_ThrowTypeError(ctx, "proxy: cannot set property");- return -1;- }- }- return ret;-}--static JSValue js_create_desc(JSContext *ctx, JSValueConst val,- JSValueConst getter, JSValueConst setter,- int flags)-{- JSValue ret;- ret = JS_NewObject(ctx);- if (JS_IsException(ret))- return ret;- if (flags & JS_PROP_HAS_GET) {- JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, getter),- JS_PROP_C_W_E);- }- if (flags & JS_PROP_HAS_SET) {- JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, setter),- JS_PROP_C_W_E);- }- if (flags & JS_PROP_HAS_VALUE) {- JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, val),- JS_PROP_C_W_E);- }- if (flags & JS_PROP_HAS_WRITABLE) {- JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable,- JS_NewBool(ctx, (flags & JS_PROP_WRITABLE) != 0),- JS_PROP_C_W_E);- }- if (flags & JS_PROP_HAS_ENUMERABLE) {- JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable,- JS_NewBool(ctx, (flags & JS_PROP_ENUMERABLE) != 0),- JS_PROP_C_W_E);- }- if (flags & JS_PROP_HAS_CONFIGURABLE) {- JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable,- JS_NewBool(ctx, (flags & JS_PROP_CONFIGURABLE) != 0),- JS_PROP_C_W_E);- }- return ret;-}--static int js_proxy_get_own_property(JSContext *ctx, JSPropertyDescriptor *pdesc,- JSValueConst obj, JSAtom prop)-{- JSProxyData *s;- JSValue method, trap_result_obj, prop_val;- int res, target_desc_ret, ret;- JSObject *p;- JSValueConst args[2];- JSPropertyDescriptor result_desc, target_desc;-- s = get_proxy_method(ctx, &method, obj, JS_ATOM_getOwnPropertyDescriptor);- if (!s)- return -1;- p = JS_VALUE_GET_OBJ(s->target);- if (JS_IsUndefined(method)) {- return JS_GetOwnPropertyInternal(ctx, pdesc, p, prop);- }- prop_val = JS_AtomToValue(ctx, prop);- if (JS_IsException(prop_val)) {- JS_FreeValue(ctx, method);- return -1;- }- args[0] = s->target;- args[1] = prop_val;- trap_result_obj = JS_CallFree(ctx, method, s->handler, 2, args);- JS_FreeValue(ctx, prop_val);- if (JS_IsException(trap_result_obj))- return -1;- if (!JS_IsObject(trap_result_obj) && !JS_IsUndefined(trap_result_obj)) {- JS_FreeValue(ctx, trap_result_obj);- goto fail;- }- target_desc_ret = JS_GetOwnPropertyInternal(ctx, &target_desc, p, prop);- if (target_desc_ret < 0) {- JS_FreeValue(ctx, trap_result_obj);- return -1;- }- if (target_desc_ret)- js_free_desc(ctx, &target_desc);- if (JS_IsUndefined(trap_result_obj)) {- if (target_desc_ret) {- if (!(target_desc.flags & JS_PROP_CONFIGURABLE) || !p->extensible)- goto fail;- }- ret = FALSE;- } else {- int flags1, extensible_target;- extensible_target = JS_IsExtensible(ctx, s->target);- if (extensible_target < 0) {- JS_FreeValue(ctx, trap_result_obj);- return -1;- }- res = js_obj_to_desc(ctx, &result_desc, trap_result_obj);- JS_FreeValue(ctx, trap_result_obj);- if (res < 0)- return -1;- - if (target_desc_ret) {- /* convert result_desc.flags to defineProperty flags */- flags1 = result_desc.flags | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE;- if (result_desc.flags & JS_PROP_GETSET)- flags1 |= JS_PROP_HAS_GET | JS_PROP_HAS_SET;- else- flags1 |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE;- /* XXX: not complete check: need to compare value &- getter/setter as in defineproperty */- if (!check_define_prop_flags(target_desc.flags, flags1))- goto fail1;- } else {- if (!extensible_target)- goto fail1;- }- if (!(result_desc.flags & JS_PROP_CONFIGURABLE)) {- if (!target_desc_ret || (target_desc.flags & JS_PROP_CONFIGURABLE))- goto fail1;- if ((result_desc.flags &- (JS_PROP_GETSET | JS_PROP_WRITABLE)) == 0 &&- target_desc_ret &&- (target_desc.flags & JS_PROP_WRITABLE) != 0) {- /* proxy-missing-checks */- fail1:- js_free_desc(ctx, &result_desc);- fail:- JS_ThrowTypeError(ctx, "proxy: inconsistent getOwnPropertyDescriptor");- return -1;- }- }- ret = TRUE;- if (pdesc) {- *pdesc = result_desc;- } else {- js_free_desc(ctx, &result_desc);- }- }- return ret;-}--static int js_proxy_define_own_property(JSContext *ctx, JSValueConst obj,- JSAtom prop, JSValueConst val,- JSValueConst getter, JSValueConst setter,- int flags)-{- JSProxyData *s;- JSValue method, ret1, prop_val, desc_val;- int res, ret;- JSObject *p;- JSValueConst args[3];- JSPropertyDescriptor desc;- BOOL setting_not_configurable;-- s = get_proxy_method(ctx, &method, obj, JS_ATOM_defineProperty);- if (!s)- return -1;- if (JS_IsUndefined(method)) {- return JS_DefineProperty(ctx, s->target, prop, val, getter, setter, flags);- }- prop_val = JS_AtomToValue(ctx, prop);- if (JS_IsException(prop_val)) {- JS_FreeValue(ctx, method);- return -1;- }- desc_val = js_create_desc(ctx, val, getter, setter, flags);- if (JS_IsException(desc_val)) {- JS_FreeValue(ctx, prop_val);- JS_FreeValue(ctx, method);- return -1;- }- args[0] = s->target;- args[1] = prop_val;- args[2] = desc_val;- ret1 = JS_CallFree(ctx, method, s->handler, 3, args);- JS_FreeValue(ctx, prop_val);- JS_FreeValue(ctx, desc_val);- if (JS_IsException(ret1))- return -1;- ret = JS_ToBoolFree(ctx, ret1);- if (!ret) {- if (flags & JS_PROP_THROW) {- JS_ThrowTypeError(ctx, "proxy: defineProperty exception");- return -1;- } else {- return 0;- }- }- p = JS_VALUE_GET_OBJ(s->target);- res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);- if (res < 0)- return -1;- setting_not_configurable = ((flags & (JS_PROP_HAS_CONFIGURABLE |- JS_PROP_CONFIGURABLE)) ==- JS_PROP_HAS_CONFIGURABLE);- if (!res) {- if (!p->extensible || setting_not_configurable)- goto fail;- } else {- if (!check_define_prop_flags(desc.flags, flags) ||- ((desc.flags & JS_PROP_CONFIGURABLE) && setting_not_configurable)) {- goto fail1;- }- if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {- if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) ==- JS_PROP_GETSET) {- if ((flags & JS_PROP_HAS_GET) &&- !js_same_value(ctx, getter, desc.getter)) {- goto fail1;- }- if ((flags & JS_PROP_HAS_SET) &&- !js_same_value(ctx, setter, desc.setter)) {- goto fail1;- }- }- } else if (flags & JS_PROP_HAS_VALUE) {- if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) ==- JS_PROP_WRITABLE && !(flags & JS_PROP_WRITABLE)) {- /* missing-proxy-check feature */- goto fail1;- } else if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 &&- !js_same_value(ctx, val, desc.value)) {- goto fail1;- }- }- if (flags & JS_PROP_HAS_WRITABLE) {- if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE |- JS_PROP_WRITABLE)) == JS_PROP_WRITABLE) {- /* proxy-missing-checks */- fail1:- js_free_desc(ctx, &desc);- fail:- JS_ThrowTypeError(ctx, "proxy: inconsistent defineProperty");- return -1;- }- }- js_free_desc(ctx, &desc);- }- return 1;-}--static int js_proxy_delete_property(JSContext *ctx, JSValueConst obj,- JSAtom atom)-{- JSProxyData *s;- JSValue method, ret, atom_val;- int res, res2, is_extensible;- JSValueConst args[2];-- s = get_proxy_method(ctx, &method, obj, JS_ATOM_deleteProperty);- if (!s)- return -1;- if (JS_IsUndefined(method)) {- return JS_DeleteProperty(ctx, s->target, atom, 0);- }- atom_val = JS_AtomToValue(ctx, atom);;- if (JS_IsException(atom_val)) {- JS_FreeValue(ctx, method);- return -1;- }- args[0] = s->target;- args[1] = atom_val;- ret = JS_CallFree(ctx, method, s->handler, 2, args);- JS_FreeValue(ctx, atom_val);- if (JS_IsException(ret))- return -1;- res = JS_ToBoolFree(ctx, ret);- if (res) {- JSPropertyDescriptor desc;- res2 = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);- if (res2 < 0)- return -1;- if (res2) {- if (!(desc.flags & JS_PROP_CONFIGURABLE))- goto fail;- is_extensible = JS_IsExtensible(ctx, s->target);- if (is_extensible < 0)- goto fail1;- if (!is_extensible) {- /* proxy-missing-checks */- fail:- JS_ThrowTypeError(ctx, "proxy: inconsistent deleteProperty");- fail1:- js_free_desc(ctx, &desc);- return -1;- }- js_free_desc(ctx, &desc);- }- }- return res;-}--/* return the index of the property or -1 if not found */-static int find_prop_key(const JSPropertyEnum *tab, int n, JSAtom atom)-{- int i;- for(i = 0; i < n; i++) {- if (tab[i].atom == atom)- return i;- }- return -1;-}--static int js_proxy_get_own_property_names(JSContext *ctx,- JSPropertyEnum **ptab,- uint32_t *plen,- JSValueConst obj)-{- JSProxyData *s;- JSValue method, prop_array, val;- uint32_t len, i, len2;- JSPropertyEnum *tab, *tab2;- JSAtom atom;- JSPropertyDescriptor desc;- int res, is_extensible, idx;-- s = get_proxy_method(ctx, &method, obj, JS_ATOM_ownKeys);- if (!s)- return -1;- if (JS_IsUndefined(method)) {- return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen,- JS_VALUE_GET_OBJ(s->target),- JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK);- }- prop_array = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);- if (JS_IsException(prop_array))- return -1;- tab = NULL;- len = 0;- tab2 = NULL;- len2 = 0;- if (js_get_length32(ctx, &len, prop_array))- goto fail;- if (len > 0) {- tab = js_mallocz(ctx, sizeof(tab[0]) * len);- if (!tab)- goto fail;- }- for(i = 0; i < len; i++) {- val = JS_GetPropertyUint32(ctx, prop_array, i);- if (JS_IsException(val))- goto fail;- if (!JS_IsString(val) && !JS_IsSymbol(val)) {- JS_FreeValue(ctx, val);- JS_ThrowTypeError(ctx, "proxy: properties must be strings or symbols");- goto fail;- }- atom = JS_ValueToAtom(ctx, val);- JS_FreeValue(ctx, val);- if (atom == JS_ATOM_NULL)- goto fail;- tab[i].atom = atom;- tab[i].is_enumerable = FALSE; /* XXX: redundant? */- }-- /* check duplicate properties (XXX: inefficient, could store the- * properties an a temporary object to use the hash) */- for(i = 1; i < len; i++) {- if (find_prop_key(tab, i, tab[i].atom) >= 0) {- JS_ThrowTypeError(ctx, "proxy: duplicate property");- goto fail;- }- }-- is_extensible = JS_IsExtensible(ctx, s->target);- if (is_extensible < 0)- goto fail;-- /* check if there are non configurable properties */- if (s->is_revoked) {- JS_ThrowTypeErrorRevokedProxy(ctx);- goto fail;- }- if (JS_GetOwnPropertyNamesInternal(ctx, &tab2, &len2, JS_VALUE_GET_OBJ(s->target),- JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK))- goto fail;- for(i = 0; i < len2; i++) {- if (s->is_revoked) {- JS_ThrowTypeErrorRevokedProxy(ctx);- goto fail;- }- res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target),- tab2[i].atom);- if (res < 0)- goto fail;- if (res) { /* safety, property should be found */- js_free_desc(ctx, &desc);- if (!(desc.flags & JS_PROP_CONFIGURABLE) || !is_extensible) {- idx = find_prop_key(tab, len, tab2[i].atom);- if (idx < 0) {- JS_ThrowTypeError(ctx, "proxy: target property must be present in proxy ownKeys");- goto fail;- }- /* mark the property as found */- if (!is_extensible)- tab[idx].is_enumerable = TRUE;- }- }- }- if (!is_extensible) {- /* check that all property in 'tab' were checked */- for(i = 0; i < len; i++) {- if (!tab[i].is_enumerable) {- JS_ThrowTypeError(ctx, "proxy: property not present in target were returned by non extensible proxy");- goto fail;- }- }- }-- js_free_prop_enum(ctx, tab2, len2);- JS_FreeValue(ctx, prop_array);- *ptab = tab;- *plen = len;- return 0;- fail:- js_free_prop_enum(ctx, tab2, len2);- js_free_prop_enum(ctx, tab, len);- JS_FreeValue(ctx, prop_array);- return -1;-}--static JSValue js_proxy_call_constructor(JSContext *ctx, JSValueConst func_obj,- JSValueConst new_target,- int argc, JSValueConst *argv)-{- JSProxyData *s;- JSValue method, arg_array, ret;- JSValueConst args[3];-- s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_construct);- if (!s)- return JS_EXCEPTION;- if (!JS_IsConstructor(ctx, s->target))- return JS_ThrowTypeError(ctx, "not a constructor");- if (JS_IsUndefined(method))- return JS_CallConstructor2(ctx, s->target, new_target, argc, argv);- arg_array = js_create_array(ctx, argc, argv);- if (JS_IsException(arg_array)) {- ret = JS_EXCEPTION;- goto fail;- }- args[0] = s->target;- args[1] = arg_array;- args[2] = new_target;- ret = JS_Call(ctx, method, s->handler, 3, args);- if (!JS_IsException(ret) && JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {- JS_FreeValue(ctx, ret);- ret = JS_ThrowTypeErrorNotAnObject(ctx);- }- fail:- JS_FreeValue(ctx, method);- JS_FreeValue(ctx, arg_array);- return ret;-}--static JSValue js_proxy_call(JSContext *ctx, JSValueConst func_obj,- JSValueConst this_obj,- int argc, JSValueConst *argv, int flags)-{- JSProxyData *s;- JSValue method, arg_array, ret;- JSValueConst args[3];-- if (flags & JS_CALL_FLAG_CONSTRUCTOR)- return js_proxy_call_constructor(ctx, func_obj, this_obj, argc, argv);- - s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_apply);- if (!s)- return JS_EXCEPTION;- if (!s->is_func) {- JS_FreeValue(ctx, method);- return JS_ThrowTypeError(ctx, "not a function");- }- if (JS_IsUndefined(method))- return JS_Call(ctx, s->target, this_obj, argc, argv);- arg_array = js_create_array(ctx, argc, argv);- if (JS_IsException(arg_array)) {- ret = JS_EXCEPTION;- goto fail;- }- args[0] = s->target;- args[1] = this_obj;- args[2] = arg_array;- ret = JS_Call(ctx, method, s->handler, 3, args);- fail:- JS_FreeValue(ctx, method);- JS_FreeValue(ctx, arg_array);- return ret;-}--static int js_proxy_isArray(JSContext *ctx, JSValueConst obj)-{- JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY);- if (!s)- return FALSE;- if (s->is_revoked) {- JS_ThrowTypeErrorRevokedProxy(ctx);- return -1;- }- return JS_IsArray(ctx, s->target);-}--static const JSClassExoticMethods js_proxy_exotic_methods = {- .get_own_property = js_proxy_get_own_property,- .define_own_property = js_proxy_define_own_property,- .delete_property = js_proxy_delete_property,- .get_own_property_names = js_proxy_get_own_property_names,- .has_property = js_proxy_has,- .get_property = js_proxy_get,- .set_property = js_proxy_set,-};--static JSValue js_proxy_constructor(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValueConst target, handler;- JSValue obj;- JSProxyData *s;-- target = argv[0];- handler = argv[1];- if (JS_VALUE_GET_TAG(target) != JS_TAG_OBJECT ||- JS_VALUE_GET_TAG(handler) != JS_TAG_OBJECT)- return JS_ThrowTypeErrorNotAnObject(ctx);-- obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_PROXY);- if (JS_IsException(obj))- return obj;- s = js_malloc(ctx, sizeof(JSProxyData));- if (!s) {- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;- }- s->target = JS_DupValue(ctx, target);- s->handler = JS_DupValue(ctx, handler);- s->is_func = JS_IsFunction(ctx, target);- s->is_revoked = FALSE;- JS_SetOpaque(obj, s);- JS_SetConstructorBit(ctx, obj, JS_IsConstructor(ctx, target));- return obj;-}--static JSValue js_proxy_revoke(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic,- JSValue *func_data)-{- JSProxyData *s = JS_GetOpaque(func_data[0], JS_CLASS_PROXY);- if (s) {- /* We do not free the handler and target in case they are- referenced as constants in the C call stack */- s->is_revoked = TRUE;- JS_FreeValue(ctx, func_data[0]);- func_data[0] = JS_NULL;- }- return JS_UNDEFINED;-}--static JSValue js_proxy_revoke_constructor(JSContext *ctx,- JSValueConst proxy_obj)-{- return JS_NewCFunctionData(ctx, js_proxy_revoke, 0, 0, 1, &proxy_obj);-}--static JSValue js_proxy_revocable(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue proxy_obj, revoke_obj = JS_UNDEFINED, obj;-- proxy_obj = js_proxy_constructor(ctx, JS_UNDEFINED, argc, argv);- if (JS_IsException(proxy_obj))- goto fail;- revoke_obj = js_proxy_revoke_constructor(ctx, proxy_obj);- if (JS_IsException(revoke_obj))- goto fail;- obj = JS_NewObject(ctx);- if (JS_IsException(obj))- goto fail;- // XXX: exceptions?- JS_DefinePropertyValue(ctx, obj, JS_ATOM_proxy, proxy_obj, JS_PROP_C_W_E);- JS_DefinePropertyValue(ctx, obj, JS_ATOM_revoke, revoke_obj, JS_PROP_C_W_E);- return obj;- fail:- JS_FreeValue(ctx, proxy_obj);- JS_FreeValue(ctx, revoke_obj);- return JS_EXCEPTION;-}--static const JSCFunctionListEntry js_proxy_funcs[] = {- JS_CFUNC_DEF("revocable", 2, js_proxy_revocable ),-};--static const JSClassShortDef js_proxy_class_def[] = {- { JS_ATOM_Object, js_proxy_finalizer, js_proxy_mark }, /* JS_CLASS_PROXY */-};--void JS_AddIntrinsicProxy(JSContext *ctx)-{- JSRuntime *rt = ctx->rt;- JSValue obj1;-- if (!JS_IsRegisteredClass(rt, JS_CLASS_PROXY)) {- init_class_range(rt, js_proxy_class_def, JS_CLASS_PROXY,- countof(js_proxy_class_def));- rt->class_array[JS_CLASS_PROXY].exotic = &js_proxy_exotic_methods;- rt->class_array[JS_CLASS_PROXY].call = js_proxy_call;- }-- obj1 = JS_NewCFunction2(ctx, js_proxy_constructor, "Proxy", 2,- JS_CFUNC_constructor, 0);- JS_SetConstructorBit(ctx, obj1, TRUE);- JS_SetPropertyFunctionList(ctx, obj1, js_proxy_funcs,- countof(js_proxy_funcs));- JS_DefinePropertyValueStr(ctx, ctx->global_obj, "Proxy",- obj1, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);-}--/* Symbol */--static JSValue js_symbol_constructor(JSContext *ctx, JSValueConst new_target,- int argc, JSValueConst *argv)-{- JSValue str;- JSString *p;-- if (!JS_IsUndefined(new_target))- return JS_ThrowTypeError(ctx, "not a constructor");- if (argc == 0 || JS_IsUndefined(argv[0])) {- p = NULL;- } else {- str = JS_ToString(ctx, argv[0]);- if (JS_IsException(str))- return JS_EXCEPTION;- p = JS_VALUE_GET_STRING(str);- }- return JS_NewSymbol(ctx, p, JS_ATOM_TYPE_SYMBOL);-}--static JSValue js_thisSymbolValue(JSContext *ctx, JSValueConst this_val)-{- if (JS_VALUE_GET_TAG(this_val) == JS_TAG_SYMBOL)- return JS_DupValue(ctx, this_val);-- if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {- JSObject *p = JS_VALUE_GET_OBJ(this_val);- if (p->class_id == JS_CLASS_SYMBOL) {- if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_SYMBOL)- return JS_DupValue(ctx, p->u.object_data);- }- }- return JS_ThrowTypeError(ctx, "not a symbol");-}--static JSValue js_symbol_toString(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue val, ret;- val = js_thisSymbolValue(ctx, this_val);- if (JS_IsException(val))- return val;- /* XXX: use JS_ToStringInternal() with a flags */- ret = js_string_constructor(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val);- JS_FreeValue(ctx, val);- return ret;-}--static JSValue js_symbol_valueOf(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return js_thisSymbolValue(ctx, this_val);-}--static JSValue js_symbol_get_description(JSContext *ctx, JSValueConst this_val)-{- JSValue val, ret;- JSAtomStruct *p;-- val = js_thisSymbolValue(ctx, this_val);- if (JS_IsException(val))- return val;- p = JS_VALUE_GET_PTR(val);- if (p->len == 0 && p->is_wide_char != 0) {- ret = JS_UNDEFINED;- } else {- ret = JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p));- }- JS_FreeValue(ctx, val);- return ret;-}--static const JSCFunctionListEntry js_symbol_proto_funcs[] = {- JS_CFUNC_DEF("toString", 0, js_symbol_toString ),- JS_CFUNC_DEF("valueOf", 0, js_symbol_valueOf ),- // XXX: should have writable: false- JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_symbol_valueOf ),- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Symbol", JS_PROP_CONFIGURABLE ),- JS_CGETSET_DEF("description", js_symbol_get_description, NULL ),-};--static JSValue js_symbol_for(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue str;-- str = JS_ToString(ctx, argv[0]);- if (JS_IsException(str))- return JS_EXCEPTION;- return JS_NewSymbol(ctx, JS_VALUE_GET_STRING(str), JS_ATOM_TYPE_GLOBAL_SYMBOL);-}--static JSValue js_symbol_keyFor(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSAtomStruct *p;-- if (!JS_IsSymbol(argv[0]))- return JS_ThrowTypeError(ctx, "not a symbol");- p = JS_VALUE_GET_PTR(argv[0]);- if (p->atom_type != JS_ATOM_TYPE_GLOBAL_SYMBOL)- return JS_UNDEFINED;- return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));-}--static const JSCFunctionListEntry js_symbol_funcs[] = {- JS_CFUNC_DEF("for", 1, js_symbol_for ),- JS_CFUNC_DEF("keyFor", 1, js_symbol_keyFor ),-};--/* Set/Map/WeakSet/WeakMap */--typedef struct JSMapRecord {- int ref_count; /* used during enumeration to avoid freeing the record */- BOOL empty; /* TRUE if the record is deleted */- struct JSMapState *map;- struct JSMapRecord *next_weak_ref;- struct list_head link;- struct list_head hash_link;- JSValue key;- JSValue value;-} JSMapRecord;--typedef struct JSMapState {- BOOL is_weak; /* TRUE if WeakSet/WeakMap */- struct list_head records; /* list of JSMapRecord.link */- uint32_t record_count;- struct list_head *hash_table;- uint32_t hash_size; /* must be a power of two */- uint32_t record_count_threshold; /* count at which a hash table- resize is needed */-} JSMapState;--#define MAGIC_SET (1 << 0)-#define MAGIC_WEAK (1 << 1)--static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target,- int argc, JSValueConst *argv, int magic)-{- JSMapState *s;- JSValue obj, adder = JS_UNDEFINED, iter = JS_UNDEFINED, next_method = JS_UNDEFINED;- JSValueConst arr;- BOOL is_set, is_weak;-- is_set = magic & MAGIC_SET;- is_weak = ((magic & MAGIC_WEAK) != 0);- obj = js_create_from_ctor(ctx, new_target, JS_CLASS_MAP + magic);- if (JS_IsException(obj))- return JS_EXCEPTION;- s = js_mallocz(ctx, sizeof(*s));- if (!s)- goto fail;- init_list_head(&s->records);- s->is_weak = is_weak;- JS_SetOpaque(obj, s);- s->hash_size = 1;- s->hash_table = js_malloc(ctx, sizeof(s->hash_table[0]) * s->hash_size);- if (!s->hash_table)- goto fail;- init_list_head(&s->hash_table[0]);- s->record_count_threshold = 4;-- arr = JS_UNDEFINED;- if (argc > 0)- arr = argv[0];- if (!JS_IsUndefined(arr) && !JS_IsNull(arr)) {- JSValue item, ret;- BOOL done;-- adder = JS_GetProperty(ctx, obj, is_set ? JS_ATOM_add : JS_ATOM_set);- if (JS_IsException(adder))- goto fail;- if (!JS_IsFunction(ctx, adder)) {- JS_ThrowTypeError(ctx, "set/add is not a function");- goto fail;- }-- iter = JS_GetIterator(ctx, arr, FALSE);- if (JS_IsException(iter))- goto fail;- next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);- if (JS_IsException(next_method))- goto fail;-- for(;;) {- item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);- if (JS_IsException(item))- goto fail;- if (done) {- JS_FreeValue(ctx, item);- break;- }- if (is_set) {- ret = JS_Call(ctx, adder, obj, 1, (JSValueConst *)&item);- if (JS_IsException(ret)) {- JS_FreeValue(ctx, item);- goto fail;- }- } else {- JSValue key, value;- JSValueConst args[2];- key = JS_UNDEFINED;- value = JS_UNDEFINED;- if (!JS_IsObject(item)) {- JS_ThrowTypeErrorNotAnObject(ctx);- goto fail1;- }- key = JS_GetPropertyUint32(ctx, item, 0);- if (JS_IsException(key))- goto fail1;- value = JS_GetPropertyUint32(ctx, item, 1);- if (JS_IsException(value))- goto fail1;- args[0] = key;- args[1] = value;- ret = JS_Call(ctx, adder, obj, 2, args);- if (JS_IsException(ret)) {- fail1:- JS_FreeValue(ctx, item);- JS_FreeValue(ctx, key);- JS_FreeValue(ctx, value);- goto fail;- }- JS_FreeValue(ctx, key);- JS_FreeValue(ctx, value);- }- JS_FreeValue(ctx, ret);- JS_FreeValue(ctx, item);- }- JS_FreeValue(ctx, next_method);- JS_FreeValue(ctx, iter);- JS_FreeValue(ctx, adder);- }- return obj;- fail:- if (JS_IsObject(iter)) {- /* close the iterator object, preserving pending exception */- JS_IteratorClose(ctx, iter, TRUE);- }- JS_FreeValue(ctx, next_method);- JS_FreeValue(ctx, iter);- JS_FreeValue(ctx, adder);- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--/* XXX: could normalize strings to speed up comparison */-static JSValueConst map_normalize_key(JSContext *ctx, JSValueConst key)-{- uint32_t tag = JS_VALUE_GET_TAG(key);- /* convert -0.0 to +0.0 */- if (JS_TAG_IS_FLOAT64(tag) && JS_VALUE_GET_FLOAT64(key) == 0.0) {- key = JS_NewInt32(ctx, 0);- }- return key;-}--/* XXX: better hash ? */-static uint32_t map_hash_key(JSContext *ctx, JSValueConst key)-{- uint32_t tag = JS_VALUE_GET_NORM_TAG(key);- uint32_t h;- double d;- JSFloat64Union u;-- switch(tag) {- case JS_TAG_BOOL:- h = JS_VALUE_GET_INT(key);- break;- case JS_TAG_STRING:- h = hash_string(JS_VALUE_GET_STRING(key), 0);- break;- case JS_TAG_OBJECT:- case JS_TAG_SYMBOL:- h = (uintptr_t)JS_VALUE_GET_PTR(key) * 3163;- break;- case JS_TAG_INT:- d = JS_VALUE_GET_INT(key) * 3163;- goto hash_float64;- case JS_TAG_FLOAT64:- d = JS_VALUE_GET_FLOAT64(key);- /* normalize the NaN */- if (isnan(d))- d = JS_FLOAT64_NAN;- hash_float64:- u.d = d;- h = (u.u32[0] ^ u.u32[1]) * 3163;- break;- default:- h = 0; /* XXX: bignum support */- break;- }- h ^= tag;- return h;-}--static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s,- JSValueConst key)-{- struct list_head *el;- JSMapRecord *mr;- uint32_t h;- h = map_hash_key(ctx, key) & (s->hash_size - 1);- list_for_each(el, &s->hash_table[h]) {- mr = list_entry(el, JSMapRecord, hash_link);- if (js_same_value_zero(ctx, mr->key, key))- return mr;- }- return NULL;-}--static void map_hash_resize(JSContext *ctx, JSMapState *s)-{- uint32_t new_hash_size, i, h;- size_t slack;- struct list_head *new_hash_table, *el;- JSMapRecord *mr;-- /* XXX: no reporting of memory allocation failure */- if (s->hash_size == 1)- new_hash_size = 4;- else- new_hash_size = s->hash_size * 2;- new_hash_table = js_realloc2(ctx, s->hash_table,- sizeof(new_hash_table[0]) * new_hash_size, &slack);- if (!new_hash_table)- return;- new_hash_size += slack / sizeof(*new_hash_table);-- for(i = 0; i < new_hash_size; i++)- init_list_head(&new_hash_table[i]);-- list_for_each(el, &s->records) {- mr = list_entry(el, JSMapRecord, link);- if (!mr->empty) {- h = map_hash_key(ctx, mr->key) & (new_hash_size - 1);- list_add_tail(&mr->hash_link, &new_hash_table[h]);- }- }- s->hash_table = new_hash_table;- s->hash_size = new_hash_size;- s->record_count_threshold = new_hash_size * 2;-}--static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s,- JSValueConst key)-{- uint32_t h;- JSMapRecord *mr;-- mr = js_malloc(ctx, sizeof(*mr));- if (!mr)- return NULL;- mr->ref_count = 1;- mr->map = s;- mr->empty = FALSE;- if (s->is_weak) {- JSObject *p = JS_VALUE_GET_OBJ(key);- /* Add the weak reference */- mr->next_weak_ref = p->first_weak_ref;- p->first_weak_ref = mr;- } else {- JS_DupValue(ctx, key);- }- mr->key = (JSValue)key;- h = map_hash_key(ctx, key) & (s->hash_size - 1);- list_add_tail(&mr->hash_link, &s->hash_table[h]);- list_add_tail(&mr->link, &s->records);- s->record_count++;- if (s->record_count >= s->record_count_threshold) {- map_hash_resize(ctx, s);- }- return mr;-}--/* Remove the weak reference from the object weak- reference list. we don't use a doubly linked list to- save space, assuming a given object has few weak- references to it */-static void delete_weak_ref(JSRuntime *rt, JSMapRecord *mr)-{- JSMapRecord **pmr, *mr1;- JSObject *p;-- p = JS_VALUE_GET_OBJ(mr->key);- pmr = &p->first_weak_ref;- for(;;) {- mr1 = *pmr;- assert(mr1 != NULL);- if (mr1 == mr)- break;- pmr = &mr1->next_weak_ref;- }- *pmr = mr1->next_weak_ref;-}--static void map_delete_record(JSRuntime *rt, JSMapState *s, JSMapRecord *mr)-{- if (mr->empty)- return;- list_del(&mr->hash_link);- if (s->is_weak) {- delete_weak_ref(rt, mr);- } else {- JS_FreeValueRT(rt, mr->key);- }- JS_FreeValueRT(rt, mr->value);- if (--mr->ref_count == 0) {- list_del(&mr->link);- js_free_rt(rt, mr);- } else {- /* keep a zombie record for iterators */- mr->empty = TRUE;- mr->key = JS_UNDEFINED;- mr->value = JS_UNDEFINED;- }- s->record_count--;-}--static void map_decref_record(JSRuntime *rt, JSMapRecord *mr)-{- if (--mr->ref_count == 0) {- /* the record can be safely removed */- assert(mr->empty);- list_del(&mr->link);- js_free_rt(rt, mr);- }-}--static void reset_weak_ref(JSRuntime *rt, JSObject *p)-{- JSMapRecord *mr, *mr_next;- JSMapState *s;- - /* first pass to remove the records from the WeakMap/WeakSet- lists */- for(mr = p->first_weak_ref; mr != NULL; mr = mr->next_weak_ref) {- s = mr->map;- assert(s->is_weak);- assert(!mr->empty); /* no iterator on WeakMap/WeakSet */- list_del(&mr->hash_link);- list_del(&mr->link);- }- - /* second pass to free the values to avoid modifying the weak- reference list while traversing it. */- for(mr = p->first_weak_ref; mr != NULL; mr = mr_next) {- mr_next = mr->next_weak_ref;- JS_FreeValueRT(rt, mr->value);- js_free_rt(rt, mr);- }-- p->first_weak_ref = NULL; /* fail safe */-}--static JSValue js_map_set(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);- JSMapRecord *mr;- JSValueConst key, value;-- if (!s)- return JS_EXCEPTION;- key = map_normalize_key(ctx, argv[0]);- if (s->is_weak && !JS_IsObject(key))- return JS_ThrowTypeErrorNotAnObject(ctx);- if (magic & MAGIC_SET)- value = JS_UNDEFINED;- else- value = argv[1];- mr = map_find_record(ctx, s, key);- if (mr) {- JS_FreeValue(ctx, mr->value);- } else {- mr = map_add_record(ctx, s, key);- if (!mr)- return JS_EXCEPTION;- }- mr->value = JS_DupValue(ctx, value);- return JS_DupValue(ctx, this_val);-}--static JSValue js_map_get(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);- JSMapRecord *mr;- JSValueConst key;-- if (!s)- return JS_EXCEPTION;- key = map_normalize_key(ctx, argv[0]);- mr = map_find_record(ctx, s, key);- if (!mr)- return JS_UNDEFINED;- else- return JS_DupValue(ctx, mr->value);-}--static JSValue js_map_has(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);- JSMapRecord *mr;- JSValueConst key;-- if (!s)- return JS_EXCEPTION;- key = map_normalize_key(ctx, argv[0]);- mr = map_find_record(ctx, s, key);- return JS_NewBool(ctx, (mr != NULL));-}--static JSValue js_map_delete(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);- JSMapRecord *mr;- JSValueConst key;-- if (!s)- return JS_EXCEPTION;- key = map_normalize_key(ctx, argv[0]);- mr = map_find_record(ctx, s, key);- if (!mr)- return JS_FALSE;- map_delete_record(ctx->rt, s, mr);- return JS_TRUE;-}--static JSValue js_map_clear(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);- struct list_head *el, *el1;- JSMapRecord *mr;-- if (!s)- return JS_EXCEPTION;- list_for_each_safe(el, el1, &s->records) {- mr = list_entry(el, JSMapRecord, link);- map_delete_record(ctx->rt, s, mr);- }- return JS_UNDEFINED;-}--static JSValue js_map_get_size(JSContext *ctx, JSValueConst this_val, int magic)-{- JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);- if (!s)- return JS_EXCEPTION;- return JS_NewUint32(ctx, s->record_count);-}--static JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);- JSValueConst func, this_arg;- JSValue ret, args[3];- struct list_head *el;- JSMapRecord *mr;-- if (!s)- return JS_EXCEPTION;- func = argv[0];- if (argc > 1)- this_arg = argv[1];- else- this_arg = JS_UNDEFINED;- if (check_function(ctx, func))- return JS_EXCEPTION;- /* Note: the list can be modified while traversing it, but the- current element is locked */- el = s->records.next;- while (el != &s->records) {- mr = list_entry(el, JSMapRecord, link);- if (!mr->empty) {- mr->ref_count++;- /* must duplicate in case the record is deleted */- args[1] = JS_DupValue(ctx, mr->key);- if (magic)- args[0] = args[1];- else- args[0] = JS_DupValue(ctx, mr->value);- args[2] = (JSValue)this_val;- ret = JS_Call(ctx, func, this_arg, 3, (JSValueConst *)args);- JS_FreeValue(ctx, args[0]);- if (!magic)- JS_FreeValue(ctx, args[1]);- el = el->next;- map_decref_record(ctx->rt, mr);- if (JS_IsException(ret))- return ret;- JS_FreeValue(ctx, ret);- } else {- el = el->next;- }- }- return JS_UNDEFINED;-}--static void js_map_finalizer(JSRuntime *rt, JSValue val)-{- JSObject *p;- JSMapState *s;- struct list_head *el, *el1;- JSMapRecord *mr;-- p = JS_VALUE_GET_OBJ(val);- s = p->u.map_state;- if (s) {- /* if the object is deleted we are sure that no iterator is- using it */- list_for_each_safe(el, el1, &s->records) {- mr = list_entry(el, JSMapRecord, link);- if (!mr->empty) {- if (s->is_weak)- delete_weak_ref(rt, mr);- else- JS_FreeValueRT(rt, mr->key);- JS_FreeValueRT(rt, mr->value);- }- js_free_rt(rt, mr);- }- js_free_rt(rt, s->hash_table);- js_free_rt(rt, s);- }-}--static void js_map_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)-{- JSObject *p = JS_VALUE_GET_OBJ(val);- JSMapState *s;- struct list_head *el;- JSMapRecord *mr;-- s = p->u.map_state;- if (s) {- list_for_each(el, &s->records) {- mr = list_entry(el, JSMapRecord, link);- if (!s->is_weak)- JS_MarkValue(rt, mr->key, mark_func);- JS_MarkValue(rt, mr->value, mark_func);- }- }-}--/* Map Iterator */--typedef struct JSMapIteratorData {- JSValue obj;- JSIteratorKindEnum kind;- JSMapRecord *cur_record;-} JSMapIteratorData;--static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val)-{- JSObject *p;- JSMapIteratorData *it;-- p = JS_VALUE_GET_OBJ(val);- it = p->u.map_iterator_data;- if (it) {- /* During the GC sweep phase the Map finalizer may be- called before the Map iterator finalizer */- if (JS_IsLiveObject(rt, it->obj) && it->cur_record) {- map_decref_record(rt, it->cur_record);- }- JS_FreeValueRT(rt, it->obj);- js_free_rt(rt, it);- }-}--static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func)-{- JSObject *p = JS_VALUE_GET_OBJ(val);- JSMapIteratorData *it;- it = p->u.map_iterator_data;- if (it) {- /* the record is already marked by the object */- JS_MarkValue(rt, it->obj, mark_func);- }-}--static JSValue js_create_map_iterator(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- JSIteratorKindEnum kind;- JSMapState *s;- JSMapIteratorData *it;- JSValue enum_obj;-- kind = magic >> 2;- magic &= 3;- s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);- if (!s)- return JS_EXCEPTION;- enum_obj = JS_NewObjectClass(ctx, JS_CLASS_MAP_ITERATOR + magic);- if (JS_IsException(enum_obj))- goto fail;- it = js_malloc(ctx, sizeof(*it));- if (!it) {- JS_FreeValue(ctx, enum_obj);- goto fail;- }- it->obj = JS_DupValue(ctx, this_val);- it->kind = kind;- it->cur_record = NULL;- JS_SetOpaque(enum_obj, it);- return enum_obj;- fail:- return JS_EXCEPTION;-}--static JSValue js_map_iterator_next(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv,- BOOL *pdone, int magic)-{- JSMapIteratorData *it;- JSMapState *s;- JSMapRecord *mr;- struct list_head *el;-- it = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP_ITERATOR + magic);- if (!it) {- *pdone = FALSE;- return JS_EXCEPTION;- }- if (JS_IsUndefined(it->obj))- goto done;- s = JS_GetOpaque(it->obj, JS_CLASS_MAP + magic);- assert(s != NULL);- if (!it->cur_record) {- el = s->records.next;- } else {- mr = it->cur_record;- el = mr->link.next;- map_decref_record(ctx->rt, mr); /* the record can be freed here */- }- for(;;) {- if (el == &s->records) {- /* no more record */- it->cur_record = NULL;- JS_FreeValue(ctx, it->obj);- it->obj = JS_UNDEFINED;- done:- /* end of enumeration */- *pdone = TRUE;- return JS_UNDEFINED;- }- mr = list_entry(el, JSMapRecord, link);- if (!mr->empty)- break;- /* get the next record */- el = mr->link.next;- }-- /* lock the record so that it won't be freed */- mr->ref_count++;- it->cur_record = mr;- *pdone = FALSE;-- if (it->kind == JS_ITERATOR_KIND_KEY) {- return JS_DupValue(ctx, mr->key);- } else {- JSValueConst args[2];- args[0] = mr->key;- if (magic)- args[1] = mr->key;- else- args[1] = mr->value;- if (it->kind == JS_ITERATOR_KIND_VALUE) {- return JS_DupValue(ctx, args[1]);- } else {- return js_create_array(ctx, 2, args);- }- }-}--static const JSCFunctionListEntry js_map_funcs[] = {- JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),-};--static const JSCFunctionListEntry js_map_proto_funcs[] = {- JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, 0 ),- JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, 0 ),- JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, 0 ),- JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, 0 ),- JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, 0 ),- JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, 0),- JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, 0 ),- JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_VALUE << 2) | 0 ),- JS_CFUNC_MAGIC_DEF("keys", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | 0 ),- JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | 0 ),- JS_ALIAS_DEF("[Symbol.iterator]", "entries" ),- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map", JS_PROP_CONFIGURABLE ),-};--static const JSCFunctionListEntry js_map_iterator_proto_funcs[] = {- JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, 0 ),- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map Iterator", JS_PROP_CONFIGURABLE ),-};--static const JSCFunctionListEntry js_set_proto_funcs[] = {- JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET ),- JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET ),- JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET ),- JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, MAGIC_SET ),- JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, MAGIC_SET ),- JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, MAGIC_SET ),- JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | MAGIC_SET ),- JS_ALIAS_DEF("keys", "values" ),- JS_ALIAS_DEF("[Symbol.iterator]", "values" ),- JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | MAGIC_SET ),- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set", JS_PROP_CONFIGURABLE ),-};--static const JSCFunctionListEntry js_set_iterator_proto_funcs[] = {- JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, MAGIC_SET ),- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set Iterator", JS_PROP_CONFIGURABLE ),-};--static const JSCFunctionListEntry js_weak_map_proto_funcs[] = {- JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, MAGIC_WEAK ),- JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, MAGIC_WEAK ),- JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_WEAK ),- JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_WEAK ),- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakMap", JS_PROP_CONFIGURABLE ),-};--static const JSCFunctionListEntry js_weak_set_proto_funcs[] = {- JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET | MAGIC_WEAK ),- JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET | MAGIC_WEAK ),- JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET | MAGIC_WEAK ),- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakSet", JS_PROP_CONFIGURABLE ),-};--static const JSCFunctionListEntry * const js_map_proto_funcs_ptr[6] = {- js_map_proto_funcs,- js_set_proto_funcs,- js_weak_map_proto_funcs,- js_weak_set_proto_funcs,- js_map_iterator_proto_funcs,- js_set_iterator_proto_funcs,-};--static const uint8_t js_map_proto_funcs_count[6] = {- countof(js_map_proto_funcs),- countof(js_set_proto_funcs),- countof(js_weak_map_proto_funcs),- countof(js_weak_set_proto_funcs),- countof(js_map_iterator_proto_funcs),- countof(js_set_iterator_proto_funcs),-};--void JS_AddIntrinsicMapSet(JSContext *ctx)-{- int i;- JSValue obj1;- char buf[ATOM_GET_STR_BUF_SIZE];-- for(i = 0; i < 4; i++) {- const char *name = JS_AtomGetStr(ctx, buf, sizeof(buf),- JS_ATOM_Map + i);- ctx->class_proto[JS_CLASS_MAP + i] = JS_NewObject(ctx);- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP + i],- js_map_proto_funcs_ptr[i],- js_map_proto_funcs_count[i]);- obj1 = JS_NewCFunctionMagic(ctx, js_map_constructor, name, 0,- JS_CFUNC_constructor_magic, i);- if (i < 2) {- JS_SetPropertyFunctionList(ctx, obj1, js_map_funcs,- countof(js_map_funcs));- }- JS_NewGlobalCConstructor2(ctx, obj1, name, ctx->class_proto[JS_CLASS_MAP + i]);- }-- for(i = 0; i < 2; i++) {- ctx->class_proto[JS_CLASS_MAP_ITERATOR + i] =- JS_NewObjectProto(ctx, ctx->iterator_proto);- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP_ITERATOR + i],- js_map_proto_funcs_ptr[i + 4],- js_map_proto_funcs_count[i + 4]);- }-}--/* Generator */-static const JSCFunctionListEntry js_generator_function_proto_funcs[] = {- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "GeneratorFunction", JS_PROP_CONFIGURABLE),-};--static const JSCFunctionListEntry js_generator_proto_funcs[] = {- JS_ITERATOR_NEXT_DEF("next", 1, js_generator_next, GEN_MAGIC_NEXT ),- JS_ITERATOR_NEXT_DEF("return", 1, js_generator_next, GEN_MAGIC_RETURN ),- JS_ITERATOR_NEXT_DEF("throw", 1, js_generator_next, GEN_MAGIC_THROW ),- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Generator", JS_PROP_CONFIGURABLE),-};--/* Promise */--typedef enum JSPromiseStateEnum {- JS_PROMISE_PENDING,- JS_PROMISE_FULFILLED,- JS_PROMISE_REJECTED,-} JSPromiseStateEnum;--typedef struct JSPromiseData {- JSPromiseStateEnum promise_state;- /* 0=fulfill, 1=reject, list of JSPromiseReactionData.link */- struct list_head promise_reactions[2];- BOOL is_handled; /* Note: only useful to debug */- JSValue promise_result;-} JSPromiseData;--typedef struct JSPromiseFunctionDataResolved {- int ref_count;- BOOL already_resolved;-} JSPromiseFunctionDataResolved;--typedef struct JSPromiseFunctionData {- JSValue promise;- JSPromiseFunctionDataResolved *presolved;-} JSPromiseFunctionData;--typedef struct JSPromiseReactionData {- struct list_head link; /* not used in promise_reaction_job */- JSValue resolving_funcs[2];- JSValue handler;-} JSPromiseReactionData;--static int js_create_resolving_functions(JSContext *ctx, JSValue *args,- JSValueConst promise);--static void promise_reaction_data_free(JSRuntime *rt,- JSPromiseReactionData *rd)-{- JS_FreeValueRT(rt, rd->resolving_funcs[0]);- JS_FreeValueRT(rt, rd->resolving_funcs[1]);- JS_FreeValueRT(rt, rd->handler);- js_free_rt(rt, rd);-}--static JSValue promise_reaction_job(JSContext *ctx, int argc,- JSValueConst *argv)-{- JSValueConst handler, arg, func;- JSValue res, res2;- BOOL is_reject;-- assert(argc == 5);- handler = argv[2];- is_reject = JS_ToBool(ctx, argv[3]);- arg = argv[4];-#ifdef DUMP_PROMISE- printf("promise_reaction_job: is_reject=%d\n", is_reject);-#endif-- if (JS_IsUndefined(handler)) {- if (is_reject) {- res = JS_Throw(ctx, JS_DupValue(ctx, arg));- } else {- res = JS_DupValue(ctx, arg);- }- } else {- res = JS_Call(ctx, handler, JS_UNDEFINED, 1, &arg);- }- is_reject = JS_IsException(res);- if (is_reject)- res = JS_GetException(ctx);- func = argv[is_reject];- /* as an extension, we support undefined as value to avoid- creating a dummy promise in the 'await' implementation of async- functions */- if (!JS_IsUndefined(func)) {- res2 = JS_Call(ctx, func, JS_UNDEFINED,- 1, (JSValueConst *)&res);- } else {- res2 = JS_UNDEFINED;- }- JS_FreeValue(ctx, res);-- return res2;-}--void JS_SetHostPromiseRejectionTracker(JSRuntime *rt,- JSHostPromiseRejectionTracker *cb,- void *opaque)-{- rt->host_promise_rejection_tracker = cb;- rt->host_promise_rejection_tracker_opaque = opaque;-}--static void fulfill_or_reject_promise(JSContext *ctx, JSValueConst promise,- JSValueConst value, BOOL is_reject)-{- JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);- struct list_head *el, *el1;- JSPromiseReactionData *rd;- JSValueConst args[5];-- if (!s || s->promise_state != JS_PROMISE_PENDING)- return; /* should never happen */- set_value(ctx, &s->promise_result, JS_DupValue(ctx, value));- s->promise_state = JS_PROMISE_FULFILLED + is_reject;-#ifdef DUMP_PROMISE- printf("fulfill_or_reject_promise: is_reject=%d\n", is_reject);-#endif- if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) {- JSRuntime *rt = ctx->rt;- if (rt->host_promise_rejection_tracker) {- rt->host_promise_rejection_tracker(ctx, promise, value, FALSE,- rt->host_promise_rejection_tracker_opaque);- }- }-- list_for_each_safe(el, el1, &s->promise_reactions[is_reject]) {- rd = list_entry(el, JSPromiseReactionData, link);- args[0] = rd->resolving_funcs[0];- args[1] = rd->resolving_funcs[1];- args[2] = rd->handler;- args[3] = JS_NewBool(ctx, is_reject);- args[4] = value;- JS_EnqueueJob(ctx, promise_reaction_job, 5, args);- list_del(&rd->link);- promise_reaction_data_free(ctx->rt, rd);- }-- list_for_each_safe(el, el1, &s->promise_reactions[1 - is_reject]) {- rd = list_entry(el, JSPromiseReactionData, link);- list_del(&rd->link);- promise_reaction_data_free(ctx->rt, rd);- }-}--static void reject_promise(JSContext *ctx, JSValueConst promise,- JSValueConst value)-{- fulfill_or_reject_promise(ctx, promise, value, TRUE);-}--static JSValue js_promise_resolve_thenable_job(JSContext *ctx,- int argc, JSValueConst *argv)-{- JSValueConst promise, thenable, then;- JSValue args[2], res;--#ifdef DUMP_PROMISE- printf("js_promise_resolve_thenable_job\n");-#endif- assert(argc == 3);- promise = argv[0];- thenable = argv[1];- then = argv[2];- if (js_create_resolving_functions(ctx, args, promise) < 0)- return JS_EXCEPTION;- res = JS_Call(ctx, then, thenable, 2, (JSValueConst *)args);- if (JS_IsException(res)) {- JSValue error = JS_GetException(ctx);- res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error);- JS_FreeValue(ctx, error);- }- JS_FreeValue(ctx, args[0]);- JS_FreeValue(ctx, args[1]);- return res;-}--static void js_promise_resolve_function_free_resolved(JSRuntime *rt,- JSPromiseFunctionDataResolved *sr)-{- if (--sr->ref_count == 0) {- js_free_rt(rt, sr);- }-}--static int js_create_resolving_functions(JSContext *ctx,- JSValue *resolving_funcs,- JSValueConst promise)--{- JSValue obj;- JSPromiseFunctionData *s;- JSPromiseFunctionDataResolved *sr;- int i, ret;-- sr = js_malloc(ctx, sizeof(*sr));- if (!sr)- return -1;- sr->ref_count = 1;- sr->already_resolved = FALSE; /* must be shared between the two functions */- ret = 0;- for(i = 0; i < 2; i++) {- obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,- JS_CLASS_PROMISE_RESOLVE_FUNCTION + i);- if (JS_IsException(obj))- goto fail;- s = js_malloc(ctx, sizeof(*s));- if (!s) {- JS_FreeValue(ctx, obj);- fail:-- if (i != 0)- JS_FreeValue(ctx, resolving_funcs[0]);- ret = -1;- break;- }- sr->ref_count++;- s->presolved = sr;- s->promise = JS_DupValue(ctx, promise);- JS_SetOpaque(obj, s);- js_function_set_properties(ctx, obj, JS_ATOM_empty_string, 1);- resolving_funcs[i] = obj;- }- js_promise_resolve_function_free_resolved(ctx->rt, sr);- return ret;-}--static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val)-{- JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data;- if (s) {- js_promise_resolve_function_free_resolved(rt, s->presolved);- JS_FreeValueRT(rt, s->promise);- js_free_rt(rt, s);- }-}--static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func)-{- JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data;- if (s) {- JS_MarkValue(rt, s->promise, mark_func);- }-}--static JSValue js_promise_resolve_function_call(JSContext *ctx,- JSValueConst func_obj,- JSValueConst this_val,- int argc, JSValueConst *argv,- int flags)-{- JSObject *p = JS_VALUE_GET_OBJ(func_obj);- JSPromiseFunctionData *s;- JSValueConst resolution, args[3];- JSValue then;- BOOL is_reject;-- s = p->u.promise_function_data;- if (!s || s->presolved->already_resolved)- return JS_UNDEFINED;- s->presolved->already_resolved = TRUE;- is_reject = p->class_id - JS_CLASS_PROMISE_RESOLVE_FUNCTION;- if (argc > 0)- resolution = argv[0];- else- resolution = JS_UNDEFINED;-#ifdef DUMP_PROMISE- printf("js_promise_resolving_function_call: is_reject=%d resolution=", is_reject);- JS_DumpValue(ctx, resolution);- printf("\n");-#endif- if (is_reject || !JS_IsObject(resolution)) {- goto done;- } else if (js_same_value(ctx, resolution, s->promise)) {- JS_ThrowTypeError(ctx, "promise self resolution");- goto fail_reject;- }- then = JS_GetProperty(ctx, resolution, JS_ATOM_then);- if (JS_IsException(then)) {- JSValue error;- fail_reject:- error = JS_GetException(ctx);- reject_promise(ctx, s->promise, error);- JS_FreeValue(ctx, error);- } else if (!JS_IsFunction(ctx, then)) {- JS_FreeValue(ctx, then);- done:- fulfill_or_reject_promise(ctx, s->promise, resolution, is_reject);- } else {- args[0] = s->promise;- args[1] = resolution;- args[2] = then;- JS_EnqueueJob(ctx, js_promise_resolve_thenable_job, 3, args);- JS_FreeValue(ctx, then);- }- return JS_UNDEFINED;-}--static void js_promise_finalizer(JSRuntime *rt, JSValue val)-{- JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE);- struct list_head *el, *el1;- int i;-- if (!s)- return;- for(i = 0; i < 2; i++) {- list_for_each_safe(el, el1, &s->promise_reactions[i]) {- JSPromiseReactionData *rd =- list_entry(el, JSPromiseReactionData, link);- promise_reaction_data_free(rt, rd);- }- }- JS_FreeValueRT(rt, s->promise_result);- js_free_rt(rt, s);-}--static void js_promise_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func)-{- JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE);- struct list_head *el;- int i;-- if (!s)- return;- for(i = 0; i < 2; i++) {- list_for_each(el, &s->promise_reactions[i]) {- JSPromiseReactionData *rd =- list_entry(el, JSPromiseReactionData, link);- JS_MarkValue(rt, rd->resolving_funcs[0], mark_func);- JS_MarkValue(rt, rd->resolving_funcs[1], mark_func);- JS_MarkValue(rt, rd->handler, mark_func);- }- }- JS_MarkValue(rt, s->promise_result, mark_func);-}--static JSValue js_promise_constructor(JSContext *ctx, JSValueConst new_target,- int argc, JSValueConst *argv)-{- JSValueConst executor;- JSValue obj;- JSPromiseData *s;- JSValue args[2], ret;- int i;-- executor = argv[0];- if (check_function(ctx, executor))- return JS_EXCEPTION;- obj = js_create_from_ctor(ctx, new_target, JS_CLASS_PROMISE);- if (JS_IsException(obj))- return JS_EXCEPTION;- s = js_mallocz(ctx, sizeof(*s));- if (!s)- goto fail;- s->promise_state = JS_PROMISE_PENDING;- s->is_handled = FALSE;- for(i = 0; i < 2; i++)- init_list_head(&s->promise_reactions[i]);- s->promise_result = JS_UNDEFINED;- JS_SetOpaque(obj, s);- if (js_create_resolving_functions(ctx, args, obj))- goto fail;- ret = JS_Call(ctx, executor, JS_UNDEFINED, 2, (JSValueConst *)args);- if (JS_IsException(ret)) {- JSValue ret2, error;- error = JS_GetException(ctx);- ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error);- JS_FreeValue(ctx, error);- if (JS_IsException(ret2))- goto fail1;- JS_FreeValue(ctx, ret2);- }- JS_FreeValue(ctx, ret);- JS_FreeValue(ctx, args[0]);- JS_FreeValue(ctx, args[1]);- return obj;- fail1:- JS_FreeValue(ctx, args[0]);- JS_FreeValue(ctx, args[1]);- fail:- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;-}--static JSValue js_promise_executor(JSContext *ctx,- JSValueConst this_val,- int argc, JSValueConst *argv,- int magic, JSValue *func_data)-{- int i;-- for(i = 0; i < 2; i++) {- if (!JS_IsUndefined(func_data[i]))- return JS_ThrowTypeError(ctx, "resolving function already set");- func_data[i] = JS_DupValue(ctx, argv[i]);- }- return JS_UNDEFINED;-}--static JSValue js_promise_executor_new(JSContext *ctx)-{- JSValueConst func_data[2];-- func_data[0] = JS_UNDEFINED;- func_data[1] = JS_UNDEFINED;- return JS_NewCFunctionData(ctx, js_promise_executor, 2,- 0, 2, func_data);-}--static JSValue js_new_promise_capability(JSContext *ctx,- JSValue *resolving_funcs,- JSValueConst ctor)-{- JSValue executor, result_promise;- JSCFunctionDataRecord *s;- int i;-- executor = js_promise_executor_new(ctx);- if (JS_IsException(executor))- return executor;-- if (JS_IsUndefined(ctor)) {- result_promise = js_promise_constructor(ctx, ctor, 1,- (JSValueConst *)&executor);- } else {- result_promise = JS_CallConstructor(ctx, ctor, 1,- (JSValueConst *)&executor);- }- if (JS_IsException(result_promise))- goto fail;- s = JS_GetOpaque(executor, JS_CLASS_C_FUNCTION_DATA);- for(i = 0; i < 2; i++) {- if (check_function(ctx, s->data[i]))- goto fail;- }- for(i = 0; i < 2; i++)- resolving_funcs[i] = JS_DupValue(ctx, s->data[i]);- JS_FreeValue(ctx, executor);- return result_promise;- fail:- JS_FreeValue(ctx, executor);- JS_FreeValue(ctx, result_promise);- return JS_EXCEPTION;-}--JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs)-{- return js_new_promise_capability(ctx, resolving_funcs, JS_UNDEFINED);-}--static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- JSValue result_promise, resolving_funcs[2], ret;- BOOL is_reject = magic;-- if (!JS_IsObject(this_val))- return JS_ThrowTypeErrorNotAnObject(ctx);- if (!is_reject && JS_GetOpaque(argv[0], JS_CLASS_PROMISE)) {- JSValue ctor;- BOOL is_same;- ctor = JS_GetProperty(ctx, argv[0], JS_ATOM_constructor);- if (JS_IsException(ctor))- return ctor;- is_same = js_same_value(ctx, ctor, this_val);- JS_FreeValue(ctx, ctor);- if (is_same)- return JS_DupValue(ctx, argv[0]);- }- result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);- if (JS_IsException(result_promise))- return result_promise;- ret = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 1, argv);- JS_FreeValue(ctx, resolving_funcs[0]);- JS_FreeValue(ctx, resolving_funcs[1]);- if (JS_IsException(ret)) {- JS_FreeValue(ctx, result_promise);- return ret;- }- JS_FreeValue(ctx, ret);- return result_promise;-}--#if 0-static JSValue js_promise___newPromiseCapability(JSContext *ctx,- JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue result_promise, resolving_funcs[2], obj;- JSValueConst ctor;- ctor = argv[0];- if (!JS_IsObject(ctor))- return JS_ThrowTypeErrorNotAnObject(ctx);- result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor);- if (JS_IsException(result_promise))- return result_promise;- obj = JS_NewObject(ctx);- if (JS_IsException(obj)) {- JS_FreeValue(ctx, resolving_funcs[0]);- JS_FreeValue(ctx, resolving_funcs[1]);- JS_FreeValue(ctx, result_promise);- return JS_EXCEPTION;- }- JS_DefinePropertyValue(ctx, obj, JS_ATOM_promise, result_promise, JS_PROP_C_W_E);- JS_DefinePropertyValue(ctx, obj, JS_ATOM_resolve, resolving_funcs[0], JS_PROP_C_W_E);- JS_DefinePropertyValue(ctx, obj, JS_ATOM_reject, resolving_funcs[1], JS_PROP_C_W_E);- return obj;-}-#endif--static __exception int remainingElementsCount_add(JSContext *ctx,- JSValueConst resolve_element_env,- int addend)-{- JSValue val;- int remainingElementsCount;-- val = JS_GetPropertyUint32(ctx, resolve_element_env, 0);- if (JS_IsException(val))- return -1;- if (JS_ToInt32Free(ctx, &remainingElementsCount, val))- return -1;- remainingElementsCount += addend;- if (JS_SetPropertyUint32(ctx, resolve_element_env, 0,- JS_NewInt32(ctx, remainingElementsCount)) < 0)- return -1;- return (remainingElementsCount == 0);-}--#define PROMISE_MAGIC_all 0-#define PROMISE_MAGIC_allSettled 1-#define PROMISE_MAGIC_any 2--static JSValue js_promise_all_resolve_element(JSContext *ctx,- JSValueConst this_val,- int argc, JSValueConst *argv,- int magic,- JSValue *func_data)-{- int resolve_type = magic & 3;- int is_reject = magic & 4;- BOOL alreadyCalled = JS_ToBool(ctx, func_data[0]);- JSValueConst values = func_data[2];- JSValueConst resolve = func_data[3];- JSValueConst resolve_element_env = func_data[4];- JSValue ret, obj;- int is_zero, index;- - if (JS_ToInt32(ctx, &index, func_data[1]))- return JS_EXCEPTION;- if (alreadyCalled)- return JS_UNDEFINED;- func_data[0] = JS_NewBool(ctx, TRUE);-- if (resolve_type == PROMISE_MAGIC_allSettled) {- JSValue str;- - obj = JS_NewObject(ctx);- if (JS_IsException(obj))- return JS_EXCEPTION;- str = JS_NewString(ctx, is_reject ? "rejected" : "fulfilled");- if (JS_IsException(str))- goto fail1;- if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_status,- str,- JS_PROP_C_W_E) < 0)- goto fail1;- if (JS_DefinePropertyValue(ctx, obj,- is_reject ? JS_ATOM_reason : JS_ATOM_value,- JS_DupValue(ctx, argv[0]),- JS_PROP_C_W_E) < 0) {- fail1:- JS_FreeValue(ctx, obj);- return JS_EXCEPTION;- }- } else {- obj = JS_DupValue(ctx, argv[0]);- }- if (JS_DefinePropertyValueUint32(ctx, values, index,- obj, JS_PROP_C_W_E) < 0)- return JS_EXCEPTION;- - is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1);- if (is_zero < 0)- return JS_EXCEPTION;- if (is_zero) {- if (resolve_type == PROMISE_MAGIC_any) {- JSValue error;- error = js_aggregate_error_constructor(ctx, values);- if (JS_IsException(error))- return JS_EXCEPTION;- ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&error);- JS_FreeValue(ctx, error);- } else {- ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&values);- }- if (JS_IsException(ret))- return ret;- JS_FreeValue(ctx, ret);- }- return JS_UNDEFINED;-}--/* magic = 0: Promise.all 1: Promise.allSettled */-static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- JSValue result_promise, resolving_funcs[2], item, next_promise, ret;- JSValue next_method = JS_UNDEFINED, values = JS_UNDEFINED;- JSValue resolve_element_env = JS_UNDEFINED, resolve_element, reject_element;- JSValue promise_resolve = JS_UNDEFINED, iter = JS_UNDEFINED;- JSValueConst then_args[2], resolve_element_data[5];- BOOL done;- int index, is_zero, is_promise_any = (magic == PROMISE_MAGIC_any);- - if (!JS_IsObject(this_val))- return JS_ThrowTypeErrorNotAnObject(ctx);- result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);- if (JS_IsException(result_promise))- return result_promise;- promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve);- if (JS_IsException(promise_resolve) ||- check_function(ctx, promise_resolve))- goto fail_reject;- iter = JS_GetIterator(ctx, argv[0], FALSE);- if (JS_IsException(iter)) {- JSValue error;- fail_reject:- error = JS_GetException(ctx);- ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1,- (JSValueConst *)&error);- JS_FreeValue(ctx, error);- if (JS_IsException(ret))- goto fail;- JS_FreeValue(ctx, ret);- } else {- next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);- if (JS_IsException(next_method))- goto fail_reject;- values = JS_NewArray(ctx);- if (JS_IsException(values))- goto fail_reject;- resolve_element_env = JS_NewArray(ctx);- if (JS_IsException(resolve_element_env))- goto fail_reject;- /* remainingElementsCount field */- if (JS_DefinePropertyValueUint32(ctx, resolve_element_env, 0,- JS_NewInt32(ctx, 1),- JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0)- goto fail_reject;- - index = 0;- for(;;) {- /* XXX: conformance: should close the iterator if error on 'done'- access, but not on 'value' access */- item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);- if (JS_IsException(item))- goto fail_reject;- if (done)- break;- next_promise = JS_Call(ctx, promise_resolve, - this_val, 1, (JSValueConst *)&item);- JS_FreeValue(ctx, item);- if (JS_IsException(next_promise)) {- fail_reject1:- JS_IteratorClose(ctx, iter, TRUE);- goto fail_reject;- }- resolve_element_data[0] = JS_NewBool(ctx, FALSE);- resolve_element_data[1] = (JSValueConst)JS_NewInt32(ctx, index);- resolve_element_data[2] = values;- resolve_element_data[3] = resolving_funcs[is_promise_any];- resolve_element_data[4] = resolve_element_env;- resolve_element =- JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1,- magic, 5, resolve_element_data);- if (JS_IsException(resolve_element)) {- JS_FreeValue(ctx, next_promise);- goto fail_reject1;- }- - if (magic == PROMISE_MAGIC_allSettled) {- reject_element =- JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1,- magic | 4, 5, resolve_element_data);- if (JS_IsException(reject_element)) {- JS_FreeValue(ctx, next_promise);- goto fail_reject1;- }- } else if (magic == PROMISE_MAGIC_any) {- if (JS_DefinePropertyValueUint32(ctx, values, index,- JS_UNDEFINED, JS_PROP_C_W_E) < 0)- goto fail_reject1;- reject_element = resolve_element;- resolve_element = JS_DupValue(ctx, resolving_funcs[0]);- } else {- reject_element = JS_DupValue(ctx, resolving_funcs[1]);- }-- if (remainingElementsCount_add(ctx, resolve_element_env, 1) < 0) {- JS_FreeValue(ctx, next_promise);- JS_FreeValue(ctx, resolve_element);- JS_FreeValue(ctx, reject_element);- goto fail_reject1;- }-- then_args[0] = resolve_element;- then_args[1] = reject_element;- ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, then_args);- JS_FreeValue(ctx, resolve_element);- JS_FreeValue(ctx, reject_element);- if (check_exception_free(ctx, ret))- goto fail_reject1;- index++;- }-- is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1);- if (is_zero < 0)- goto fail_reject;- if (is_zero) {- if (magic == PROMISE_MAGIC_any) {- JSValue error;- error = js_aggregate_error_constructor(ctx, values);- if (JS_IsException(error))- goto fail_reject;- JS_FreeValue(ctx, values);- values = error;- }- ret = JS_Call(ctx, resolving_funcs[is_promise_any], JS_UNDEFINED,- 1, (JSValueConst *)&values);- if (check_exception_free(ctx, ret))- goto fail_reject;- }- }- done:- JS_FreeValue(ctx, promise_resolve);- JS_FreeValue(ctx, resolve_element_env);- JS_FreeValue(ctx, values);- JS_FreeValue(ctx, next_method);- JS_FreeValue(ctx, iter);- JS_FreeValue(ctx, resolving_funcs[0]);- JS_FreeValue(ctx, resolving_funcs[1]);- return result_promise;- fail:- JS_FreeValue(ctx, result_promise);- result_promise = JS_EXCEPTION;- goto done;-}--static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue result_promise, resolving_funcs[2], item, next_promise, ret;- JSValue next_method = JS_UNDEFINED, iter = JS_UNDEFINED;- JSValue promise_resolve = JS_UNDEFINED;- BOOL done;-- if (!JS_IsObject(this_val))- return JS_ThrowTypeErrorNotAnObject(ctx);- result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);- if (JS_IsException(result_promise))- return result_promise;- promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve);- if (JS_IsException(promise_resolve) ||- check_function(ctx, promise_resolve))- goto fail_reject;- iter = JS_GetIterator(ctx, argv[0], FALSE);- if (JS_IsException(iter)) {- JSValue error;- fail_reject:- error = JS_GetException(ctx);- ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1,- (JSValueConst *)&error);- JS_FreeValue(ctx, error);- if (JS_IsException(ret))- goto fail;- JS_FreeValue(ctx, ret);- } else {- next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);- if (JS_IsException(next_method))- goto fail_reject;-- for(;;) {- /* XXX: conformance: should close the iterator if error on 'done'- access, but not on 'value' access */- item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);- if (JS_IsException(item))- goto fail_reject;- if (done)- break;- next_promise = JS_Call(ctx, promise_resolve,- this_val, 1, (JSValueConst *)&item);- JS_FreeValue(ctx, item);- if (JS_IsException(next_promise)) {- fail_reject1:- JS_IteratorClose(ctx, iter, TRUE);- goto fail_reject;- }- ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2,- (JSValueConst *)resolving_funcs);- if (check_exception_free(ctx, ret))- goto fail_reject1;- }- }- done:- JS_FreeValue(ctx, promise_resolve);- JS_FreeValue(ctx, next_method);- JS_FreeValue(ctx, iter);- JS_FreeValue(ctx, resolving_funcs[0]);- JS_FreeValue(ctx, resolving_funcs[1]);- return result_promise;- fail:- //JS_FreeValue(ctx, next_method); // why not???- JS_FreeValue(ctx, result_promise);- result_promise = JS_EXCEPTION;- goto done;-}--static __exception int perform_promise_then(JSContext *ctx,- JSValueConst promise,- JSValueConst *resolve_reject,- JSValueConst *cap_resolving_funcs)-{- JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);- JSPromiseReactionData *rd_array[2], *rd;- int i, j;-- rd_array[0] = NULL;- rd_array[1] = NULL;- for(i = 0; i < 2; i++) {- JSValueConst handler;- rd = js_mallocz(ctx, sizeof(*rd));- if (!rd) {- if (i == 1)- promise_reaction_data_free(ctx->rt, rd_array[0]);- return -1;- }- for(j = 0; j < 2; j++)- rd->resolving_funcs[j] = JS_DupValue(ctx, cap_resolving_funcs[j]);- handler = resolve_reject[i];- if (!JS_IsFunction(ctx, handler))- handler = JS_UNDEFINED;- rd->handler = JS_DupValue(ctx, handler);- rd_array[i] = rd;- }-- if (s->promise_state == JS_PROMISE_PENDING) {- for(i = 0; i < 2; i++)- list_add_tail(&rd_array[i]->link, &s->promise_reactions[i]);- } else {- JSValueConst args[5];- if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) {- JSRuntime *rt = ctx->rt;- if (rt->host_promise_rejection_tracker) {- rt->host_promise_rejection_tracker(ctx, promise, s->promise_result,- TRUE, rt->host_promise_rejection_tracker_opaque);- }- }- i = s->promise_state - JS_PROMISE_FULFILLED;- rd = rd_array[i];- args[0] = rd->resolving_funcs[0];- args[1] = rd->resolving_funcs[1];- args[2] = rd->handler;- args[3] = JS_NewBool(ctx, i);- args[4] = s->promise_result;- JS_EnqueueJob(ctx, promise_reaction_job, 5, args);- for(i = 0; i < 2; i++)- promise_reaction_data_free(ctx->rt, rd_array[i]);- }- s->is_handled = TRUE;- return 0;-}--static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue ctor, result_promise, resolving_funcs[2];- JSPromiseData *s;- int i, ret;-- s = JS_GetOpaque2(ctx, this_val, JS_CLASS_PROMISE);- if (!s)- return JS_EXCEPTION;-- ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);- if (JS_IsException(ctor))- return ctor;- result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor);- JS_FreeValue(ctx, ctor);- if (JS_IsException(result_promise))- return result_promise;- ret = perform_promise_then(ctx, this_val, argv,- (JSValueConst *)resolving_funcs);- for(i = 0; i < 2; i++)- JS_FreeValue(ctx, resolving_funcs[i]);- if (ret) {- JS_FreeValue(ctx, result_promise);- return JS_EXCEPTION;- }- return result_promise;-}--static JSValue js_promise_catch(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValueConst args[2];- args[0] = JS_UNDEFINED;- args[1] = argv[0];- return JS_Invoke(ctx, this_val, JS_ATOM_then, 2, args);-}--static JSValue js_promise_finally_value_thunk(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv,- int magic, JSValue *func_data)-{- return JS_DupValue(ctx, func_data[0]);-}--static JSValue js_promise_finally_thrower(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv,- int magic, JSValue *func_data)-{- return JS_Throw(ctx, JS_DupValue(ctx, func_data[0]));-}--static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv,- int magic, JSValue *func_data)-{- JSValueConst ctor = func_data[0];- JSValueConst onFinally = func_data[1];- JSValue res, promise, resolving_funcs[2], ret, then_func;-- res = JS_Call(ctx, onFinally, JS_UNDEFINED, 0, NULL);- if (JS_IsException(res))- return res;- promise = js_new_promise_capability(ctx, resolving_funcs, ctor);- if (JS_IsException(promise)) {- JS_FreeValue(ctx, res);- return JS_EXCEPTION;- }- ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED,- 1, (JSValueConst*)&res);- JS_FreeValue(ctx, res);- JS_FreeValue(ctx, resolving_funcs[0]);- JS_FreeValue(ctx, resolving_funcs[1]);- if (JS_IsException(ret)) {- JS_FreeValue(ctx, promise);- return ret;- }- JS_FreeValue(ctx, ret);- if (magic == 0) {- then_func = JS_NewCFunctionData(ctx, js_promise_finally_value_thunk, 0,- 0, 1, argv);- } else {- then_func = JS_NewCFunctionData(ctx, js_promise_finally_thrower, 0,- 0, 1, argv);- }- if (JS_IsException(then_func)) {- JS_FreeValue(ctx, promise);- return then_func;- }- ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, (JSValueConst *)&then_func);- JS_FreeValue(ctx, then_func);- return ret;-}--static JSValue js_promise_finally(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValueConst onFinally = argv[0];- JSValue ctor, ret;- JSValue then_funcs[2];- JSValueConst func_data[2];- int i;-- ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);- if (JS_IsException(ctor))- return ctor;- if (!JS_IsFunction(ctx, onFinally)) {- then_funcs[0] = JS_DupValue(ctx, onFinally);- then_funcs[1] = JS_DupValue(ctx, onFinally);- } else {- func_data[0] = ctor;- func_data[1] = onFinally;- for(i = 0; i < 2; i++) {- then_funcs[i] = JS_NewCFunctionData(ctx, js_promise_then_finally_func, 1, i, 2, func_data);- if (JS_IsException(then_funcs[i])) {- if (i == 1)- JS_FreeValue(ctx, then_funcs[0]);- JS_FreeValue(ctx, ctor);- return JS_EXCEPTION;- }- }- }- JS_FreeValue(ctx, ctor);- ret = JS_Invoke(ctx, this_val, JS_ATOM_then, 2, (JSValueConst *)then_funcs);- JS_FreeValue(ctx, then_funcs[0]);- JS_FreeValue(ctx, then_funcs[1]);- return ret;-}--static const JSCFunctionListEntry js_promise_funcs[] = {- JS_CFUNC_MAGIC_DEF("resolve", 1, js_promise_resolve, 0 ),- JS_CFUNC_MAGIC_DEF("reject", 1, js_promise_resolve, 1 ),- JS_CFUNC_MAGIC_DEF("all", 1, js_promise_all, PROMISE_MAGIC_all ),- JS_CFUNC_MAGIC_DEF("allSettled", 1, js_promise_all, PROMISE_MAGIC_allSettled ),- JS_CFUNC_MAGIC_DEF("any", 1, js_promise_all, PROMISE_MAGIC_any ),- JS_CFUNC_DEF("race", 1, js_promise_race ),- //JS_CFUNC_DEF("__newPromiseCapability", 1, js_promise___newPromiseCapability ),- JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL),-};--static const JSCFunctionListEntry js_promise_proto_funcs[] = {- JS_CFUNC_DEF("then", 2, js_promise_then ),- JS_CFUNC_DEF("catch", 1, js_promise_catch ),- JS_CFUNC_DEF("finally", 1, js_promise_finally ),- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Promise", JS_PROP_CONFIGURABLE ),-};--/* AsyncFunction */-static const JSCFunctionListEntry js_async_function_proto_funcs[] = {- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncFunction", JS_PROP_CONFIGURABLE ),-};--static JSValue js_async_from_sync_iterator_unwrap(JSContext *ctx,- JSValueConst this_val,- int argc, JSValueConst *argv,- int magic, JSValue *func_data)-{- return js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]),- JS_ToBool(ctx, func_data[0]));-}--static JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx,- BOOL done)-{- JSValueConst func_data[1];-- func_data[0] = (JSValueConst)JS_NewBool(ctx, done);- return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_unwrap,- 1, 0, 1, func_data);-}--/* AsyncIteratorPrototype */--static const JSCFunctionListEntry js_async_iterator_proto_funcs[] = {- JS_CFUNC_DEF("[Symbol.asyncIterator]", 0, js_iterator_proto_iterator ),-};--/* AsyncFromSyncIteratorPrototype */--typedef struct JSAsyncFromSyncIteratorData {- JSValue sync_iter;- JSValue next_method;-} JSAsyncFromSyncIteratorData;--static void js_async_from_sync_iterator_finalizer(JSRuntime *rt, JSValue val)-{- JSAsyncFromSyncIteratorData *s =- JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);- if (s) {- JS_FreeValueRT(rt, s->sync_iter);- JS_FreeValueRT(rt, s->next_method);- js_free_rt(rt, s);- }-}--static void js_async_from_sync_iterator_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func)-{- JSAsyncFromSyncIteratorData *s =- JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);- if (s) {- JS_MarkValue(rt, s->sync_iter, mark_func);- JS_MarkValue(rt, s->next_method, mark_func);- }-}--static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx,- JSValueConst sync_iter)-{- JSValue async_iter, next_method;- JSAsyncFromSyncIteratorData *s;-- next_method = JS_GetProperty(ctx, sync_iter, JS_ATOM_next);- if (JS_IsException(next_method))- return JS_EXCEPTION;- async_iter = JS_NewObjectClass(ctx, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);- if (JS_IsException(async_iter)) {- JS_FreeValue(ctx, next_method);- return async_iter;- }- s = js_mallocz(ctx, sizeof(*s));- if (!s) {- JS_FreeValue(ctx, async_iter);- JS_FreeValue(ctx, next_method);- return JS_EXCEPTION;- }- s->sync_iter = JS_DupValue(ctx, sync_iter);- s->next_method = next_method;- JS_SetOpaque(async_iter, s);- return async_iter;-}--static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv,- int magic)-{- JSValue promise, resolving_funcs[2], value, err, method;- JSAsyncFromSyncIteratorData *s;- int done;- int is_reject;-- promise = JS_NewPromiseCapability(ctx, resolving_funcs);- if (JS_IsException(promise))- return JS_EXCEPTION;- s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);- if (!s) {- JS_ThrowTypeError(ctx, "not an Async-from-Sync Iterator");- goto reject;- }-- if (magic == GEN_MAGIC_NEXT) {- method = JS_DupValue(ctx, s->next_method);- } else {- method = JS_GetProperty(ctx, s->sync_iter,- magic == GEN_MAGIC_RETURN ? JS_ATOM_return :- JS_ATOM_throw);- if (JS_IsException(method))- goto reject;- if (JS_IsUndefined(method) || JS_IsNull(method)) {- if (magic == GEN_MAGIC_RETURN) {- err = js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), TRUE);- is_reject = 0;- } else {- err = JS_DupValue(ctx, argv[0]);- is_reject = 1;- }- goto done_resolve;- }- }- value = JS_IteratorNext2(ctx, s->sync_iter, method,- argc >= 1 ? 1 : 0, argv, &done);- JS_FreeValue(ctx, method);- if (JS_IsException(value))- goto reject;- if (done == 2) {- JSValue obj = value;- value = JS_IteratorGetCompleteValue(ctx, obj, &done);- JS_FreeValue(ctx, obj);- if (JS_IsException(value))- goto reject;- }-- if (JS_IsException(value)) {- JSValue res2;- reject:- err = JS_GetException(ctx);- is_reject = 1;- done_resolve:- res2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED,- 1, (JSValueConst *)&err);- JS_FreeValue(ctx, err);- JS_FreeValue(ctx, res2);- JS_FreeValue(ctx, resolving_funcs[0]);- JS_FreeValue(ctx, resolving_funcs[1]);- return promise;- }- {- JSValue value_wrapper_promise, resolve_reject[2];- int res;-- value_wrapper_promise = js_promise_resolve(ctx, ctx->promise_ctor,- 1, (JSValueConst *)&value, 0);- if (JS_IsException(value_wrapper_promise)) {- JS_FreeValue(ctx, value);- goto reject;- }-- resolve_reject[0] =- js_async_from_sync_iterator_unwrap_func_create(ctx, done);- if (JS_IsException(resolve_reject[0])) {- JS_FreeValue(ctx, value_wrapper_promise);- goto fail;- }- JS_FreeValue(ctx, value);- resolve_reject[1] = JS_UNDEFINED;-- res = perform_promise_then(ctx, value_wrapper_promise,- (JSValueConst *)resolve_reject,- (JSValueConst *)resolving_funcs);- JS_FreeValue(ctx, resolve_reject[0]);- JS_FreeValue(ctx, value_wrapper_promise);- JS_FreeValue(ctx, resolving_funcs[0]);- JS_FreeValue(ctx, resolving_funcs[1]);- if (res) {- JS_FreeValue(ctx, promise);- return JS_EXCEPTION;- }- }- return promise;- fail:- JS_FreeValue(ctx, value);- JS_FreeValue(ctx, resolving_funcs[0]);- JS_FreeValue(ctx, resolving_funcs[1]);- JS_FreeValue(ctx, promise);- return JS_EXCEPTION;-}--static const JSCFunctionListEntry js_async_from_sync_iterator_proto_funcs[] = {- JS_CFUNC_MAGIC_DEF("next", 1, js_async_from_sync_iterator_next, GEN_MAGIC_NEXT ),- JS_CFUNC_MAGIC_DEF("return", 1, js_async_from_sync_iterator_next, GEN_MAGIC_RETURN ),- JS_CFUNC_MAGIC_DEF("throw", 1, js_async_from_sync_iterator_next, GEN_MAGIC_THROW ),-};--/* AsyncGeneratorFunction */--static const JSCFunctionListEntry js_async_generator_function_proto_funcs[] = {- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGeneratorFunction", JS_PROP_CONFIGURABLE ),-};--/* AsyncGenerator prototype */--static const JSCFunctionListEntry js_async_generator_proto_funcs[] = {- JS_CFUNC_MAGIC_DEF("next", 1, js_async_generator_next, GEN_MAGIC_NEXT ),- JS_CFUNC_MAGIC_DEF("return", 1, js_async_generator_next, GEN_MAGIC_RETURN ),- JS_CFUNC_MAGIC_DEF("throw", 1, js_async_generator_next, GEN_MAGIC_THROW ),- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGenerator", JS_PROP_CONFIGURABLE ),-};--static JSClassShortDef const js_async_class_def[] = {- { JS_ATOM_Promise, js_promise_finalizer, js_promise_mark }, /* JS_CLASS_PROMISE */- { JS_ATOM_PromiseResolveFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_RESOLVE_FUNCTION */- { JS_ATOM_PromiseRejectFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_REJECT_FUNCTION */- { JS_ATOM_AsyncFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_FUNCTION */- { JS_ATOM_AsyncFunctionResolve, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_RESOLVE */- { JS_ATOM_AsyncFunctionReject, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_REJECT */- { JS_ATOM_empty_string, js_async_from_sync_iterator_finalizer, js_async_from_sync_iterator_mark }, /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */- { JS_ATOM_AsyncGeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_GENERATOR_FUNCTION */- { JS_ATOM_AsyncGenerator, js_async_generator_finalizer, js_async_generator_mark }, /* JS_CLASS_ASYNC_GENERATOR */-};--void JS_AddIntrinsicPromise(JSContext *ctx)-{- JSRuntime *rt = ctx->rt;- JSValue obj1;-- if (!JS_IsRegisteredClass(rt, JS_CLASS_PROMISE)) {- init_class_range(rt, js_async_class_def, JS_CLASS_PROMISE,- countof(js_async_class_def));- rt->class_array[JS_CLASS_PROMISE_RESOLVE_FUNCTION].call = js_promise_resolve_function_call;- rt->class_array[JS_CLASS_PROMISE_REJECT_FUNCTION].call = js_promise_resolve_function_call;- rt->class_array[JS_CLASS_ASYNC_FUNCTION].call = js_async_function_call;- rt->class_array[JS_CLASS_ASYNC_FUNCTION_RESOLVE].call = js_async_function_resolve_call;- rt->class_array[JS_CLASS_ASYNC_FUNCTION_REJECT].call = js_async_function_resolve_call;- rt->class_array[JS_CLASS_ASYNC_GENERATOR_FUNCTION].call = js_async_generator_function_call;- }-- /* Promise */- ctx->class_proto[JS_CLASS_PROMISE] = JS_NewObject(ctx);- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_PROMISE],- js_promise_proto_funcs,- countof(js_promise_proto_funcs));- obj1 = JS_NewCFunction2(ctx, js_promise_constructor, "Promise", 1,- JS_CFUNC_constructor, 0);- ctx->promise_ctor = JS_DupValue(ctx, obj1);- JS_SetPropertyFunctionList(ctx, obj1,- js_promise_funcs,- countof(js_promise_funcs));- JS_NewGlobalCConstructor2(ctx, obj1, "Promise",- ctx->class_proto[JS_CLASS_PROMISE]);-- /* AsyncFunction */- ctx->class_proto[JS_CLASS_ASYNC_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto);- obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor,- "AsyncFunction", 1,- JS_CFUNC_constructor_or_func_magic, JS_FUNC_ASYNC,- ctx->function_ctor);- JS_SetPropertyFunctionList(ctx,- ctx->class_proto[JS_CLASS_ASYNC_FUNCTION],- js_async_function_proto_funcs,- countof(js_async_function_proto_funcs));- JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_FUNCTION],- 0, JS_PROP_CONFIGURABLE);- JS_FreeValue(ctx, obj1);-- /* AsyncIteratorPrototype */- ctx->async_iterator_proto = JS_NewObject(ctx);- JS_SetPropertyFunctionList(ctx, ctx->async_iterator_proto,- js_async_iterator_proto_funcs,- countof(js_async_iterator_proto_funcs));-- /* AsyncFromSyncIteratorPrototype */- ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR] =- JS_NewObjectProto(ctx, ctx->async_iterator_proto);- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR],- js_async_from_sync_iterator_proto_funcs,- countof(js_async_from_sync_iterator_proto_funcs));-- /* AsyncGeneratorPrototype */- ctx->class_proto[JS_CLASS_ASYNC_GENERATOR] =- JS_NewObjectProto(ctx, ctx->async_iterator_proto);- JS_SetPropertyFunctionList(ctx,- ctx->class_proto[JS_CLASS_ASYNC_GENERATOR],- js_async_generator_proto_funcs,- countof(js_async_generator_proto_funcs));-- /* AsyncGeneratorFunction */- ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION] =- JS_NewObjectProto(ctx, ctx->function_proto);- obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor,- "AsyncGeneratorFunction", 1,- JS_CFUNC_constructor_or_func_magic,- JS_FUNC_ASYNC_GENERATOR,- ctx->function_ctor);- JS_SetPropertyFunctionList(ctx,- ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],- js_async_generator_function_proto_funcs,- countof(js_async_generator_function_proto_funcs));- JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],- ctx->class_proto[JS_CLASS_ASYNC_GENERATOR],- JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE);- JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],- 0, JS_PROP_CONFIGURABLE);- JS_FreeValue(ctx, obj1);-}--/* URI handling */--static int string_get_hex(JSString *p, int k, int n) {- int c = 0, h;- while (n-- > 0) {- if ((h = from_hex(string_get(p, k++))) < 0)- return -1;- c = (c << 4) | h;- }- return c;-}--static int isURIReserved(int c) {- return c < 0x100 && memchr(";/?:@&=+$,#", c, sizeof(";/?:@&=+$,#") - 1) != NULL;-}--static int __attribute__((format(printf, 2, 3))) js_throw_URIError(JSContext *ctx, const char *fmt, ...)-{- va_list ap;-- va_start(ap, fmt);- JS_ThrowError(ctx, JS_URI_ERROR, fmt, ap);- va_end(ap);- return -1;-}--static int hex_decode(JSContext *ctx, JSString *p, int k) {- int c;-- if (k >= p->len || string_get(p, k) != '%')- return js_throw_URIError(ctx, "expecting %%");- if (k + 2 >= p->len || (c = string_get_hex(p, k + 1, 2)) < 0)- return js_throw_URIError(ctx, "expecting hex digit");-- return c;-}--static JSValue js_global_decodeURI(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int isComponent)-{- JSValue str;- StringBuffer b_s, *b = &b_s;- JSString *p;- int k, c, c1, n, c_min;-- str = JS_ToString(ctx, argv[0]);- if (JS_IsException(str))- return str;-- string_buffer_init(ctx, b, 0);-- p = JS_VALUE_GET_STRING(str);- for (k = 0; k < p->len;) {- c = string_get(p, k);- if (c == '%') {- c = hex_decode(ctx, p, k);- if (c < 0)- goto fail;- k += 3;- if (c < 0x80) {- if (!isComponent && isURIReserved(c)) {- c = '%';- k -= 2;- }- } else {- /* Decode URI-encoded UTF-8 sequence */- if (c >= 0xc0 && c <= 0xdf) {- n = 1;- c_min = 0x80;- c &= 0x1f;- } else if (c >= 0xe0 && c <= 0xef) {- n = 2;- c_min = 0x800;- c &= 0xf;- } else if (c >= 0xf0 && c <= 0xf7) {- n = 3;- c_min = 0x10000;- c &= 0x7;- } else {- n = 0;- c_min = 1;- c = 0;- }- while (n-- > 0) {- c1 = hex_decode(ctx, p, k);- if (c1 < 0)- goto fail;- k += 3;- if ((c1 & 0xc0) != 0x80) {- c = 0;- break;- }- c = (c << 6) | (c1 & 0x3f);- }- if (c < c_min || c > 0x10FFFF ||- (c >= 0xd800 && c < 0xe000)) {- js_throw_URIError(ctx, "malformed UTF-8");- goto fail;- }- }- } else {- k++;- }- string_buffer_putc(b, c);- }- JS_FreeValue(ctx, str);- return string_buffer_end(b);--fail:- JS_FreeValue(ctx, str);- string_buffer_free(b);- return JS_EXCEPTION;-}--static int isUnescaped(int c) {- static char const unescaped_chars[] =- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"- "abcdefghijklmnopqrstuvwxyz"- "0123456789"- "@*_+-./";- return c < 0x100 &&- memchr(unescaped_chars, c, sizeof(unescaped_chars) - 1);-}--static int isURIUnescaped(int c, int isComponent) {- return c < 0x100 &&- ((c >= 0x61 && c <= 0x7a) ||- (c >= 0x41 && c <= 0x5a) ||- (c >= 0x30 && c <= 0x39) ||- memchr("-_.!~*'()", c, sizeof("-_.!~*'()") - 1) != NULL ||- (!isComponent && isURIReserved(c)));-}--static int encodeURI_hex(StringBuffer *b, int c) {- uint8_t buf[6];- int n = 0;- const char *hex = "0123456789ABCDEF";-- buf[n++] = '%';- if (c >= 256) {- buf[n++] = 'u';- buf[n++] = hex[(c >> 12) & 15];- buf[n++] = hex[(c >> 8) & 15];- }- buf[n++] = hex[(c >> 4) & 15];- buf[n++] = hex[(c >> 0) & 15];- return string_buffer_write8(b, buf, n);-}--static JSValue js_global_encodeURI(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv,- int isComponent)-{- JSValue str;- StringBuffer b_s, *b = &b_s;- JSString *p;- int k, c, c1;-- str = JS_ToString(ctx, argv[0]);- if (JS_IsException(str))- return str;-- p = JS_VALUE_GET_STRING(str);- string_buffer_init(ctx, b, p->len);- for (k = 0; k < p->len;) {- c = string_get(p, k);- k++;- if (isURIUnescaped(c, isComponent)) {- string_buffer_putc16(b, c);- } else {- if (c >= 0xdc00 && c <= 0xdfff) {- js_throw_URIError(ctx, "invalid character");- goto fail;- } else if (c >= 0xd800 && c <= 0xdbff) {- if (k >= p->len) {- js_throw_URIError(ctx, "expecting surrogate pair");- goto fail;- }- c1 = string_get(p, k);- k++;- if (c1 < 0xdc00 || c1 > 0xdfff) {- js_throw_URIError(ctx, "expecting surrogate pair");- goto fail;- }- c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;- }- if (c < 0x80) {- encodeURI_hex(b, c);- } else {- /* XXX: use C UTF-8 conversion ? */- if (c < 0x800) {- encodeURI_hex(b, (c >> 6) | 0xc0);- } else {- if (c < 0x10000) {- encodeURI_hex(b, (c >> 12) | 0xe0);- } else {- encodeURI_hex(b, (c >> 18) | 0xf0);- encodeURI_hex(b, ((c >> 12) & 0x3f) | 0x80);- }- encodeURI_hex(b, ((c >> 6) & 0x3f) | 0x80);- }- encodeURI_hex(b, (c & 0x3f) | 0x80);- }- }- }- JS_FreeValue(ctx, str);- return string_buffer_end(b);--fail:- JS_FreeValue(ctx, str);- string_buffer_free(b);- return JS_EXCEPTION;-}--static JSValue js_global_escape(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue str;- StringBuffer b_s, *b = &b_s;- JSString *p;- int i, len, c;-- str = JS_ToString(ctx, argv[0]);- if (JS_IsException(str))- return str;-- p = JS_VALUE_GET_STRING(str);- string_buffer_init(ctx, b, p->len);- for (i = 0, len = p->len; i < len; i++) {- c = string_get(p, i);- if (isUnescaped(c)) {- string_buffer_putc16(b, c);- } else {- encodeURI_hex(b, c);- }- }- JS_FreeValue(ctx, str);- return string_buffer_end(b);-}--static JSValue js_global_unescape(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue str;- StringBuffer b_s, *b = &b_s;- JSString *p;- int i, len, c, n;-- str = JS_ToString(ctx, argv[0]);- if (JS_IsException(str))- return str;-- string_buffer_init(ctx, b, 0);- p = JS_VALUE_GET_STRING(str);- for (i = 0, len = p->len; i < len; i++) {- c = string_get(p, i);- if (c == '%') {- if (i + 6 <= len- && string_get(p, i + 1) == 'u'- && (n = string_get_hex(p, i + 2, 4)) >= 0) {- c = n;- i += 6 - 1;- } else- if (i + 3 <= len- && (n = string_get_hex(p, i + 1, 2)) >= 0) {- c = n;- i += 3 - 1;- }- }- string_buffer_putc16(b, c);- }- JS_FreeValue(ctx, str);- return string_buffer_end(b);-}--/* global object */--static const JSCFunctionListEntry js_global_funcs[] = {- JS_CFUNC_DEF("parseInt", 2, js_parseInt ),- JS_CFUNC_DEF("parseFloat", 1, js_parseFloat ),- JS_CFUNC_DEF("isNaN", 1, js_global_isNaN ),- JS_CFUNC_DEF("isFinite", 1, js_global_isFinite ),-- JS_CFUNC_MAGIC_DEF("decodeURI", 1, js_global_decodeURI, 0 ),- JS_CFUNC_MAGIC_DEF("decodeURIComponent", 1, js_global_decodeURI, 1 ),- JS_CFUNC_MAGIC_DEF("encodeURI", 1, js_global_encodeURI, 0 ),- JS_CFUNC_MAGIC_DEF("encodeURIComponent", 1, js_global_encodeURI, 1 ),- JS_CFUNC_DEF("escape", 1, js_global_escape ),- JS_CFUNC_DEF("unescape", 1, js_global_unescape ),- JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ),- JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),- JS_PROP_UNDEFINED_DEF("undefined", 0 ),-- /* for the 'Date' implementation */- JS_CFUNC_DEF("__date_clock", 0, js___date_clock ),- //JS_CFUNC_DEF("__date_now", 0, js___date_now ),- //JS_CFUNC_DEF("__date_getTimezoneOffset", 1, js___date_getTimezoneOffset ),- //JS_CFUNC_DEF("__date_create", 3, js___date_create ),-};--/* Date */--static int64_t math_mod(int64_t a, int64_t b) {- /* return positive modulo */- int64_t m = a % b;- return m + (m < 0) * b;-}--static int64_t floor_div(int64_t a, int64_t b) {- /* integer division rounding toward -Infinity */- int64_t m = a % b;- return (a - (m + (m < 0) * b)) / b;-}--static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv);--static __exception int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst this_val)-{- if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {- JSObject *p = JS_VALUE_GET_OBJ(this_val);- if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data))- return JS_ToFloat64(ctx, valp, p->u.object_data);- }- JS_ThrowTypeError(ctx, "not a Date object");- return -1;-}--static JSValue JS_SetThisTimeValue(JSContext *ctx, JSValueConst this_val, double v)-{- if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {- JSObject *p = JS_VALUE_GET_OBJ(this_val);- if (p->class_id == JS_CLASS_DATE) {- JS_FreeValue(ctx, p->u.object_data);- p->u.object_data = __JS_NewFloat64(ctx, v);- return JS_DupValue(ctx, p->u.object_data);- }- }- return JS_ThrowTypeError(ctx, "not a Date object");-}--static int64_t days_from_year(int64_t y) {- return 365 * (y - 1970) + floor_div(y - 1969, 4) -- floor_div(y - 1901, 100) + floor_div(y - 1601, 400);-}--static int64_t days_in_year(int64_t y) {- return 365 + !(y % 4) - !(y % 100) + !(y % 400);-}--/* return the year, update days */-static int64_t year_from_days(int64_t *days) {- int64_t y, d1, nd, d = *days;- y = floor_div(d * 10000, 3652425) + 1970;- /* the initial approximation is very good, so only a few- iterations are necessary */- for(;;) {- d1 = d - days_from_year(y);- if (d1 < 0) {- y--;- d1 += days_in_year(y);- } else {- nd = days_in_year(y);- if (d1 < nd)- break;- d1 -= nd;- y++;- }- }- *days = d1;- return y;-}--static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };-static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";-static char const day_names[] = "SunMonTueWedThuFriSat";--static __exception int get_date_fields(JSContext *ctx, JSValueConst obj,- int64_t fields[9], int is_local, int force)-{- double dval;- int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0;-- if (JS_ThisTimeValue(ctx, &dval, obj))- return -1;-- if (isnan(dval)) {- if (!force)- return FALSE; /* NaN */- d = 0; /* initialize all fields to 0 */- } else {- d = dval;- if (is_local) {- tz = -getTimezoneOffset(d);- d += tz * 60000;- }- }-- /* result is >= 0, we can use % */- h = math_mod(d, 86400000);- days = (d - h) / 86400000;- ms = h % 1000;- h = (h - ms) / 1000;- s = h % 60;- h = (h - s) / 60;- m = h % 60;- h = (h - m) / 60;- wd = math_mod(days + 4, 7); /* week day */- y = year_from_days(&days);-- for(i = 0; i < 11; i++) {- md = month_days[i];- if (i == 1)- md += days_in_year(y) - 365;- if (days < md)- break;- days -= md;- }- fields[0] = y;- fields[1] = i;- fields[2] = days + 1;- fields[3] = h;- fields[4] = m;- fields[5] = s;- fields[6] = ms;- fields[7] = wd;- fields[8] = tz;- return TRUE;-}--static double time_clip(double t) {- if (t >= -8.64e15 && t <= 8.64e15)- return trunc(t) + 0.0; /* convert -0 to +0 */- else- return NAN;-}--static double set_date_fields(int64_t fields[], int is_local) {- int64_t days, y, m, md, h, d, i;-- i = fields[1];- m = math_mod(i, 12);- y = fields[0] + (i - m) / 12;- days = days_from_year(y);-- for(i = 0; i < m; i++) {- md = month_days[i];- if (i == 1)- md += days_in_year(y) - 365;- days += md;- }- days += fields[2] - 1;- h = ((fields[3] * 60 + fields[4]) * 60 + fields[5]) * 1000 + fields[6];- d = days * 86400000 + h;- if (is_local)- d += getTimezoneOffset(d) * 60000;- return time_clip(d);-}--static JSValue get_date_field(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- // get_date_field(obj, n, is_local)- int64_t fields[9];- int res, n, is_local;-- is_local = magic & 0x0F;- n = (magic >> 4) & 0x0F;- res = get_date_fields(ctx, this_val, fields, is_local, 0);- if (res < 0)- return JS_EXCEPTION;- if (!res)- return JS_NAN;-- if (magic & 0x100) { // getYear- fields[0] -= 1900;- }- return JS_NewInt64(ctx, fields[n]);-}--static JSValue set_date_field(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- // _field(obj, first_field, end_field, args, is_local)- int64_t fields[9];- int res, first_field, end_field, is_local, i, n;- double d, a;-- d = NAN;- first_field = (magic >> 8) & 0x0F;- end_field = (magic >> 4) & 0x0F;- is_local = magic & 0x0F;-- res = get_date_fields(ctx, this_val, fields, is_local, first_field == 0);- if (res < 0)- return JS_EXCEPTION;- if (res && argc > 0) {- n = end_field - first_field;- if (argc < n)- n = argc;- for(i = 0; i < n; i++) {- if (JS_ToFloat64(ctx, &a, argv[i]))- return JS_EXCEPTION;- if (!isfinite(a))- goto done;- fields[first_field + i] = trunc(a);- }- d = set_date_fields(fields, is_local);- }-done:- return JS_SetThisTimeValue(ctx, this_val, d);-}--/* fmt:- 0: toUTCString: "Tue, 02 Jan 2018 23:04:46 GMT"- 1: toString: "Wed Jan 03 2018 00:05:22 GMT+0100 (CET)"- 2: toISOString: "2018-01-02T23:02:56.927Z"- 3: toLocaleString: "1/2/2018, 11:40:40 PM"- part: 1=date, 2=time 3=all- XXX: should use a variant of strftime().- */-static JSValue get_date_string(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- // _string(obj, fmt, part)- char buf[64];- int64_t fields[9];- int res, fmt, part, pos;- int y, mon, d, h, m, s, ms, wd, tz;-- fmt = (magic >> 4) & 0x0F;- part = magic & 0x0F;-- res = get_date_fields(ctx, this_val, fields, fmt & 1, 0);- if (res < 0)- return JS_EXCEPTION;- if (!res) {- if (fmt == 2)- return JS_ThrowRangeError(ctx, "Date value is NaN");- else- return JS_NewString(ctx, "Invalid Date");- }-- y = fields[0];- mon = fields[1];- d = fields[2];- h = fields[3];- m = fields[4];- s = fields[5];- ms = fields[6];- wd = fields[7];- tz = fields[8];-- pos = 0;-- if (part & 1) { /* date part */- switch(fmt) {- case 0:- pos += snprintf(buf + pos, sizeof(buf) - pos,- "%.3s, %02d %.3s %0*d ",- day_names + wd * 3, d,- month_names + mon * 3, 4 + (y < 0), y);- break;- case 1:- pos += snprintf(buf + pos, sizeof(buf) - pos,- "%.3s %.3s %02d %0*d",- day_names + wd * 3,- month_names + mon * 3, d, 4 + (y < 0), y);- if (part == 3) {- buf[pos++] = ' ';- }- break;- case 2:- if (y >= 0 && y <= 9999) {- pos += snprintf(buf + pos, sizeof(buf) - pos,- "%04d", y);- } else {- pos += snprintf(buf + pos, sizeof(buf) - pos,- "%+07d", y);- }- pos += snprintf(buf + pos, sizeof(buf) - pos,- "-%02d-%02dT", mon + 1, d);- break;- case 3:- pos += snprintf(buf + pos, sizeof(buf) - pos,- "%02d/%02d/%0*d", mon + 1, d, 4 + (y < 0), y);- if (part == 3) {- buf[pos++] = ',';- buf[pos++] = ' ';- }- break;- }- }- if (part & 2) { /* time part */- switch(fmt) {- case 0:- pos += snprintf(buf + pos, sizeof(buf) - pos,- "%02d:%02d:%02d GMT", h, m, s);- break;- case 1:- pos += snprintf(buf + pos, sizeof(buf) - pos,- "%02d:%02d:%02d GMT", h, m, s);- if (tz < 0) {- buf[pos++] = '-';- tz = -tz;- } else {- buf[pos++] = '+';- }- /* tz is >= 0, can use % */- pos += snprintf(buf + pos, sizeof(buf) - pos,- "%02d%02d", tz / 60, tz % 60);- /* XXX: tack the time zone code? */- break;- case 2:- pos += snprintf(buf + pos, sizeof(buf) - pos,- "%02d:%02d:%02d.%03dZ", h, m, s, ms);- break;- case 3:- pos += snprintf(buf + pos, sizeof(buf) - pos,- "%02d:%02d:%02d %cM", (h + 1) % 12 - 1, m, s,- (h < 12) ? 'A' : 'P');- break;- }- }- return JS_NewStringLen(ctx, buf, pos);-}--/* OS dependent: return the UTC time in ms since 1970. */-static int64_t date_now(void) {- struct timeval tv;- gettimeofday(&tv, NULL);- return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);-}--static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target,- int argc, JSValueConst *argv)-{- // Date(y, mon, d, h, m, s, ms)- JSValue rv;- int i, n;- double a, val;-- if (JS_IsUndefined(new_target)) {- /* invoked as function */- argc = 0;- }- n = argc;- if (n == 0) {- val = date_now();- } else if (n == 1) {- JSValue v, dv;- if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) {- JSObject *p = JS_VALUE_GET_OBJ(argv[0]);- if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) {- if (JS_ToFloat64(ctx, &val, p->u.object_data))- return JS_EXCEPTION;- val = time_clip(val);- goto has_val;- }- }- v = JS_ToPrimitive(ctx, argv[0], HINT_NONE);- if (JS_IsString(v)) {- dv = js_Date_parse(ctx, JS_UNDEFINED, 1, (JSValueConst *)&v);- JS_FreeValue(ctx, v);- if (JS_IsException(dv))- return JS_EXCEPTION;- if (JS_ToFloat64Free(ctx, &val, dv))- return JS_EXCEPTION;- } else {- if (JS_ToFloat64Free(ctx, &val, v))- return JS_EXCEPTION;- }- val = time_clip(val);- } else {- int64_t fields[] = { 0, 0, 1, 0, 0, 0, 0 };- if (n > 7)- n = 7;- for(i = 0; i < n; i++) {- if (JS_ToFloat64(ctx, &a, argv[i]))- return JS_EXCEPTION;- if (!isfinite(a))- break;- fields[i] = trunc(a);- if (i == 0 && fields[0] >= 0 && fields[0] < 100)- fields[0] += 1900;- }- val = (i == n) ? set_date_fields(fields, 1) : NAN;- }-has_val:-#if 0- JSValueConst args[3];- args[0] = new_target;- args[1] = ctx->class_proto[JS_CLASS_DATE];- args[2] = __JS_NewFloat64(ctx, val);- rv = js___date_create(ctx, JS_UNDEFINED, 3, args);-#else- rv = js_create_from_ctor(ctx, new_target, JS_CLASS_DATE);- if (!JS_IsException(rv))- JS_SetObjectData(ctx, rv, __JS_NewFloat64(ctx, val));-#endif- if (!JS_IsException(rv) && JS_IsUndefined(new_target)) {- /* invoked as a function, return (new Date()).toString(); */- JSValue s;- s = get_date_string(ctx, rv, 0, NULL, 0x13);- JS_FreeValue(ctx, rv);- rv = s;- }- return rv;-}--static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- // UTC(y, mon, d, h, m, s, ms)- int64_t fields[] = { 0, 0, 1, 0, 0, 0, 0 };- int i, n;- double a;-- n = argc;- if (n == 0)- return JS_NAN;- if (n > 7)- n = 7;- for(i = 0; i < n; i++) {- if (JS_ToFloat64(ctx, &a, argv[i]))- return JS_EXCEPTION;- if (!isfinite(a))- return JS_NAN;- fields[i] = trunc(a);- if (i == 0 && fields[0] >= 0 && fields[0] < 100)- fields[0] += 1900;- }- return __JS_NewFloat64(ctx, set_date_fields(fields, 0));-}--static void string_skip_spaces(JSString *sp, int *pp) {- while (*pp < sp->len && string_get(sp, *pp) == ' ')- *pp += 1;-}--static void string_skip_non_spaces(JSString *sp, int *pp) {- while (*pp < sp->len && string_get(sp, *pp) != ' ')- *pp += 1;-}--/* parse a numeric field */-static int string_get_field(JSString *sp, int *pp, int64_t *pval) {- int64_t v = 0;- int c, p = *pp;-- /* skip non digits, should only skip spaces? */- while (p < sp->len) {- c = string_get(sp, p);- if (c >= '0' && c <= '9')- break;- p++;- }- if (p >= sp->len)- return -1;- while (p < sp->len) {- c = string_get(sp, p);- if (!(c >= '0' && c <= '9'))- break;- v = v * 10 + c - '0';- p++;- }- *pval = v;- *pp = p;- return 0;-}--/* parse a fixed width numeric field */-static int string_get_digits(JSString *sp, int *pp, int n, int64_t *pval) {- int64_t v = 0;- int i, c, p = *pp;-- for(i = 0; i < n; i++) {- if (p >= sp->len)- return -1;- c = string_get(sp, p);- if (!(c >= '0' && c <= '9'))- return -1;- v = v * 10 + c - '0';- p++;- }- *pval = v;- *pp = p;- return 0;-}--/* parse a signed numeric field */-static int string_get_signed_field(JSString *sp, int *pp, int64_t *pval) {- int sgn, res;-- if (*pp >= sp->len)- return -1;-- sgn = string_get(sp, *pp);- if (sgn == '-' || sgn == '+')- *pp += 1;-- res = string_get_field(sp, pp, pval);- if (res == 0 && sgn == '-')- *pval = -*pval;- return res;-}--static int find_abbrev(JSString *sp, int p, const char *list, int count) {- int n, i;-- if (p + 3 <= sp->len) {- for (n = 0; n < count; n++) {- for (i = 0; i < 3; i++) {- if (string_get(sp, p + i) != month_names[n * 3 + i])- goto next;- }- return n;- next:;- }- }- return -1;-}--static int string_get_month(JSString *sp, int *pp, int64_t *pval) {- int n;-- string_skip_spaces(sp, pp);- n = find_abbrev(sp, *pp, month_names, 12);- if (n < 0)- return -1;-- *pval = n;- *pp += 3;- return 0;-}--static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- // parse(s)- JSValue s, rv;- int64_t fields[] = { 0, 1, 1, 0, 0, 0, 0 };- int64_t tz, hh, mm;- double d;- int p, i, c, sgn;- JSString *sp;- BOOL is_local;- - rv = JS_NAN;-- s = JS_ToString(ctx, argv[0]);- if (JS_IsException(s))- return JS_EXCEPTION;- - sp = JS_VALUE_GET_STRING(s);- p = 0;- if (p < sp->len && (((c = string_get(sp, p)) >= '0' && c <= '9') || c == '+' || c == '-')) {- /* ISO format */- /* year field can be negative */- /* XXX: could be stricter */- if (string_get_signed_field(sp, &p, &fields[0]))- goto done;-- is_local = TRUE;- for (i = 1; i < 6; i++) {- if (string_get_field(sp, &p, &fields[i]))- break;- }- if (i <= 3) {- /* no time: UTC by default */- is_local = FALSE;- } else if (i == 6 && p < sp->len && string_get(sp, p) == '.') {- /* parse milliseconds as a fractional part, round to nearest */- /* XXX: the spec does not indicate which rounding should be used */- int mul = 1000, ms = 0;- while (++p < sp->len) {- int c = string_get(sp, p);- if (!(c >= '0' && c <= '9'))- break;- if (mul == 1 && c >= '5')- ms += 1;- ms += (c - '0') * (mul /= 10);- }- fields[6] = ms;- }- fields[1] -= 1;-- /* parse the time zone offset if present: [+-]HH:mm */- tz = 0;- if (p < sp->len) {- sgn = string_get(sp, p);- if (sgn == '+' || sgn == '-') {- if (string_get_field(sp, &p, &hh))- goto done;- if (string_get_field(sp, &p, &mm))- goto done;- tz = hh * 60 + mm;- if (sgn == '-')- tz = -tz;- is_local = FALSE;- } else if (sgn == 'Z') {- is_local = FALSE;- }- }- } else {- /* toString or toUTCString format */- /* skip the day of the week */- string_skip_non_spaces(sp, &p);- string_skip_spaces(sp, &p);- if (p >= sp->len)- goto done;- c = string_get(sp, p);- if (c >= '0' && c <= '9') {- /* day of month first */- if (string_get_field(sp, &p, &fields[2]))- goto done;- if (string_get_month(sp, &p, &fields[1]))- goto done;- } else {- /* month first */- if (string_get_month(sp, &p, &fields[1]))- goto done;- if (string_get_field(sp, &p, &fields[2]))- goto done;- }- string_skip_spaces(sp, &p);- if (string_get_signed_field(sp, &p, &fields[0]))- goto done;-- /* hour, min, seconds */- for(i = 0; i < 3; i++) {- if (string_get_field(sp, &p, &fields[3 + i]))- goto done;- }- // XXX: parse optional milliseconds?-- /* parse the time zone offset if present: [+-]HHmm */- is_local = FALSE;- tz = 0;- for (tz = 0; p < sp->len; p++) {- sgn = string_get(sp, p);- if (sgn == '+' || sgn == '-') {- p++;- if (string_get_digits(sp, &p, 2, &hh))- goto done;- if (string_get_digits(sp, &p, 2, &mm))- goto done;- tz = hh * 60 + mm;- if (sgn == '-')- tz = -tz;- break;- }- }- }- d = set_date_fields(fields, is_local) - tz * 60000;- rv = __JS_NewFloat64(ctx, d);--done:- JS_FreeValue(ctx, s);- return rv;-}--static JSValue js_Date_now(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- // now()- return JS_NewInt64(ctx, date_now());-}--static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- // Symbol_toPrimitive(hint)- JSValueConst obj = this_val;- JSAtom hint = JS_ATOM_NULL;- int hint_num;-- if (!JS_IsObject(obj))- return JS_ThrowTypeErrorNotAnObject(ctx);-- if (JS_IsString(argv[0])) {- hint = JS_ValueToAtom(ctx, argv[0]);- if (hint == JS_ATOM_NULL)- return JS_EXCEPTION;- JS_FreeAtom(ctx, hint);- }- switch (hint) {- case JS_ATOM_number:-#ifdef CONFIG_BIGNUM- case JS_ATOM_integer:-#endif- hint_num = HINT_NUMBER;- break;- case JS_ATOM_string:- case JS_ATOM_default:- hint_num = HINT_STRING;- break;- default:- return JS_ThrowTypeError(ctx, "invalid hint");- }- return JS_ToPrimitive(ctx, obj, hint_num | HINT_FORCE_ORDINARY);-}--static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- // getTimezoneOffset()- double v;-- if (JS_ThisTimeValue(ctx, &v, this_val))- return JS_EXCEPTION;- if (isnan(v))- return JS_NAN;- else- return JS_NewInt64(ctx, getTimezoneOffset((int64_t)trunc(v)));-}--static JSValue js_date_getTime(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- // getTime()- double v;-- if (JS_ThisTimeValue(ctx, &v, this_val))- return JS_EXCEPTION;- return __JS_NewFloat64(ctx, v);-}--static JSValue js_date_setTime(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- // setTime(v)- double v;-- if (JS_ThisTimeValue(ctx, &v, this_val) || JS_ToFloat64(ctx, &v, argv[0]))- return JS_EXCEPTION;- return JS_SetThisTimeValue(ctx, this_val, time_clip(v));-}--static JSValue js_date_setYear(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- // setYear(y)- double y;- JSValueConst args[1];-- if (JS_ThisTimeValue(ctx, &y, this_val) || JS_ToFloat64(ctx, &y, argv[0]))- return JS_EXCEPTION;- y = +y;- if (isfinite(y)) {- y = trunc(y);- if (y >= 0 && y < 100)- y += 1900;- }- args[0] = __JS_NewFloat64(ctx, y);- return set_date_field(ctx, this_val, 1, args, 0x011);-}--static JSValue js_date_toJSON(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- // toJSON(key)- JSValue obj, tv, method, rv;- double d;-- rv = JS_EXCEPTION;- tv = JS_UNDEFINED;-- obj = JS_ToObject(ctx, this_val);- tv = JS_ToPrimitive(ctx, obj, HINT_NUMBER);- if (JS_IsException(tv))- goto exception;- if (JS_IsNumber(tv)) {- if (JS_ToFloat64(ctx, &d, tv) < 0)- goto exception;- if (!isfinite(d)) {- rv = JS_NULL;- goto done;- }- }- method = JS_GetPropertyStr(ctx, obj, "toISOString");- if (JS_IsException(method))- goto exception;- if (!JS_IsFunction(ctx, method)) {- JS_ThrowTypeError(ctx, "object needs toISOString method");- JS_FreeValue(ctx, method);- goto exception;- }- rv = JS_CallFree(ctx, method, obj, 0, NULL);-exception:-done:- JS_FreeValue(ctx, obj);- JS_FreeValue(ctx, tv);- return rv;-}--static const JSCFunctionListEntry js_date_funcs[] = {- JS_CFUNC_DEF("now", 0, js_Date_now ),- JS_CFUNC_DEF("parse", 1, js_Date_parse ),- JS_CFUNC_DEF("UTC", 7, js_Date_UTC ),-};--static const JSCFunctionListEntry js_date_proto_funcs[] = {- JS_CFUNC_DEF("valueOf", 0, js_date_getTime ),- JS_CFUNC_MAGIC_DEF("toString", 0, get_date_string, 0x13 ),- JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_date_Symbol_toPrimitive ),- JS_CFUNC_MAGIC_DEF("toUTCString", 0, get_date_string, 0x03 ),- JS_ALIAS_DEF("toGMTString", "toUTCString" ),- JS_CFUNC_MAGIC_DEF("toISOString", 0, get_date_string, 0x23 ),- JS_CFUNC_MAGIC_DEF("toDateString", 0, get_date_string, 0x11 ),- JS_CFUNC_MAGIC_DEF("toTimeString", 0, get_date_string, 0x12 ),- JS_CFUNC_MAGIC_DEF("toLocaleString", 0, get_date_string, 0x33 ),- JS_CFUNC_MAGIC_DEF("toLocaleDateString", 0, get_date_string, 0x31 ),- JS_CFUNC_MAGIC_DEF("toLocaleTimeString", 0, get_date_string, 0x32 ),- JS_CFUNC_DEF("getTimezoneOffset", 0, js_date_getTimezoneOffset ),- JS_CFUNC_DEF("getTime", 0, js_date_getTime ),- JS_CFUNC_MAGIC_DEF("getYear", 0, get_date_field, 0x101 ),- JS_CFUNC_MAGIC_DEF("getFullYear", 0, get_date_field, 0x01 ),- JS_CFUNC_MAGIC_DEF("getUTCFullYear", 0, get_date_field, 0x00 ),- JS_CFUNC_MAGIC_DEF("getMonth", 0, get_date_field, 0x11 ),- JS_CFUNC_MAGIC_DEF("getUTCMonth", 0, get_date_field, 0x10 ),- JS_CFUNC_MAGIC_DEF("getDate", 0, get_date_field, 0x21 ),- JS_CFUNC_MAGIC_DEF("getUTCDate", 0, get_date_field, 0x20 ),- JS_CFUNC_MAGIC_DEF("getHours", 0, get_date_field, 0x31 ),- JS_CFUNC_MAGIC_DEF("getUTCHours", 0, get_date_field, 0x30 ),- JS_CFUNC_MAGIC_DEF("getMinutes", 0, get_date_field, 0x41 ),- JS_CFUNC_MAGIC_DEF("getUTCMinutes", 0, get_date_field, 0x40 ),- JS_CFUNC_MAGIC_DEF("getSeconds", 0, get_date_field, 0x51 ),- JS_CFUNC_MAGIC_DEF("getUTCSeconds", 0, get_date_field, 0x50 ),- JS_CFUNC_MAGIC_DEF("getMilliseconds", 0, get_date_field, 0x61 ),- JS_CFUNC_MAGIC_DEF("getUTCMilliseconds", 0, get_date_field, 0x60 ),- JS_CFUNC_MAGIC_DEF("getDay", 0, get_date_field, 0x71 ),- JS_CFUNC_MAGIC_DEF("getUTCDay", 0, get_date_field, 0x70 ),- JS_CFUNC_DEF("setTime", 1, js_date_setTime ),- JS_CFUNC_MAGIC_DEF("setMilliseconds", 1, set_date_field, 0x671 ),- JS_CFUNC_MAGIC_DEF("setUTCMilliseconds", 1, set_date_field, 0x670 ),- JS_CFUNC_MAGIC_DEF("setSeconds", 2, set_date_field, 0x571 ),- JS_CFUNC_MAGIC_DEF("setUTCSeconds", 2, set_date_field, 0x570 ),- JS_CFUNC_MAGIC_DEF("setMinutes", 3, set_date_field, 0x471 ),- JS_CFUNC_MAGIC_DEF("setUTCMinutes", 3, set_date_field, 0x470 ),- JS_CFUNC_MAGIC_DEF("setHours", 4, set_date_field, 0x371 ),- JS_CFUNC_MAGIC_DEF("setUTCHours", 4, set_date_field, 0x370 ),- JS_CFUNC_MAGIC_DEF("setDate", 1, set_date_field, 0x231 ),- JS_CFUNC_MAGIC_DEF("setUTCDate", 1, set_date_field, 0x230 ),- JS_CFUNC_MAGIC_DEF("setMonth", 2, set_date_field, 0x131 ),- JS_CFUNC_MAGIC_DEF("setUTCMonth", 2, set_date_field, 0x130 ),- JS_CFUNC_DEF("setYear", 1, js_date_setYear ),- JS_CFUNC_MAGIC_DEF("setFullYear", 3, set_date_field, 0x031 ),- JS_CFUNC_MAGIC_DEF("setUTCFullYear", 3, set_date_field, 0x030 ),- JS_CFUNC_DEF("toJSON", 1, js_date_toJSON ),-};--void JS_AddIntrinsicDate(JSContext *ctx)-{- JSValueConst obj;-- /* Date */- ctx->class_proto[JS_CLASS_DATE] = JS_NewObject(ctx);- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATE], js_date_proto_funcs,- countof(js_date_proto_funcs));- obj = JS_NewGlobalCConstructor(ctx, "Date", js_date_constructor, 7,- ctx->class_proto[JS_CLASS_DATE]);- JS_SetPropertyFunctionList(ctx, obj, js_date_funcs, countof(js_date_funcs));-}--/* eval */--void JS_AddIntrinsicEval(JSContext *ctx)-{- ctx->eval_internal = __JS_EvalInternal;-}--#ifdef CONFIG_BIGNUM--/* Operators */--static void js_operator_set_finalizer(JSRuntime *rt, JSValue val)-{- JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET);- int i, j;- JSBinaryOperatorDefEntry *ent;- - if (opset) {- for(i = 0; i < JS_OVOP_COUNT; i++) {- if (opset->self_ops[i])- JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i]));- }- for(j = 0; j < opset->left.count; j++) {- ent = &opset->left.tab[j];- for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {- if (ent->ops[i])- JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]));- }- }- js_free_rt(rt, opset->left.tab);- for(j = 0; j < opset->right.count; j++) {- ent = &opset->right.tab[j];- for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {- if (ent->ops[i])- JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]));- }- }- js_free_rt(rt, opset->right.tab);- js_free_rt(rt, opset);- }-}--static void js_operator_set_mark(JSRuntime *rt, JSValueConst val,- JS_MarkFunc *mark_func)-{- JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET);- int i, j;- JSBinaryOperatorDefEntry *ent;- - if (opset) {- for(i = 0; i < JS_OVOP_COUNT; i++) {- if (opset->self_ops[i])- JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i]),- mark_func);- }- for(j = 0; j < opset->left.count; j++) {- ent = &opset->left.tab[j];- for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {- if (ent->ops[i])- JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]),- mark_func);- }- }- for(j = 0; j < opset->right.count; j++) {- ent = &opset->right.tab[j];- for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {- if (ent->ops[i])- JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]),- mark_func);- }- }- }-}---/* create an OperatorSet object */-static JSValue js_operators_create_internal(JSContext *ctx,- int argc, JSValueConst *argv,- BOOL is_primitive)-{- JSValue opset_obj, prop, obj;- JSOperatorSetData *opset, *opset1;- JSBinaryOperatorDef *def;- JSValueConst arg;- int i, j;- JSBinaryOperatorDefEntry *new_tab;- JSBinaryOperatorDefEntry *ent;- uint32_t op_count;-- if (ctx->rt->operator_count == UINT32_MAX) {- return JS_ThrowTypeError(ctx, "too many operators");- }- opset_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_OPERATOR_SET);- if (JS_IsException(opset_obj))- goto fail;- opset = js_mallocz(ctx, sizeof(*opset));- if (!opset)- goto fail;- JS_SetOpaque(opset_obj, opset);- if (argc >= 1) {- arg = argv[0];- /* self operators */- for(i = 0; i < JS_OVOP_COUNT; i++) {- prop = JS_GetPropertyStr(ctx, arg, js_overloadable_operator_names[i]);- if (JS_IsException(prop))- goto fail;- if (!JS_IsUndefined(prop)) {- if (check_function(ctx, prop)) {- JS_FreeValue(ctx, prop);- goto fail;- }- opset->self_ops[i] = JS_VALUE_GET_OBJ(prop);- }- }- }- /* left & right operators */- for(j = 1; j < argc; j++) {- arg = argv[j];- prop = JS_GetPropertyStr(ctx, arg, "left");- if (JS_IsException(prop))- goto fail;- def = &opset->right;- if (JS_IsUndefined(prop)) {- prop = JS_GetPropertyStr(ctx, arg, "right");- if (JS_IsException(prop))- goto fail;- if (JS_IsUndefined(prop)) {- JS_ThrowTypeError(ctx, "left or right property must be present");- goto fail;- }- def = &opset->left;- }- /* get the operator set */- obj = JS_GetProperty(ctx, prop, JS_ATOM_prototype);- JS_FreeValue(ctx, prop);- if (JS_IsException(obj))- goto fail;- prop = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet);- JS_FreeValue(ctx, obj);- if (JS_IsException(prop))- goto fail;- opset1 = JS_GetOpaque2(ctx, prop, JS_CLASS_OPERATOR_SET);- if (!opset1) {- JS_FreeValue(ctx, prop);- goto fail;- }- op_count = opset1->operator_counter;- JS_FreeValue(ctx, prop);- - /* we assume there are few entries */- new_tab = js_realloc(ctx, def->tab,- (def->count + 1) * sizeof(def->tab[0]));- if (!new_tab)- goto fail;- def->tab = new_tab;- def->count++;- ent = def->tab + def->count - 1;- memset(ent, 0, sizeof(def->tab[0]));- ent->operator_index = op_count;- - for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {- prop = JS_GetPropertyStr(ctx, arg,- js_overloadable_operator_names[i]);- if (JS_IsException(prop))- goto fail;- if (!JS_IsUndefined(prop)) {- if (check_function(ctx, prop)) {- JS_FreeValue(ctx, prop);- goto fail;- }- ent->ops[i] = JS_VALUE_GET_OBJ(prop);- }- }- }- opset->is_primitive = is_primitive;- opset->operator_counter = ctx->rt->operator_count++;- return opset_obj;- fail:- JS_FreeValue(ctx, opset_obj);- return JS_EXCEPTION;-}--static JSValue js_operators_create(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return js_operators_create_internal(ctx, argc, argv, FALSE);-}--static JSValue js_operators_updateBigIntOperators(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue opset_obj, prop;- JSOperatorSetData *opset;- const JSOverloadableOperatorEnum ops[2] = { JS_OVOP_DIV, JS_OVOP_POW };- JSOverloadableOperatorEnum op;- int i;- - opset_obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_BIG_INT],- JS_ATOM_Symbol_operatorSet);- if (JS_IsException(opset_obj))- goto fail;- opset = JS_GetOpaque2(ctx, opset_obj, JS_CLASS_OPERATOR_SET);- if (!opset)- goto fail;- for(i = 0; i < countof(ops); i++) {- op = ops[i];- prop = JS_GetPropertyStr(ctx, argv[0],- js_overloadable_operator_names[op]);- if (JS_IsException(prop))- goto fail;- if (!JS_IsUndefined(prop)) {- if (!JS_IsNull(prop) && check_function(ctx, prop)) {- JS_FreeValue(ctx, prop);- goto fail;- }- if (opset->self_ops[op])- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[op]));- if (JS_IsNull(prop)) {- opset->self_ops[op] = NULL;- } else {- opset->self_ops[op] = JS_VALUE_GET_PTR(prop);- }- }- }- JS_FreeValue(ctx, opset_obj);- return JS_UNDEFINED;- fail:- JS_FreeValue(ctx, opset_obj);- return JS_EXCEPTION;-}--static int js_operators_set_default(JSContext *ctx, JSValueConst obj)-{- JSValue opset_obj;-- if (!JS_IsObject(obj)) /* in case the prototype is not defined */- return 0;- opset_obj = js_operators_create_internal(ctx, 0, NULL, TRUE);- if (JS_IsException(opset_obj))- return -1;- /* cannot be modified by the user */- JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_operatorSet,- opset_obj, 0);- return 0;-}--static JSValue js_dummy_operators_ctor(JSContext *ctx, JSValueConst new_target,- int argc, JSValueConst *argv)-{- return js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);-}--static JSValue js_global_operators(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue func_obj, proto, opset_obj;-- func_obj = JS_UNDEFINED;- proto = JS_NewObject(ctx);- if (JS_IsException(proto))- return JS_EXCEPTION;- opset_obj = js_operators_create_internal(ctx, argc, argv, FALSE);- if (JS_IsException(opset_obj))- goto fail;- JS_DefinePropertyValue(ctx, proto, JS_ATOM_Symbol_operatorSet,- opset_obj, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);- func_obj = JS_NewCFunction2(ctx, js_dummy_operators_ctor, "Operators",- 0, JS_CFUNC_constructor, 0);- if (JS_IsException(func_obj))- goto fail;- JS_SetConstructor2(ctx, func_obj, proto,- 0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);- JS_FreeValue(ctx, proto);- return func_obj;- fail:- JS_FreeValue(ctx, proto);- JS_FreeValue(ctx, func_obj);- return JS_EXCEPTION;-}--static const JSCFunctionListEntry js_operators_funcs[] = {- JS_CFUNC_DEF("create", 1, js_operators_create ),- JS_CFUNC_DEF("updateBigIntOperators", 2, js_operators_updateBigIntOperators ),-};--/* must be called after all overloadable base types are initialized */-void JS_AddIntrinsicOperators(JSContext *ctx)-{- JSValue obj;-- ctx->allow_operator_overloading = TRUE;- obj = JS_NewCFunction(ctx, js_global_operators, "Operators", 1);- JS_SetPropertyFunctionList(ctx, obj,- js_operators_funcs,- countof(js_operators_funcs));- JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_Operators,- obj,- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);- /* add default operatorSets */- js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BOOLEAN]);- js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_NUMBER]);- js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_STRING]);- js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_INT]);- js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT]);- js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL]);-}--/* BigInt */--static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val)-{- uint32_t tag;-- redo:- tag = JS_VALUE_GET_NORM_TAG(val);- switch(tag) {- case JS_TAG_INT:- case JS_TAG_BOOL:- val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val));- break;- case JS_TAG_BIG_INT:- break;- case JS_TAG_FLOAT64:- case JS_TAG_BIG_FLOAT:- {- bf_t *a, a_s;- - a = JS_ToBigFloat(ctx, &a_s, val);- if (!bf_is_finite(a)) {- JS_FreeValue(ctx, val);- val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to bigint");- } else {- JSValue val1 = JS_NewBigInt(ctx);- bf_t *r;- int ret;- if (JS_IsException(val1)) {- JS_FreeValue(ctx, val);- return JS_EXCEPTION;- }- r = JS_GetBigInt(val1);- ret = bf_set(r, a);- ret |= bf_rint(r, BF_RNDZ);- JS_FreeValue(ctx, val);- if (ret & BF_ST_MEM_ERROR) {- JS_FreeValue(ctx, val1);- val = JS_ThrowOutOfMemory(ctx);- } else if (ret & BF_ST_INEXACT) {- JS_FreeValue(ctx, val1);- val = JS_ThrowRangeError(ctx, "cannot convert to bigint: not an integer");- } else {- val = JS_CompactBigInt(ctx, val1);- }- }- if (a == &a_s)- bf_delete(a);- }- break;- case JS_TAG_BIG_DECIMAL:- val = JS_ToStringFree(ctx, val);- if (JS_IsException(val))- break;- goto redo;- case JS_TAG_STRING:- val = JS_StringToBigIntErr(ctx, val);- break;- case JS_TAG_OBJECT:- val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);- if (JS_IsException(val))- break;- goto redo;- case JS_TAG_NULL:- case JS_TAG_UNDEFINED:- default:- JS_FreeValue(ctx, val);- return JS_ThrowTypeError(ctx, "cannot convert to bigint");- }- return val;-}--static JSValue js_bigint_constructor(JSContext *ctx,- JSValueConst this_val,- int argc, JSValueConst *argv)-{- return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0]));-}--static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val)-{- if (JS_IsBigInt(ctx, this_val))- return JS_DupValue(ctx, this_val);-- if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {- JSObject *p = JS_VALUE_GET_OBJ(this_val);- if (p->class_id == JS_CLASS_BIG_INT) {- if (JS_IsBigInt(ctx, p->u.object_data))- return JS_DupValue(ctx, p->u.object_data);- }- }- return JS_ThrowTypeError(ctx, "not a bigint");-}--static JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue val;- int base;- JSValue ret;-- val = js_thisBigIntValue(ctx, this_val);- if (JS_IsException(val))- return val;- if (argc == 0 || JS_IsUndefined(argv[0])) {- base = 10;- } else {- base = js_get_radix(ctx, argv[0]);- if (base < 0)- goto fail;- }- ret = js_bigint_to_string1(ctx, val, base);- JS_FreeValue(ctx, val);- return ret;- fail:- JS_FreeValue(ctx, val);- return JS_EXCEPTION;-}--static JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return js_thisBigIntValue(ctx, this_val);-}--static JSValue js_bigint_div(JSContext *ctx,- JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- bf_t a_s, b_s, *a, *b, *r, *q;- int status;- JSValue q_val, r_val;- - q_val = JS_NewBigInt(ctx);- if (JS_IsException(q_val))- return JS_EXCEPTION;- r_val = JS_NewBigInt(ctx);- if (JS_IsException(r_val))- goto fail;- b = NULL;- a = JS_ToBigInt(ctx, &a_s, argv[0]);- if (!a)- goto fail;- b = JS_ToBigInt(ctx, &b_s, argv[1]);- if (!b) {- JS_FreeBigInt(ctx, a, &a_s);- goto fail;- }- q = JS_GetBigInt(q_val);- r = JS_GetBigInt(r_val);- status = bf_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ, magic & 0xf);- JS_FreeBigInt(ctx, a, &a_s);- JS_FreeBigInt(ctx, b, &b_s);- if (unlikely(status)) {- throw_bf_exception(ctx, status);- goto fail;- }- q_val = JS_CompactBigInt(ctx, q_val);- if (magic & 0x10) {- JSValue ret;- ret = JS_NewArray(ctx);- if (JS_IsException(ret))- goto fail;- JS_SetPropertyUint32(ctx, ret, 0, q_val);- JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, r_val));- return ret;- } else {- JS_FreeValue(ctx, r_val);- return q_val;- }- fail:- JS_FreeValue(ctx, q_val);- JS_FreeValue(ctx, r_val);- return JS_EXCEPTION;-}--static JSValue js_bigint_sqrt(JSContext *ctx,- JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- bf_t a_s, *a, *r, *rem;- int status;- JSValue r_val, rem_val;- - r_val = JS_NewBigInt(ctx);- if (JS_IsException(r_val))- return JS_EXCEPTION;- rem_val = JS_NewBigInt(ctx);- if (JS_IsException(rem_val))- return JS_EXCEPTION;- r = JS_GetBigInt(r_val);- rem = JS_GetBigInt(rem_val);-- a = JS_ToBigInt(ctx, &a_s, argv[0]);- if (!a)- goto fail;- status = bf_sqrtrem(r, rem, a);- JS_FreeBigInt(ctx, a, &a_s);- if (unlikely(status & ~BF_ST_INEXACT)) {- throw_bf_exception(ctx, status);- goto fail;- }- r_val = JS_CompactBigInt(ctx, r_val);- if (magic) {- JSValue ret;- ret = JS_NewArray(ctx);- if (JS_IsException(ret))- goto fail;- JS_SetPropertyUint32(ctx, ret, 0, r_val);- JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, rem_val));- return ret;- } else {- JS_FreeValue(ctx, rem_val);- return r_val;- }- fail:- JS_FreeValue(ctx, r_val);- JS_FreeValue(ctx, rem_val);- return JS_EXCEPTION;-}--static JSValue js_bigint_op1(JSContext *ctx,- JSValueConst this_val,- int argc, JSValueConst *argv,- int magic)-{- bf_t a_s, *a;- int64_t res;-- a = JS_ToBigInt(ctx, &a_s, argv[0]);- if (!a)- return JS_EXCEPTION;- switch(magic) {- case 0: /* floorLog2 */- if (a->sign || a->expn <= 0) {- res = -1;- } else {- res = a->expn - 1;- }- break;- case 1: /* ctz */- if (bf_is_zero(a)) {- res = -1;- } else {- res = bf_get_exp_min(a);- }- break;- default:- abort();- }- JS_FreeBigInt(ctx, a, &a_s);- return JS_NewBigInt64(ctx, res);-}--static JSValue js_bigint_asUintN(JSContext *ctx,- JSValueConst this_val,- int argc, JSValueConst *argv, int asIntN)-{- uint64_t bits;- bf_t a_s, *a = &a_s, *r, mask_s, *mask = &mask_s;- JSValue res;- - if (JS_ToIndex(ctx, &bits, argv[0]))- return JS_EXCEPTION;- res = JS_NewBigInt(ctx);- if (JS_IsException(res))- return JS_EXCEPTION;- r = JS_GetBigInt(res);- a = JS_ToBigInt(ctx, &a_s, argv[1]);- if (!a) {- JS_FreeValue(ctx, res);- return JS_EXCEPTION;- }- /* XXX: optimize */- r = JS_GetBigInt(res);- bf_init(ctx->bf_ctx, mask);- bf_set_ui(mask, 1);- bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ);- bf_add_si(mask, mask, -1, BF_PREC_INF, BF_RNDZ);- bf_logic_and(r, a, mask);- if (asIntN && bits != 0) {- bf_set_ui(mask, 1);- bf_mul_2exp(mask, bits - 1, BF_PREC_INF, BF_RNDZ);- if (bf_cmpu(r, mask) >= 0) {- bf_set_ui(mask, 1);- bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ);- bf_sub(r, r, mask, BF_PREC_INF, BF_RNDZ);- }- }- bf_delete(mask);- JS_FreeBigInt(ctx, a, &a_s);- return JS_CompactBigInt(ctx, res);-}--static const JSCFunctionListEntry js_bigint_funcs[] = {- JS_CFUNC_MAGIC_DEF("asUintN", 2, js_bigint_asUintN, 0 ),- JS_CFUNC_MAGIC_DEF("asIntN", 2, js_bigint_asUintN, 1 ),- /* QuickJS extensions */- JS_CFUNC_MAGIC_DEF("tdiv", 2, js_bigint_div, BF_RNDZ ),- JS_CFUNC_MAGIC_DEF("fdiv", 2, js_bigint_div, BF_RNDD ),- JS_CFUNC_MAGIC_DEF("cdiv", 2, js_bigint_div, BF_RNDU ),- JS_CFUNC_MAGIC_DEF("ediv", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN ),- JS_CFUNC_MAGIC_DEF("tdivrem", 2, js_bigint_div, BF_RNDZ | 0x10 ),- JS_CFUNC_MAGIC_DEF("fdivrem", 2, js_bigint_div, BF_RNDD | 0x10 ),- JS_CFUNC_MAGIC_DEF("cdivrem", 2, js_bigint_div, BF_RNDU | 0x10 ),- JS_CFUNC_MAGIC_DEF("edivrem", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN | 0x10 ),- JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigint_sqrt, 0 ),- JS_CFUNC_MAGIC_DEF("sqrtrem", 1, js_bigint_sqrt, 1 ),- JS_CFUNC_MAGIC_DEF("floorLog2", 1, js_bigint_op1, 0 ),- JS_CFUNC_MAGIC_DEF("ctz", 1, js_bigint_op1, 1 ),-};--static const JSCFunctionListEntry js_bigint_proto_funcs[] = {- JS_CFUNC_DEF("toString", 0, js_bigint_toString ),- JS_CFUNC_DEF("valueOf", 0, js_bigint_valueOf ),- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "BigInt", JS_PROP_CONFIGURABLE ),-};--void JS_AddIntrinsicBigInt(JSContext *ctx)-{- JSRuntime *rt = ctx->rt;- JSValue obj1;-- rt->bigint_ops.to_string = js_bigint_to_string;- rt->bigint_ops.from_string = js_string_to_bigint;- rt->bigint_ops.unary_arith = js_unary_arith_bigint;- rt->bigint_ops.binary_arith = js_binary_arith_bigint;- rt->bigint_ops.compare = js_compare_bigfloat;- - ctx->class_proto[JS_CLASS_BIG_INT] = JS_NewObject(ctx);- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_INT],- js_bigint_proto_funcs,- countof(js_bigint_proto_funcs));- obj1 = JS_NewCFunction(ctx, js_bigint_constructor, "BigInt", 1);- JS_NewGlobalCConstructor2(ctx, obj1, "BigInt",- ctx->class_proto[JS_CLASS_BIG_INT]);- JS_SetPropertyFunctionList(ctx, obj1, js_bigint_funcs,- countof(js_bigint_funcs));-}--/* BigFloat */--static JSValue js_thisBigFloatValue(JSContext *ctx, JSValueConst this_val)-{- if (JS_IsBigFloat(this_val))- return JS_DupValue(ctx, this_val);-- if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {- JSObject *p = JS_VALUE_GET_OBJ(this_val);- if (p->class_id == JS_CLASS_BIG_FLOAT) {- if (JS_IsBigFloat(p->u.object_data))- return JS_DupValue(ctx, p->u.object_data);- }- }- return JS_ThrowTypeError(ctx, "not a bigfloat");-}--static JSValue js_bigfloat_toString(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue val;- int base;- JSValue ret;-- val = js_thisBigFloatValue(ctx, this_val);- if (JS_IsException(val))- return val;- if (argc == 0 || JS_IsUndefined(argv[0])) {- base = 10;- } else {- base = js_get_radix(ctx, argv[0]);- if (base < 0)- goto fail;- }- ret = js_ftoa(ctx, val, base, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN);- JS_FreeValue(ctx, val);- return ret;- fail:- JS_FreeValue(ctx, val);- return JS_EXCEPTION;-}--static JSValue js_bigfloat_valueOf(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return js_thisBigFloatValue(ctx, this_val);-}--static int bigfloat_get_rnd_mode(JSContext *ctx, JSValueConst val)-{- int rnd_mode;- if (JS_ToInt32Sat(ctx, &rnd_mode, val))- return -1;- if (rnd_mode < BF_RNDN || rnd_mode > BF_RNDF) {- JS_ThrowRangeError(ctx, "invalid rounding mode");- return -1;- }- return rnd_mode;-}--static JSValue js_bigfloat_toFixed(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue val, ret;- int64_t f;- int rnd_mode, radix;-- val = js_thisBigFloatValue(ctx, this_val);- if (JS_IsException(val))- return val;- if (JS_ToInt64Sat(ctx, &f, argv[0]))- goto fail;- if (f < 0 || f > BF_PREC_MAX) {- JS_ThrowRangeError(ctx, "invalid number of digits");- goto fail;- }- rnd_mode = BF_RNDNA;- radix = 10;- /* XXX: swap parameter order for rounding mode and radix */- if (argc > 1) {- rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);- if (rnd_mode < 0)- goto fail;- }- if (argc > 2) {- radix = js_get_radix(ctx, argv[2]);- if (radix < 0)- goto fail;- }- ret = js_ftoa(ctx, val, radix, f, rnd_mode | BF_FTOA_FORMAT_FRAC);- JS_FreeValue(ctx, val);- return ret;- fail:- JS_FreeValue(ctx, val);- return JS_EXCEPTION;-}--static BOOL js_bigfloat_is_finite(JSContext *ctx, JSValueConst val)-{- BOOL res;- uint32_t tag;-- tag = JS_VALUE_GET_NORM_TAG(val);- switch(tag) {- case JS_TAG_BIG_FLOAT:- {- JSBigFloat *p = JS_VALUE_GET_PTR(val);- res = bf_is_finite(&p->num);- }- break;- default:- res = FALSE;- break;- }- return res;-}--static JSValue js_bigfloat_toExponential(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue val, ret;- int64_t f;- int rnd_mode, radix;-- val = js_thisBigFloatValue(ctx, this_val);- if (JS_IsException(val))- return val;- if (JS_ToInt64Sat(ctx, &f, argv[0]))- goto fail;- if (!js_bigfloat_is_finite(ctx, val)) {- ret = JS_ToString(ctx, val);- } else if (JS_IsUndefined(argv[0])) {- ret = js_ftoa(ctx, val, 10, 0,- BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP);- } else {- if (f < 0 || f > BF_PREC_MAX) {- JS_ThrowRangeError(ctx, "invalid number of digits");- goto fail;- }- rnd_mode = BF_RNDNA;- radix = 10;- if (argc > 1) {- rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);- if (rnd_mode < 0)- goto fail;- }- if (argc > 2) {- radix = js_get_radix(ctx, argv[2]);- if (radix < 0)- goto fail;- }- ret = js_ftoa(ctx, val, radix, f + 1,- rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP);- }- JS_FreeValue(ctx, val);- return ret;- fail:- JS_FreeValue(ctx, val);- return JS_EXCEPTION;-}--static JSValue js_bigfloat_toPrecision(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue val, ret;- int64_t p;- int rnd_mode, radix;-- val = js_thisBigFloatValue(ctx, this_val);- if (JS_IsException(val))- return val;- if (JS_IsUndefined(argv[0]))- goto to_string;- if (JS_ToInt64Sat(ctx, &p, argv[0]))- goto fail;- if (!js_bigfloat_is_finite(ctx, val)) {- to_string:- ret = JS_ToString(ctx, this_val);- } else {- if (p < 1 || p > BF_PREC_MAX) {- JS_ThrowRangeError(ctx, "invalid number of digits");- goto fail;- }- rnd_mode = BF_RNDNA;- radix = 10;- if (argc > 1) {- rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);- if (rnd_mode < 0)- goto fail;- }- if (argc > 2) {- radix = js_get_radix(ctx, argv[2]);- if (radix < 0)- goto fail;- }- ret = js_ftoa(ctx, val, radix, p, rnd_mode | BF_FTOA_FORMAT_FIXED);- }- JS_FreeValue(ctx, val);- return ret;- fail:- JS_FreeValue(ctx, val);- return JS_EXCEPTION;-}--static const JSCFunctionListEntry js_bigfloat_proto_funcs[] = {- JS_CFUNC_DEF("toString", 0, js_bigfloat_toString ),- JS_CFUNC_DEF("valueOf", 0, js_bigfloat_valueOf ),- JS_CFUNC_DEF("toPrecision", 1, js_bigfloat_toPrecision ),- JS_CFUNC_DEF("toFixed", 1, js_bigfloat_toFixed ),- JS_CFUNC_DEF("toExponential", 1, js_bigfloat_toExponential ),-};--static JSValue js_bigfloat_constructor(JSContext *ctx,- JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue val;- if (argc == 0) {- bf_t *r;- val = JS_NewBigFloat(ctx);- if (JS_IsException(val))- return val;- r = JS_GetBigFloat(val);- bf_set_zero(r, 0);- } else {- val = JS_DupValue(ctx, argv[0]);- redo:- switch(JS_VALUE_GET_NORM_TAG(val)) {- case JS_TAG_BIG_FLOAT:- break;- case JS_TAG_FLOAT64:- {- bf_t *r;- double d = JS_VALUE_GET_FLOAT64(val);- val = JS_NewBigFloat(ctx);- if (JS_IsException(val))- break;- r = JS_GetBigFloat(val);- if (bf_set_float64(r, d))- goto fail;- }- break;- case JS_TAG_INT:- {- bf_t *r;- int32_t v = JS_VALUE_GET_INT(val);- val = JS_NewBigFloat(ctx);- if (JS_IsException(val))- break;- r = JS_GetBigFloat(val);- if (bf_set_si(r, v))- goto fail;- }- break;- case JS_TAG_BIG_INT:- /* We keep the full precision of the integer */- {- JSBigFloat *p = JS_VALUE_GET_PTR(val);- val = JS_MKPTR(JS_TAG_BIG_FLOAT, p);- }- break;- case JS_TAG_BIG_DECIMAL:- val = JS_ToStringFree(ctx, val);- if (JS_IsException(val))- break;- goto redo;- case JS_TAG_STRING:- {- const char *str, *p;- size_t len;- int err;-- str = JS_ToCStringLen(ctx, &len, val);- JS_FreeValue(ctx, val);- if (!str)- return JS_EXCEPTION;- p = str;- p += skip_spaces(p);- if ((p - str) == len) {- bf_t *r;- val = JS_NewBigFloat(ctx);- if (JS_IsException(val))- break;- r = JS_GetBigFloat(val);- bf_set_zero(r, 0);- err = 0;- } else {- val = js_atof(ctx, p, &p, 0, ATOD_ACCEPT_BIN_OCT |- ATOD_TYPE_BIG_FLOAT |- ATOD_ACCEPT_PREFIX_AFTER_SIGN);- if (JS_IsException(val)) {- JS_FreeCString(ctx, str);- return JS_EXCEPTION;- }- p += skip_spaces(p);- err = ((p - str) != len);- }- JS_FreeCString(ctx, str);- if (err) {- JS_FreeValue(ctx, val);- return JS_ThrowSyntaxError(ctx, "invalid bigfloat literal");- }- }- break;- case JS_TAG_OBJECT:- val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);- if (JS_IsException(val))- break;- goto redo;- case JS_TAG_NULL:- case JS_TAG_UNDEFINED:- default:- JS_FreeValue(ctx, val);- return JS_ThrowTypeError(ctx, "cannot convert to bigfloat");- }- }- return val;- fail:- JS_FreeValue(ctx, val);- return JS_EXCEPTION;-}--static JSValue js_bigfloat_get_const(JSContext *ctx,- JSValueConst this_val, int magic)-{- bf_t *r;- JSValue val;- val = JS_NewBigFloat(ctx);- if (JS_IsException(val))- return val;- r = JS_GetBigFloat(val);- switch(magic) {- case 0: /* PI */- bf_const_pi(r, ctx->fp_env.prec, ctx->fp_env.flags);- break;- case 1: /* LN2 */- bf_const_log2(r, ctx->fp_env.prec, ctx->fp_env.flags);- break;- case 2: /* MIN_VALUE */- case 3: /* MAX_VALUE */- {- slimb_t e_range, e;- e_range = (limb_t)1 << (bf_get_exp_bits(ctx->fp_env.flags) - 1);- bf_set_ui(r, 1);- if (magic == 2) {- e = -e_range + 2;- if (ctx->fp_env.flags & BF_FLAG_SUBNORMAL)- e -= ctx->fp_env.prec - 1;- bf_mul_2exp(r, e, ctx->fp_env.prec, ctx->fp_env.flags);- } else {- bf_mul_2exp(r, ctx->fp_env.prec, ctx->fp_env.prec,- ctx->fp_env.flags);- bf_add_si(r, r, -1, ctx->fp_env.prec, ctx->fp_env.flags);- bf_mul_2exp(r, e_range - ctx->fp_env.prec, ctx->fp_env.prec,- ctx->fp_env.flags);- }- }- break;- case 4: /* EPSILON */- bf_set_ui(r, 1);- bf_mul_2exp(r, 1 - ctx->fp_env.prec,- ctx->fp_env.prec, ctx->fp_env.flags);- break;- default:- abort();- }- return val;-}--static JSValue js_bigfloat_parseFloat(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- bf_t *a;- const char *str;- JSValue ret;- int radix;- JSFloatEnv *fe;-- str = JS_ToCString(ctx, argv[0]);- if (!str)- return JS_EXCEPTION;- if (JS_ToInt32(ctx, &radix, argv[1])) {- fail:- JS_FreeCString(ctx, str);- return JS_EXCEPTION;- }- if (radix != 0 && (radix < 2 || radix > 36)) {- JS_ThrowRangeError(ctx, "radix must be between 2 and 36");- goto fail;- }- fe = &ctx->fp_env;- if (argc > 2) {- fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV);- if (!fe)- goto fail;- }- ret = JS_NewBigFloat(ctx);- if (JS_IsException(ret))- goto done;- a = JS_GetBigFloat(ret);- /* XXX: use js_atof() */- bf_atof(a, str, NULL, radix, fe->prec, fe->flags);- done:- JS_FreeCString(ctx, str);- return ret;-}--static JSValue js_bigfloat_isFinite(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValueConst val = argv[0];- JSBigFloat *p;- - if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT)- return JS_FALSE;- p = JS_VALUE_GET_PTR(val);- return JS_NewBool(ctx, bf_is_finite(&p->num));-}--static JSValue js_bigfloat_isNaN(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValueConst val = argv[0];- JSBigFloat *p;- - if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT)- return JS_FALSE;- p = JS_VALUE_GET_PTR(val);- return JS_NewBool(ctx, bf_is_nan(&p->num));-}--enum {- MATH_OP_ABS,- MATH_OP_FLOOR,- MATH_OP_CEIL,- MATH_OP_ROUND,- MATH_OP_TRUNC,- MATH_OP_SQRT,- MATH_OP_FPROUND,- MATH_OP_ACOS,- MATH_OP_ASIN,- MATH_OP_ATAN,- MATH_OP_ATAN2,- MATH_OP_COS,- MATH_OP_EXP,- MATH_OP_LOG,- MATH_OP_POW,- MATH_OP_SIN,- MATH_OP_TAN,- MATH_OP_FMOD,- MATH_OP_REM,- MATH_OP_SIGN,-- MATH_OP_ADD,- MATH_OP_SUB,- MATH_OP_MUL,- MATH_OP_DIV,-};--static JSValue js_bigfloat_fop(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- bf_t a_s, *a, *r;- JSFloatEnv *fe;- int rnd_mode;- JSValue op1, res;-- op1 = JS_ToNumeric(ctx, argv[0]);- if (JS_IsException(op1))- return op1;- a = JS_ToBigFloat(ctx, &a_s, op1);- fe = &ctx->fp_env;- if (argc > 1) {- fe = JS_GetOpaque2(ctx, argv[1], JS_CLASS_FLOAT_ENV);- if (!fe)- goto fail;- }- res = JS_NewBigFloat(ctx);- if (JS_IsException(res)) {- fail:- if (a == &a_s)- bf_delete(a);- JS_FreeValue(ctx, op1);- return JS_EXCEPTION;- }- r = JS_GetBigFloat(res);- switch (magic) {- case MATH_OP_ABS:- bf_set(r, a);- r->sign = 0;- break;- case MATH_OP_FLOOR:- rnd_mode = BF_RNDD;- goto rint;- case MATH_OP_CEIL:- rnd_mode = BF_RNDU;- goto rint;- case MATH_OP_ROUND:- rnd_mode = BF_RNDNA;- goto rint;- case MATH_OP_TRUNC:- rnd_mode = BF_RNDZ;- rint:- bf_set(r, a);- fe->status |= bf_rint(r, rnd_mode);- break;- case MATH_OP_SQRT:- fe->status |= bf_sqrt(r, a, fe->prec, fe->flags);- break;- case MATH_OP_FPROUND:- bf_set(r, a);- fe->status |= bf_round(r, fe->prec, fe->flags);- break;- case MATH_OP_ACOS:- fe->status |= bf_acos(r, a, fe->prec, fe->flags);- break;- case MATH_OP_ASIN:- fe->status |= bf_asin(r, a, fe->prec, fe->flags);- break;- case MATH_OP_ATAN:- fe->status |= bf_atan(r, a, fe->prec, fe->flags);- break;- case MATH_OP_COS:- fe->status |= bf_cos(r, a, fe->prec, fe->flags);- break;- case MATH_OP_EXP:- fe->status |= bf_exp(r, a, fe->prec, fe->flags);- break;- case MATH_OP_LOG:- fe->status |= bf_log(r, a, fe->prec, fe->flags);- break;- case MATH_OP_SIN:- fe->status |= bf_sin(r, a, fe->prec, fe->flags);- break;- case MATH_OP_TAN:- fe->status |= bf_tan(r, a, fe->prec, fe->flags);- break;- case MATH_OP_SIGN:- if (bf_is_nan(a) || bf_is_zero(a)) {- bf_set(r, a);- } else {- bf_set_si(r, 1 - 2 * a->sign);- }- break;- default:- abort();- }- if (a == &a_s)- bf_delete(a);- JS_FreeValue(ctx, op1);- return res;-}--static JSValue js_bigfloat_fop2(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- bf_t a_s, *a, b_s, *b, r_s, *r = &r_s;- JSFloatEnv *fe;- JSValue op1, op2, res;-- op1 = JS_ToNumeric(ctx, argv[0]);- if (JS_IsException(op1))- return op1;- op2 = JS_ToNumeric(ctx, argv[1]);- if (JS_IsException(op2)) {- JS_FreeValue(ctx, op1);- return op2;- }- a = JS_ToBigFloat(ctx, &a_s, op1);- b = JS_ToBigFloat(ctx, &b_s, op2);- fe = &ctx->fp_env;- if (argc > 2) {- fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV);- if (!fe)- goto fail;- }- res = JS_NewBigFloat(ctx);- if (JS_IsException(res)) {- fail:- if (a == &a_s)- bf_delete(a);- if (b == &b_s)- bf_delete(b);- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- return JS_EXCEPTION;- }- r = JS_GetBigFloat(res);- switch (magic) {- case MATH_OP_ATAN2:- fe->status |= bf_atan2(r, a, b, fe->prec, fe->flags);- break;- case MATH_OP_POW:- fe->status |= bf_pow(r, a, b, fe->prec, fe->flags | BF_POW_JS_QUIRKS);- break;- case MATH_OP_FMOD:- fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ);- break;- case MATH_OP_REM:- fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDN);- break;- case MATH_OP_ADD:- fe->status |= bf_add(r, a, b, fe->prec, fe->flags);- break;- case MATH_OP_SUB:- fe->status |= bf_sub(r, a, b, fe->prec, fe->flags);- break;- case MATH_OP_MUL:- fe->status |= bf_mul(r, a, b, fe->prec, fe->flags);- break;- case MATH_OP_DIV:- fe->status |= bf_div(r, a, b, fe->prec, fe->flags);- break;- default:- abort();- }- if (a == &a_s)- bf_delete(a);- if (b == &b_s)- bf_delete(b);- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- return res;-}--static const JSCFunctionListEntry js_bigfloat_funcs[] = {- JS_CGETSET_MAGIC_DEF("PI", js_bigfloat_get_const, NULL, 0 ),- JS_CGETSET_MAGIC_DEF("LN2", js_bigfloat_get_const, NULL, 1 ),- JS_CGETSET_MAGIC_DEF("MIN_VALUE", js_bigfloat_get_const, NULL, 2 ),- JS_CGETSET_MAGIC_DEF("MAX_VALUE", js_bigfloat_get_const, NULL, 3 ),- JS_CGETSET_MAGIC_DEF("EPSILON", js_bigfloat_get_const, NULL, 4 ),- JS_CFUNC_DEF("parseFloat", 1, js_bigfloat_parseFloat ),- JS_CFUNC_DEF("isFinite", 1, js_bigfloat_isFinite ),- JS_CFUNC_DEF("isNaN", 1, js_bigfloat_isNaN ),- JS_CFUNC_MAGIC_DEF("abs", 1, js_bigfloat_fop, MATH_OP_ABS ),- JS_CFUNC_MAGIC_DEF("fpRound", 1, js_bigfloat_fop, MATH_OP_FPROUND ),- JS_CFUNC_MAGIC_DEF("floor", 1, js_bigfloat_fop, MATH_OP_FLOOR ),- JS_CFUNC_MAGIC_DEF("ceil", 1, js_bigfloat_fop, MATH_OP_CEIL ),- JS_CFUNC_MAGIC_DEF("round", 1, js_bigfloat_fop, MATH_OP_ROUND ),- JS_CFUNC_MAGIC_DEF("trunc", 1, js_bigfloat_fop, MATH_OP_TRUNC ),- JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigfloat_fop, MATH_OP_SQRT ),- JS_CFUNC_MAGIC_DEF("acos", 1, js_bigfloat_fop, MATH_OP_ACOS ),- JS_CFUNC_MAGIC_DEF("asin", 1, js_bigfloat_fop, MATH_OP_ASIN ),- JS_CFUNC_MAGIC_DEF("atan", 1, js_bigfloat_fop, MATH_OP_ATAN ),- JS_CFUNC_MAGIC_DEF("atan2", 2, js_bigfloat_fop2, MATH_OP_ATAN2 ),- JS_CFUNC_MAGIC_DEF("cos", 1, js_bigfloat_fop, MATH_OP_COS ),- JS_CFUNC_MAGIC_DEF("exp", 1, js_bigfloat_fop, MATH_OP_EXP ),- JS_CFUNC_MAGIC_DEF("log", 1, js_bigfloat_fop, MATH_OP_LOG ),- JS_CFUNC_MAGIC_DEF("pow", 2, js_bigfloat_fop2, MATH_OP_POW ),- JS_CFUNC_MAGIC_DEF("sin", 1, js_bigfloat_fop, MATH_OP_SIN ),- JS_CFUNC_MAGIC_DEF("tan", 1, js_bigfloat_fop, MATH_OP_TAN ),- JS_CFUNC_MAGIC_DEF("sign", 1, js_bigfloat_fop, MATH_OP_SIGN ),- JS_CFUNC_MAGIC_DEF("add", 2, js_bigfloat_fop2, MATH_OP_ADD ),- JS_CFUNC_MAGIC_DEF("sub", 2, js_bigfloat_fop2, MATH_OP_SUB ),- JS_CFUNC_MAGIC_DEF("mul", 2, js_bigfloat_fop2, MATH_OP_MUL ),- JS_CFUNC_MAGIC_DEF("div", 2, js_bigfloat_fop2, MATH_OP_DIV ),- JS_CFUNC_MAGIC_DEF("fmod", 2, js_bigfloat_fop2, MATH_OP_FMOD ),- JS_CFUNC_MAGIC_DEF("remainder", 2, js_bigfloat_fop2, MATH_OP_REM ),-};--/* FloatEnv */--static JSValue js_float_env_constructor(JSContext *ctx,- JSValueConst new_target,- int argc, JSValueConst *argv)-{- JSValue obj;- JSFloatEnv *fe;- int64_t prec;- int flags, rndmode;-- prec = ctx->fp_env.prec;- flags = ctx->fp_env.flags;- if (!JS_IsUndefined(argv[0])) {- if (JS_ToInt64Sat(ctx, &prec, argv[0]))- return JS_EXCEPTION;- if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)- return JS_ThrowRangeError(ctx, "invalid precision");- flags = BF_RNDN; /* RNDN, max exponent size, no subnormal */- if (argc > 1 && !JS_IsUndefined(argv[1])) {- if (JS_ToInt32Sat(ctx, &rndmode, argv[1]))- return JS_EXCEPTION;- if (rndmode < BF_RNDN || rndmode > BF_RNDF)- return JS_ThrowRangeError(ctx, "invalid rounding mode");- flags = rndmode;- }- }-- obj = JS_NewObjectClass(ctx, JS_CLASS_FLOAT_ENV);- if (JS_IsException(obj))- return JS_EXCEPTION;- fe = js_malloc(ctx, sizeof(*fe));- if (!fe)- return JS_EXCEPTION;- fe->prec = prec;- fe->flags = flags;- fe->status = 0;- JS_SetOpaque(obj, fe);- return obj;-}--static void js_float_env_finalizer(JSRuntime *rt, JSValue val)-{- JSFloatEnv *fe = JS_GetOpaque(val, JS_CLASS_FLOAT_ENV);- js_free_rt(rt, fe);-}--static JSValue js_float_env_get_prec(JSContext *ctx, JSValueConst this_val)-{- return JS_NewInt64(ctx, ctx->fp_env.prec);-}--static JSValue js_float_env_get_expBits(JSContext *ctx, JSValueConst this_val)-{- return JS_NewInt32(ctx, bf_get_exp_bits(ctx->fp_env.flags));-}--static JSValue js_float_env_setPrec(JSContext *ctx,- JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValueConst func;- int exp_bits, flags, saved_flags;- JSValue ret;- limb_t saved_prec;- int64_t prec;-- func = argv[0];- if (JS_ToInt64Sat(ctx, &prec, argv[1]))- return JS_EXCEPTION;- if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)- return JS_ThrowRangeError(ctx, "invalid precision");- exp_bits = BF_EXP_BITS_MAX;-- if (argc > 2 && !JS_IsUndefined(argv[2])) {- if (JS_ToInt32Sat(ctx, &exp_bits, argv[2]))- return JS_EXCEPTION;- if (exp_bits < BF_EXP_BITS_MIN || exp_bits > BF_EXP_BITS_MAX)- return JS_ThrowRangeError(ctx, "invalid number of exponent bits");- }-- flags = BF_RNDN | BF_FLAG_SUBNORMAL | bf_set_exp_bits(exp_bits);-- saved_prec = ctx->fp_env.prec;- saved_flags = ctx->fp_env.flags;-- ctx->fp_env.prec = prec;- ctx->fp_env.flags = flags;-- ret = JS_Call(ctx, func, JS_UNDEFINED, 0, NULL);- /* always restore the floating point precision */- ctx->fp_env.prec = saved_prec;- ctx->fp_env.flags = saved_flags;- return ret;-}--#define FE_PREC (-1)-#define FE_EXP (-2)-#define FE_RNDMODE (-3)-#define FE_SUBNORMAL (-4)--static JSValue js_float_env_proto_get_status(JSContext *ctx, JSValueConst this_val, int magic)-{- JSFloatEnv *fe;- fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);- if (!fe)- return JS_EXCEPTION;- switch(magic) {- case FE_PREC:- return JS_NewInt64(ctx, fe->prec);- case FE_EXP:- return JS_NewInt32(ctx, bf_get_exp_bits(fe->flags));- case FE_RNDMODE:- return JS_NewInt32(ctx, fe->flags & BF_RND_MASK);- case FE_SUBNORMAL:- return JS_NewBool(ctx, (fe->flags & BF_FLAG_SUBNORMAL) != 0);- default:- return JS_NewBool(ctx, (fe->status & magic) != 0);- }-}--static JSValue js_float_env_proto_set_status(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic)-{- JSFloatEnv *fe;- int b;- int64_t prec;-- fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);- if (!fe)- return JS_EXCEPTION;- switch(magic) {- case FE_PREC:- if (JS_ToInt64Sat(ctx, &prec, val))- return JS_EXCEPTION;- if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)- return JS_ThrowRangeError(ctx, "invalid precision");- fe->prec = prec;- break;- case FE_EXP:- if (JS_ToInt32Sat(ctx, &b, val))- return JS_EXCEPTION;- if (b < BF_EXP_BITS_MIN || b > BF_EXP_BITS_MAX)- return JS_ThrowRangeError(ctx, "invalid number of exponent bits");- fe->flags = (fe->flags & ~(BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)) |- bf_set_exp_bits(b);- break;- case FE_RNDMODE:- b = bigfloat_get_rnd_mode(ctx, val);- if (b < 0)- return JS_EXCEPTION;- fe->flags = (fe->flags & ~BF_RND_MASK) | b;- break;- case FE_SUBNORMAL:- b = JS_ToBool(ctx, val);- fe->flags = (fe->flags & ~BF_FLAG_SUBNORMAL) | (b ? BF_FLAG_SUBNORMAL: 0);- break;- default:- b = JS_ToBool(ctx, val);- fe->status = (fe->status & ~magic) & ((-b) & magic);- break;- }- return JS_UNDEFINED;-}--static JSValue js_float_env_clearStatus(JSContext *ctx,- JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSFloatEnv *fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);- if (!fe)- return JS_EXCEPTION;- fe->status = 0;- return JS_UNDEFINED;-}--static const JSCFunctionListEntry js_float_env_funcs[] = {- JS_CGETSET_DEF("prec", js_float_env_get_prec, NULL ),- JS_CGETSET_DEF("expBits", js_float_env_get_expBits, NULL ),- JS_CFUNC_DEF("setPrec", 2, js_float_env_setPrec ),- JS_PROP_INT32_DEF("RNDN", BF_RNDN, 0 ),- JS_PROP_INT32_DEF("RNDZ", BF_RNDZ, 0 ),- JS_PROP_INT32_DEF("RNDU", BF_RNDU, 0 ),- JS_PROP_INT32_DEF("RNDD", BF_RNDD, 0 ),- JS_PROP_INT32_DEF("RNDNA", BF_RNDNA, 0 ),- JS_PROP_INT32_DEF("RNDA", BF_RNDA, 0 ),- JS_PROP_INT32_DEF("RNDF", BF_RNDF, 0 ),- JS_PROP_INT32_DEF("precMin", BF_PREC_MIN, 0 ),- JS_PROP_INT64_DEF("precMax", BF_PREC_MAX, 0 ),- JS_PROP_INT32_DEF("expBitsMin", BF_EXP_BITS_MIN, 0 ),- JS_PROP_INT32_DEF("expBitsMax", BF_EXP_BITS_MAX, 0 ),-};--static const JSCFunctionListEntry js_float_env_proto_funcs[] = {- JS_CGETSET_MAGIC_DEF("prec", js_float_env_proto_get_status,- js_float_env_proto_set_status, FE_PREC ),- JS_CGETSET_MAGIC_DEF("expBits", js_float_env_proto_get_status,- js_float_env_proto_set_status, FE_EXP ),- JS_CGETSET_MAGIC_DEF("rndMode", js_float_env_proto_get_status,- js_float_env_proto_set_status, FE_RNDMODE ),- JS_CGETSET_MAGIC_DEF("subnormal", js_float_env_proto_get_status,- js_float_env_proto_set_status, FE_SUBNORMAL ),- JS_CGETSET_MAGIC_DEF("invalidOperation", js_float_env_proto_get_status,- js_float_env_proto_set_status, BF_ST_INVALID_OP ),- JS_CGETSET_MAGIC_DEF("divideByZero", js_float_env_proto_get_status,- js_float_env_proto_set_status, BF_ST_DIVIDE_ZERO ),- JS_CGETSET_MAGIC_DEF("overflow", js_float_env_proto_get_status,- js_float_env_proto_set_status, BF_ST_OVERFLOW ),- JS_CGETSET_MAGIC_DEF("underflow", js_float_env_proto_get_status,- js_float_env_proto_set_status, BF_ST_UNDERFLOW ),- JS_CGETSET_MAGIC_DEF("inexact", js_float_env_proto_get_status,- js_float_env_proto_set_status, BF_ST_INEXACT ),- JS_CFUNC_DEF("clearStatus", 0, js_float_env_clearStatus ),-};--void JS_AddIntrinsicBigFloat(JSContext *ctx)-{- JSRuntime *rt = ctx->rt;- JSValue obj1;- JSValueConst obj2;- - rt->bigfloat_ops.to_string = js_bigfloat_to_string;- rt->bigfloat_ops.from_string = js_string_to_bigfloat;- rt->bigfloat_ops.unary_arith = js_unary_arith_bigfloat;- rt->bigfloat_ops.binary_arith = js_binary_arith_bigfloat;- rt->bigfloat_ops.compare = js_compare_bigfloat;- rt->bigfloat_ops.mul_pow10_to_float64 = js_mul_pow10_to_float64;- rt->bigfloat_ops.mul_pow10 = js_mul_pow10;- - ctx->class_proto[JS_CLASS_BIG_FLOAT] = JS_NewObject(ctx);- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT],- js_bigfloat_proto_funcs,- countof(js_bigfloat_proto_funcs));- obj1 = JS_NewCFunction(ctx, js_bigfloat_constructor, "BigFloat", 1);- JS_NewGlobalCConstructor2(ctx, obj1, "BigFloat",- ctx->class_proto[JS_CLASS_BIG_FLOAT]);- JS_SetPropertyFunctionList(ctx, obj1, js_bigfloat_funcs,- countof(js_bigfloat_funcs));-- ctx->class_proto[JS_CLASS_FLOAT_ENV] = JS_NewObject(ctx);- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_FLOAT_ENV],- js_float_env_proto_funcs,- countof(js_float_env_proto_funcs));- obj2 = JS_NewGlobalCConstructorOnly(ctx, "BigFloatEnv",- js_float_env_constructor, 1,- ctx->class_proto[JS_CLASS_FLOAT_ENV]);- JS_SetPropertyFunctionList(ctx, obj2, js_float_env_funcs,- countof(js_float_env_funcs));-}--/* BigDecimal */--static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,- BOOL allow_null_or_undefined)-{- redo:- switch(JS_VALUE_GET_NORM_TAG(val)) {- case JS_TAG_BIG_DECIMAL:- break;- case JS_TAG_NULL:- if (!allow_null_or_undefined)- goto fail;- /* fall thru */- case JS_TAG_BOOL:- case JS_TAG_INT:- {- bfdec_t *r;- int32_t v = JS_VALUE_GET_INT(val);-- val = JS_NewBigDecimal(ctx);- if (JS_IsException(val))- break;- r = JS_GetBigDecimal(val);- if (bfdec_set_si(r, v)) {- JS_FreeValue(ctx, val);- val = JS_EXCEPTION;- break;- }- }- break;- case JS_TAG_FLOAT64:- case JS_TAG_BIG_INT:- case JS_TAG_BIG_FLOAT:- val = JS_ToStringFree(ctx, val);- if (JS_IsException(val))- break;- goto redo;- case JS_TAG_STRING:- {- const char *str, *p;- size_t len;- int err;-- str = JS_ToCStringLen(ctx, &len, val);- JS_FreeValue(ctx, val);- if (!str)- return JS_EXCEPTION;- p = str;- p += skip_spaces(p);- if ((p - str) == len) {- bfdec_t *r;- val = JS_NewBigDecimal(ctx);- if (JS_IsException(val))- break;- r = JS_GetBigDecimal(val);- bfdec_set_zero(r, 0);- err = 0;- } else {- val = js_atof(ctx, p, &p, 0, ATOD_TYPE_BIG_DECIMAL);- if (JS_IsException(val)) {- JS_FreeCString(ctx, str);- return JS_EXCEPTION;- }- p += skip_spaces(p);- err = ((p - str) != len);- }- JS_FreeCString(ctx, str);- if (err) {- JS_FreeValue(ctx, val);- return JS_ThrowSyntaxError(ctx, "invalid bigdecimal literal");- }- }- break;- case JS_TAG_OBJECT:- val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);- if (JS_IsException(val))- break;- goto redo;- case JS_TAG_UNDEFINED:- {- bfdec_t *r;- if (!allow_null_or_undefined)- goto fail;- val = JS_NewBigDecimal(ctx);- if (JS_IsException(val))- break;- r = JS_GetBigDecimal(val);- bfdec_set_nan(r);- }- break;- default:- fail:- JS_FreeValue(ctx, val);- return JS_ThrowTypeError(ctx, "cannot convert to bigdecimal");- }- return val;-}--static JSValue js_bigdecimal_constructor(JSContext *ctx,- JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue val;- if (argc == 0) {- bfdec_t *r;- val = JS_NewBigDecimal(ctx);- if (JS_IsException(val))- return val;- r = JS_GetBigDecimal(val);- bfdec_set_zero(r, 0);- } else {- val = JS_ToBigDecimalFree(ctx, JS_DupValue(ctx, argv[0]), FALSE);- }- return val;-}--static JSValue js_thisBigDecimalValue(JSContext *ctx, JSValueConst this_val)-{- if (JS_IsBigDecimal(this_val))- return JS_DupValue(ctx, this_val);-- if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {- JSObject *p = JS_VALUE_GET_OBJ(this_val);- if (p->class_id == JS_CLASS_BIG_DECIMAL) {- if (JS_IsBigDecimal(p->u.object_data))- return JS_DupValue(ctx, p->u.object_data);- }- }- return JS_ThrowTypeError(ctx, "not a bigdecimal");-}--static JSValue js_bigdecimal_toString(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue val;-- val = js_thisBigDecimalValue(ctx, this_val);- if (JS_IsException(val))- return val;- return JS_ToStringFree(ctx, val);-}--static JSValue js_bigdecimal_valueOf(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- return js_thisBigDecimalValue(ctx, this_val);-}--static int js_bigdecimal_get_rnd_mode(JSContext *ctx, JSValueConst obj)-{- const char *str;- size_t size;- int rnd_mode;- - str = JS_ToCStringLen(ctx, &size, obj);- if (!str)- return -1;- if (strlen(str) != size)- goto invalid_rounding_mode;- if (!strcmp(str, "floor")) {- rnd_mode = BF_RNDD;- } else if (!strcmp(str, "ceiling")) {- rnd_mode = BF_RNDU;- } else if (!strcmp(str, "down")) {- rnd_mode = BF_RNDZ;- } else if (!strcmp(str, "up")) {- rnd_mode = BF_RNDA;- } else if (!strcmp(str, "half-even")) {- rnd_mode = BF_RNDN;- } else if (!strcmp(str, "half-up")) {- rnd_mode = BF_RNDNA;- } else {- invalid_rounding_mode:- JS_FreeCString(ctx, str);- JS_ThrowTypeError(ctx, "invalid rounding mode");- return -1;- }- JS_FreeCString(ctx, str);- return rnd_mode;-}--typedef struct {- int64_t prec;- bf_flags_t flags;-} BigDecimalEnv;--static int js_bigdecimal_get_env(JSContext *ctx, BigDecimalEnv *fe,- JSValueConst obj)-{- JSValue prop;- int64_t val;- BOOL has_prec;- int rnd_mode;- - if (!JS_IsObject(obj)) {- JS_ThrowTypeErrorNotAnObject(ctx);- return -1;- }- prop = JS_GetProperty(ctx, obj, JS_ATOM_roundingMode);- if (JS_IsException(prop))- return -1;- rnd_mode = js_bigdecimal_get_rnd_mode(ctx, prop);- JS_FreeValue(ctx, prop);- if (rnd_mode < 0)- return -1;- fe->flags = rnd_mode;- - prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumSignificantDigits);- if (JS_IsException(prop))- return -1;- has_prec = FALSE;- if (!JS_IsUndefined(prop)) {- if (JS_ToInt64SatFree(ctx, &val, prop))- return -1;- if (val < 1 || val > BF_PREC_MAX)- goto invalid_precision;- fe->prec = val;- has_prec = TRUE;- }-- prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumFractionDigits);- if (JS_IsException(prop))- return -1;- if (!JS_IsUndefined(prop)) {- if (has_prec) {- JS_FreeValue(ctx, prop);- JS_ThrowTypeError(ctx, "cannot provide both maximumSignificantDigits and maximumFractionDigits");- return -1;- }- if (JS_ToInt64SatFree(ctx, &val, prop))- return -1;- if (val < 0 || val > BF_PREC_MAX) {- invalid_precision:- JS_ThrowTypeError(ctx, "invalid precision");- return -1;- }- fe->prec = val;- fe->flags |= BF_FLAG_RADPNT_PREC;- has_prec = TRUE;- }- if (!has_prec) {- JS_ThrowTypeError(ctx, "precision must be present");- return -1;- }- return 0;-}---static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv, int magic)-{- bfdec_t *a, *b, r_s, *r = &r_s;- JSValue op1, op2, res;- BigDecimalEnv fe_s, *fe = &fe_s;- int op_count, ret;-- if (magic == MATH_OP_SQRT ||- magic == MATH_OP_ROUND)- op_count = 1;- else- op_count = 2;- - op1 = JS_ToNumeric(ctx, argv[0]);- if (JS_IsException(op1))- return op1;- a = JS_ToBigDecimal(ctx, op1);- if (!a) {- JS_FreeValue(ctx, op1);- return JS_EXCEPTION;- }- if (op_count >= 2) {- op2 = JS_ToNumeric(ctx, argv[1]);- if (JS_IsException(op2)) {- JS_FreeValue(ctx, op1);- return op2;- }- b = JS_ToBigDecimal(ctx, op2);- if (!b)- goto fail;- } else {- op2 = JS_UNDEFINED;- b = NULL;- }- fe->flags = BF_RNDZ;- fe->prec = BF_PREC_INF;- if (op_count < argc) {- if (js_bigdecimal_get_env(ctx, fe, argv[op_count]))- goto fail;- }-- res = JS_NewBigDecimal(ctx);- if (JS_IsException(res)) {- fail:- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- return JS_EXCEPTION;- }- r = JS_GetBigDecimal(res);- switch (magic) {- case MATH_OP_ADD:- ret = bfdec_add(r, a, b, fe->prec, fe->flags);- break;- case MATH_OP_SUB:- ret = bfdec_sub(r, a, b, fe->prec, fe->flags);- break;- case MATH_OP_MUL:- ret = bfdec_mul(r, a, b, fe->prec, fe->flags);- break;- case MATH_OP_DIV:- ret = bfdec_div(r, a, b, fe->prec, fe->flags);- break;- case MATH_OP_FMOD:- ret = bfdec_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ);- break;- case MATH_OP_SQRT:- ret = bfdec_sqrt(r, a, fe->prec, fe->flags);- break;- case MATH_OP_ROUND:- ret = bfdec_set(r, a);- if (!(ret & BF_ST_MEM_ERROR))- ret = bfdec_round(r, fe->prec, fe->flags);- break;- default:- abort();- }- JS_FreeValue(ctx, op1);- JS_FreeValue(ctx, op2);- ret &= BF_ST_MEM_ERROR | BF_ST_DIVIDE_ZERO | BF_ST_INVALID_OP |- BF_ST_OVERFLOW;- if (ret != 0) {- JS_FreeValue(ctx, res);- return throw_bf_exception(ctx, ret);- } else {- return res;- }-}--static JSValue js_bigdecimal_toFixed(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue val, ret;- int64_t f;- int rnd_mode;-- val = js_thisBigDecimalValue(ctx, this_val);- if (JS_IsException(val))- return val;- if (JS_ToInt64Sat(ctx, &f, argv[0]))- goto fail;- if (f < 0 || f > BF_PREC_MAX) {- JS_ThrowRangeError(ctx, "invalid number of digits");- goto fail;- }- rnd_mode = BF_RNDNA;- if (argc > 1) {- rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);- if (rnd_mode < 0)- goto fail;- }- ret = js_bigdecimal_to_string1(ctx, val, f, rnd_mode | BF_FTOA_FORMAT_FRAC);- JS_FreeValue(ctx, val);- return ret;- fail:- JS_FreeValue(ctx, val);- return JS_EXCEPTION;-}--static JSValue js_bigdecimal_toExponential(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue val, ret;- int64_t f;- int rnd_mode;-- val = js_thisBigDecimalValue(ctx, this_val);- if (JS_IsException(val))- return val;- if (JS_ToInt64Sat(ctx, &f, argv[0]))- goto fail;- if (JS_IsUndefined(argv[0])) {- ret = js_bigdecimal_to_string1(ctx, val, 0,- BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP);- } else {- if (f < 0 || f > BF_PREC_MAX) {- JS_ThrowRangeError(ctx, "invalid number of digits");- goto fail;- }- rnd_mode = BF_RNDNA;- if (argc > 1) {- rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);- if (rnd_mode < 0)- goto fail;- }- ret = js_bigdecimal_to_string1(ctx, val, f + 1,- rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP);- }- JS_FreeValue(ctx, val);- return ret;- fail:- JS_FreeValue(ctx, val);- return JS_EXCEPTION;-}--static JSValue js_bigdecimal_toPrecision(JSContext *ctx, JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSValue val, ret;- int64_t p;- int rnd_mode;-- val = js_thisBigDecimalValue(ctx, this_val);- if (JS_IsException(val))- return val;- if (JS_IsUndefined(argv[0])) {- return JS_ToStringFree(ctx, val);- }- if (JS_ToInt64Sat(ctx, &p, argv[0]))- goto fail;- if (p < 1 || p > BF_PREC_MAX) {- JS_ThrowRangeError(ctx, "invalid number of digits");- goto fail;- }- rnd_mode = BF_RNDNA;- if (argc > 1) {- rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);- if (rnd_mode < 0)- goto fail;- }- ret = js_bigdecimal_to_string1(ctx, val, p,- rnd_mode | BF_FTOA_FORMAT_FIXED);- JS_FreeValue(ctx, val);- return ret;- fail:- JS_FreeValue(ctx, val);- return JS_EXCEPTION;-}--static const JSCFunctionListEntry js_bigdecimal_proto_funcs[] = {- JS_CFUNC_DEF("toString", 0, js_bigdecimal_toString ),- JS_CFUNC_DEF("valueOf", 0, js_bigdecimal_valueOf ),- JS_CFUNC_DEF("toPrecision", 1, js_bigdecimal_toPrecision ),- JS_CFUNC_DEF("toFixed", 1, js_bigdecimal_toFixed ),- JS_CFUNC_DEF("toExponential", 1, js_bigdecimal_toExponential ),-};--static const JSCFunctionListEntry js_bigdecimal_funcs[] = {- JS_CFUNC_MAGIC_DEF("add", 2, js_bigdecimal_fop, MATH_OP_ADD ),- JS_CFUNC_MAGIC_DEF("sub", 2, js_bigdecimal_fop, MATH_OP_SUB ),- JS_CFUNC_MAGIC_DEF("mul", 2, js_bigdecimal_fop, MATH_OP_MUL ),- JS_CFUNC_MAGIC_DEF("div", 2, js_bigdecimal_fop, MATH_OP_DIV ),- JS_CFUNC_MAGIC_DEF("mod", 2, js_bigdecimal_fop, MATH_OP_FMOD ),- JS_CFUNC_MAGIC_DEF("round", 1, js_bigdecimal_fop, MATH_OP_ROUND ),- JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigdecimal_fop, MATH_OP_SQRT ),-};--void JS_AddIntrinsicBigDecimal(JSContext *ctx)-{- JSRuntime *rt = ctx->rt;- JSValue obj1;-- rt->bigdecimal_ops.to_string = js_bigdecimal_to_string;- rt->bigdecimal_ops.from_string = js_string_to_bigdecimal;- rt->bigdecimal_ops.unary_arith = js_unary_arith_bigdecimal;- rt->bigdecimal_ops.binary_arith = js_binary_arith_bigdecimal;- rt->bigdecimal_ops.compare = js_compare_bigdecimal;-- ctx->class_proto[JS_CLASS_BIG_DECIMAL] = JS_NewObject(ctx);- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL],- js_bigdecimal_proto_funcs,- countof(js_bigdecimal_proto_funcs));- obj1 = JS_NewCFunction(ctx, js_bigdecimal_constructor, "BigDecimal", 1);- JS_NewGlobalCConstructor2(ctx, obj1, "BigDecimal",- ctx->class_proto[JS_CLASS_BIG_DECIMAL]);- JS_SetPropertyFunctionList(ctx, obj1, js_bigdecimal_funcs,- countof(js_bigdecimal_funcs));-}--void JS_EnableBignumExt(JSContext *ctx, BOOL enable)-{- ctx->bignum_ext = enable;-}--#endif /* CONFIG_BIGNUM */--static const char * const native_error_name[JS_NATIVE_ERROR_COUNT] = {- "EvalError", "RangeError", "ReferenceError",- "SyntaxError", "TypeError", "URIError",- "InternalError", "AggregateError",-};--/* Minimum amount of objects to be able to compile code and display- error messages. No JSAtom should be allocated by this function. */-static void JS_AddIntrinsicBasicObjects(JSContext *ctx)-{- JSValue proto;- int i;-- ctx->class_proto[JS_CLASS_OBJECT] = JS_NewObjectProto(ctx, JS_NULL);- ctx->function_proto = JS_NewCFunction3(ctx, js_function_proto, "", 0,- JS_CFUNC_generic, 0,- ctx->class_proto[JS_CLASS_OBJECT]);- ctx->class_proto[JS_CLASS_BYTECODE_FUNCTION] = JS_DupValue(ctx, ctx->function_proto);- ctx->class_proto[JS_CLASS_ERROR] = JS_NewObject(ctx);-#if 0- /* these are auto-initialized from js_error_proto_funcs,- but delaying might be a problem */- JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ERROR], JS_ATOM_name,- JS_AtomToString(ctx, JS_ATOM_Error),- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);- JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ERROR], JS_ATOM_message,- JS_AtomToString(ctx, JS_ATOM_empty_string),- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);-#endif- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ERROR],- js_error_proto_funcs,- countof(js_error_proto_funcs));-- for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {- proto = JS_NewObjectProto(ctx, ctx->class_proto[JS_CLASS_ERROR]);- JS_DefinePropertyValue(ctx, proto, JS_ATOM_name,- JS_NewAtomString(ctx, native_error_name[i]),- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);- JS_DefinePropertyValue(ctx, proto, JS_ATOM_message,- JS_AtomToString(ctx, JS_ATOM_empty_string),- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);- ctx->native_error_proto[i] = proto;- }-- /* the array prototype is an array */- ctx->class_proto[JS_CLASS_ARRAY] =- JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],- JS_CLASS_ARRAY);-- ctx->array_shape = js_new_shape2(ctx, get_proto_obj(ctx->class_proto[JS_CLASS_ARRAY]),- JS_PROP_INITIAL_HASH_SIZE, 1);- add_shape_property(ctx, &ctx->array_shape, NULL,- JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_LENGTH);-- /* XXX: could test it on first context creation to ensure that no- new atoms are created in JS_AddIntrinsicBasicObjects(). It is- necessary to avoid useless renumbering of atoms after- JS_EvalBinary() if it is done just after- JS_AddIntrinsicBasicObjects(). */- // assert(ctx->rt->atom_count == JS_ATOM_END);-}--void JS_AddIntrinsicBaseObjects(JSContext *ctx)-{- int i;- JSValueConst obj, number_obj;- JSValue obj1;-- ctx->throw_type_error = JS_NewCFunction(ctx, js_throw_type_error, NULL, 0);-- /* add caller and arguments properties to throw a TypeError */- obj1 = JS_NewCFunction(ctx, js_function_proto_caller, NULL, 0);- JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_caller, JS_UNDEFINED,- obj1, ctx->throw_type_error,- JS_PROP_HAS_GET | JS_PROP_HAS_SET |- JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE);- JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_arguments, JS_UNDEFINED,- obj1, ctx->throw_type_error,- JS_PROP_HAS_GET | JS_PROP_HAS_SET |- JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE);- JS_FreeValue(ctx, obj1);- JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, (JSValueConst *)&ctx->throw_type_error, 1));-- ctx->global_obj = JS_NewObject(ctx);- ctx->global_var_obj = JS_NewObjectProto(ctx, JS_NULL);-- /* Object */- obj = JS_NewGlobalCConstructor(ctx, "Object", js_object_constructor, 1,- ctx->class_proto[JS_CLASS_OBJECT]);- JS_SetPropertyFunctionList(ctx, obj, js_object_funcs, countof(js_object_funcs));- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_OBJECT],- js_object_proto_funcs, countof(js_object_proto_funcs));-- /* Function */- JS_SetPropertyFunctionList(ctx, ctx->function_proto, js_function_proto_funcs, countof(js_function_proto_funcs));- ctx->function_ctor = JS_NewCFunctionMagic(ctx, js_function_constructor,- "Function", 1, JS_CFUNC_constructor_or_func_magic,- JS_FUNC_NORMAL);- JS_NewGlobalCConstructor2(ctx, JS_DupValue(ctx, ctx->function_ctor), "Function",- ctx->function_proto);-- /* Error */- obj1 = JS_NewCFunctionMagic(ctx, js_error_constructor,- "Error", 1, JS_CFUNC_constructor_or_func_magic, -1);- JS_NewGlobalCConstructor2(ctx, obj1,- "Error", ctx->class_proto[JS_CLASS_ERROR]);-- for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {- JSValue func_obj;- int n_args;- n_args = 1 + (i == JS_AGGREGATE_ERROR);- func_obj = JS_NewCFunction3(ctx, (JSCFunction *)js_error_constructor,- native_error_name[i], n_args,- JS_CFUNC_constructor_or_func_magic, i, obj1);- JS_NewGlobalCConstructor2(ctx, func_obj, native_error_name[i],- ctx->native_error_proto[i]);- }-- /* Iterator prototype */- ctx->iterator_proto = JS_NewObject(ctx);- JS_SetPropertyFunctionList(ctx, ctx->iterator_proto,- js_iterator_proto_funcs,- countof(js_iterator_proto_funcs));-- /* Array */- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY],- js_array_proto_funcs,- countof(js_array_proto_funcs));-- obj = JS_NewGlobalCConstructor(ctx, "Array", js_array_constructor, 1,- ctx->class_proto[JS_CLASS_ARRAY]);- ctx->array_ctor = JS_DupValue(ctx, obj);- JS_SetPropertyFunctionList(ctx, obj, js_array_funcs,- countof(js_array_funcs));-- /* XXX: create auto_initializer */- {- /* initialize Array.prototype[Symbol.unscopables] */- char const unscopables[] = "copyWithin" "\0" "entries" "\0" "fill" "\0" "find" "\0"- "findIndex" "\0" "flat" "\0" "flatMap" "\0" "includes" "\0" "keys" "\0" "values" "\0";- const char *p = unscopables;- obj1 = JS_NewObjectProto(ctx, JS_NULL);- for(p = unscopables; *p; p += strlen(p) + 1) {- JS_DefinePropertyValueStr(ctx, obj1, p, JS_TRUE, JS_PROP_C_W_E);- }- JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ARRAY],- JS_ATOM_Symbol_unscopables, obj1,- JS_PROP_CONFIGURABLE);- }-- /* needed to initialize arguments[Symbol.iterator] */- ctx->array_proto_values =- JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_values);-- ctx->class_proto[JS_CLASS_ARRAY_ITERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_ITERATOR],- js_array_iterator_proto_funcs,- countof(js_array_iterator_proto_funcs));-- /* parseFloat and parseInteger must be defined before Number- because of the Number.parseFloat and Number.parseInteger- aliases */- JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_global_funcs,- countof(js_global_funcs));-- /* Number */- ctx->class_proto[JS_CLASS_NUMBER] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],- JS_CLASS_NUMBER);- JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_NUMBER], JS_NewInt32(ctx, 0));- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_NUMBER],- js_number_proto_funcs,- countof(js_number_proto_funcs));- number_obj = JS_NewGlobalCConstructor(ctx, "Number", js_number_constructor, 1,- ctx->class_proto[JS_CLASS_NUMBER]);- JS_SetPropertyFunctionList(ctx, number_obj, js_number_funcs, countof(js_number_funcs));-- /* Boolean */- ctx->class_proto[JS_CLASS_BOOLEAN] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],- JS_CLASS_BOOLEAN);- JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_BOOLEAN], JS_NewBool(ctx, FALSE));- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BOOLEAN], js_boolean_proto_funcs,- countof(js_boolean_proto_funcs));- JS_NewGlobalCConstructor(ctx, "Boolean", js_boolean_constructor, 1,- ctx->class_proto[JS_CLASS_BOOLEAN]);-- /* String */- ctx->class_proto[JS_CLASS_STRING] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],- JS_CLASS_STRING);- JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_STRING], JS_AtomToString(ctx, JS_ATOM_empty_string));- obj = JS_NewGlobalCConstructor(ctx, "String", js_string_constructor, 1,- ctx->class_proto[JS_CLASS_STRING]);- JS_SetPropertyFunctionList(ctx, obj, js_string_funcs,- countof(js_string_funcs));- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_funcs,- countof(js_string_proto_funcs));-- ctx->class_proto[JS_CLASS_STRING_ITERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING_ITERATOR],- js_string_iterator_proto_funcs,- countof(js_string_iterator_proto_funcs));-- /* Math: create as autoinit object */- js_random_init(ctx);- JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_math_obj, countof(js_math_obj));-- /* ES6 Reflect: create as autoinit object */- JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_reflect_obj, countof(js_reflect_obj));-- /* ES6 Symbol */- ctx->class_proto[JS_CLASS_SYMBOL] = JS_NewObject(ctx);- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SYMBOL], js_symbol_proto_funcs,- countof(js_symbol_proto_funcs));- obj = JS_NewGlobalCConstructor(ctx, "Symbol", js_symbol_constructor, 0,- ctx->class_proto[JS_CLASS_SYMBOL]);- JS_SetPropertyFunctionList(ctx, obj, js_symbol_funcs,- countof(js_symbol_funcs));- for(i = JS_ATOM_Symbol_toPrimitive; i < JS_ATOM_END; i++) {- char buf[ATOM_GET_STR_BUF_SIZE];- const char *str, *p;- str = JS_AtomGetStr(ctx, buf, sizeof(buf), i);- /* skip "Symbol." */- p = strchr(str, '.');- if (p)- str = p + 1;- JS_DefinePropertyValueStr(ctx, obj, str, JS_AtomToValue(ctx, i), 0);- }-- /* ES6 Generator */- ctx->class_proto[JS_CLASS_GENERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_GENERATOR],- js_generator_proto_funcs,- countof(js_generator_proto_funcs));-- ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto);- obj1 = JS_NewCFunctionMagic(ctx, js_function_constructor,- "GeneratorFunction", 1,- JS_CFUNC_constructor_or_func_magic, JS_FUNC_GENERATOR);- JS_SetPropertyFunctionList(ctx,- ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],- js_generator_function_proto_funcs,- countof(js_generator_function_proto_funcs));- JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],- ctx->class_proto[JS_CLASS_GENERATOR],- JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE);- JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],- 0, JS_PROP_CONFIGURABLE);- JS_FreeValue(ctx, obj1);-- /* global properties */- ctx->eval_obj = JS_NewCFunction(ctx, js_global_eval, "eval", 1);- JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_eval,- JS_DupValue(ctx, ctx->eval_obj),- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);-- JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_globalThis,- JS_DupValue(ctx, ctx->global_obj),- JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);-}--/* Typed Arrays */--static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT] = {- 0, 0, 0, 1, 1, 2, 2,-#ifdef CONFIG_BIGNUM- 3, 3, /* BigInt64Array, BigUint64Array */-#endif- 2, 3-};--static JSValue js_array_buffer_constructor3(JSContext *ctx,- JSValueConst new_target,- uint64_t len, JSClassID class_id,- uint8_t *buf,- JSFreeArrayBufferDataFunc *free_func,- void *opaque, BOOL alloc_flag)-{- JSRuntime *rt = ctx->rt;- JSValue obj;- JSArrayBuffer *abuf = NULL;-- obj = js_create_from_ctor(ctx, new_target, class_id);- if (JS_IsException(obj))- return obj;- /* XXX: we are currently limited to 2 GB */- if (len > INT32_MAX) {- JS_ThrowRangeError(ctx, "invalid array buffer length");- goto fail;- }- abuf = js_malloc(ctx, sizeof(*abuf));- if (!abuf)- goto fail;- abuf->byte_length = len;- if (alloc_flag) {- if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER &&- rt->sab_funcs.sab_alloc) {- abuf->data = rt->sab_funcs.sab_alloc(rt->sab_funcs.sab_opaque,- max_int(len, 1));- if (!abuf->data)- goto fail;- memset(abuf->data, 0, len);- } else {- /* the allocation must be done after the object creation */- abuf->data = js_mallocz(ctx, max_int(len, 1));- if (!abuf->data)- goto fail;- }- } else {- if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER &&- rt->sab_funcs.sab_dup) {- rt->sab_funcs.sab_dup(rt->sab_funcs.sab_opaque, buf);- }- abuf->data = buf;- }- init_list_head(&abuf->array_list);- abuf->detached = FALSE;- abuf->shared = (class_id == JS_CLASS_SHARED_ARRAY_BUFFER);- abuf->opaque = opaque;- abuf->free_func = free_func;- if (alloc_flag && buf)- memcpy(abuf->data, buf, len);- JS_SetOpaque(obj, abuf);- return obj;- fail:- JS_FreeValue(ctx, obj);- js_free(ctx, abuf);- return JS_EXCEPTION;-}--static void js_array_buffer_free(JSRuntime *rt, void *opaque, void *ptr)-{- js_free_rt(rt, ptr);-}--static JSValue js_array_buffer_constructor2(JSContext *ctx,- JSValueConst new_target,- uint64_t len, JSClassID class_id)-{- return js_array_buffer_constructor3(ctx, new_target, len, class_id,- NULL, js_array_buffer_free, NULL,- TRUE);-}--static JSValue js_array_buffer_constructor1(JSContext *ctx,- JSValueConst new_target,- uint64_t len)-{- return js_array_buffer_constructor2(ctx, new_target, len,- JS_CLASS_ARRAY_BUFFER);-}--JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len,- JSFreeArrayBufferDataFunc *free_func, void *opaque,- BOOL is_shared)-{- return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len,- is_shared ? JS_CLASS_SHARED_ARRAY_BUFFER : JS_CLASS_ARRAY_BUFFER,- buf, free_func, opaque, FALSE);-}--/* create a new ArrayBuffer of length 'len' and copy 'buf' to it */-JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len)-{- return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len,- JS_CLASS_ARRAY_BUFFER,- (uint8_t *)buf,- js_array_buffer_free, NULL,- TRUE);-}--static JSValue js_array_buffer_constructor(JSContext *ctx,- JSValueConst new_target,- int argc, JSValueConst *argv)-{- uint64_t len;- if (JS_ToIndex(ctx, &len, argv[0]))- return JS_EXCEPTION;- return js_array_buffer_constructor1(ctx, new_target, len);-}--static JSValue js_shared_array_buffer_constructor(JSContext *ctx,- JSValueConst new_target,- int argc, JSValueConst *argv)-{- uint64_t len;- if (JS_ToIndex(ctx, &len, argv[0]))- return JS_EXCEPTION;- return js_array_buffer_constructor2(ctx, new_target, len,- JS_CLASS_SHARED_ARRAY_BUFFER);-}--/* also used for SharedArrayBuffer */-static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val)-{- JSObject *p = JS_VALUE_GET_OBJ(val);- JSArrayBuffer *abuf = p->u.array_buffer;- if (abuf) {- /* The ArrayBuffer finalizer may be called before the typed- array finalizers using it, so abuf->array_list is not- necessarily empty. */- // assert(list_empty(&abuf->array_list));- if (abuf->shared && rt->sab_funcs.sab_free) {- rt->sab_funcs.sab_free(rt->sab_funcs.sab_opaque, abuf->data);- } else {- if (abuf->free_func)- abuf->free_func(rt, abuf->opaque, abuf->data);- }- js_free_rt(rt, abuf);- }-}--static JSValue js_array_buffer_isView(JSContext *ctx,- JSValueConst this_val,- int argc, JSValueConst *argv)-{- JSObject *p;- BOOL res;- res = FALSE;- if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) {- p = JS_VALUE_GET_OBJ(argv[0]);- if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&- p->class_id <= JS_CLASS_DATAVIEW) {- res = TRUE;- }- }- return JS_NewBool(ctx, res);-}--static const JSCFunctionListEntry js_array_buffer_funcs[] = {- JS_CFUNC_DEF("isView", 1, js_array_buffer_isView ),- JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),-};--static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx)-{- return JS_ThrowTypeError(ctx, "ArrayBuffer is detached");-}--static JSValue js_array_buffer_get_byteLength(JSContext *ctx,- JSValueConst this_val,- int class_id)-{- JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id);- if (!abuf)- return JS_EXCEPTION;- if (abuf->detached)- return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+#define JS_STACK_SIZE_MAX 65534+#define JS_STRING_LEN_MAX ((1 << 30) - 1)++#define __exception __attribute__((warn_unused_result))++typedef struct JSShape JSShape;+typedef struct JSString JSString;+typedef struct JSString JSAtomStruct;++typedef enum {+ JS_GC_PHASE_NONE,+ JS_GC_PHASE_DECREF,+ JS_GC_PHASE_REMOVE_CYCLES,+} JSGCPhaseEnum;++typedef enum OPCodeEnum OPCodeEnum;++#ifdef CONFIG_BIGNUM+/* function pointers are used for numeric operations so that it is+ possible to remove some numeric types */+typedef struct {+ JSValue (*to_string)(JSContext *ctx, JSValueConst val);+ JSValue (*from_string)(JSContext *ctx, const char *buf,+ int radix, int flags, slimb_t *pexponent);+ int (*unary_arith)(JSContext *ctx,+ JSValue *pres, OPCodeEnum op, JSValue op1);+ int (*binary_arith)(JSContext *ctx, OPCodeEnum op,+ JSValue *pres, JSValue op1, JSValue op2);+ int (*compare)(JSContext *ctx, OPCodeEnum op,+ JSValue op1, JSValue op2);+ /* only for bigfloat: */+ JSValue (*mul_pow10_to_float64)(JSContext *ctx, const bf_t *a,+ int64_t exponent);+ int (*mul_pow10)(JSContext *ctx, JSValue *sp);+} JSNumericOperations;+#endif++struct JSRuntime {+ JSMallocFunctions mf;+ JSMallocState malloc_state;+ const char *rt_info;++ int atom_hash_size; /* power of two */+ int atom_count;+ int atom_size;+ int atom_count_resize; /* resize hash table at this count */+ uint32_t *atom_hash;+ JSAtomStruct **atom_array;+ int atom_free_index; /* 0 = none */++ int class_count; /* size of class_array */+ JSClass *class_array;++ struct list_head context_list; /* list of JSContext.link */+ /* list of JSGCObjectHeader.link. List of allocated GC objects (used+ by the garbage collector) */+ struct list_head gc_obj_list;+ /* list of JSGCObjectHeader.link. Used during JS_FreeValueRT() */+ struct list_head gc_zero_ref_count_list; + struct list_head tmp_obj_list; /* used during GC */+ JSGCPhaseEnum gc_phase : 8;+ size_t malloc_gc_threshold;+#ifdef DUMP_LEAKS+ struct list_head string_list; /* list of JSString.link */+#endif+ /* stack limitation */+ const uint8_t *stack_top;+ size_t stack_size; /* in bytes */++ JSValue current_exception;+ /* true if inside an out of memory error, to avoid recursing */+ BOOL in_out_of_memory : 8;++ struct JSStackFrame *current_stack_frame;++ JSInterruptHandler *interrupt_handler;+ void *interrupt_opaque;++ JSHostPromiseRejectionTracker *host_promise_rejection_tracker;+ void *host_promise_rejection_tracker_opaque;+ + struct list_head job_list; /* list of JSJobEntry.link */++ JSModuleNormalizeFunc *module_normalize_func;+ JSModuleLoaderFunc *module_loader_func;+ void *module_loader_opaque;++ BOOL can_block : 8; /* TRUE if Atomics.wait can block */+ /* used to allocate, free and clone SharedArrayBuffers */+ JSSharedArrayBufferFunctions sab_funcs;+ + /* Shape hash table */+ int shape_hash_bits;+ int shape_hash_size;+ int shape_hash_count; /* number of hashed shapes */+ JSShape **shape_hash;+#ifdef CONFIG_BIGNUM+ bf_context_t bf_ctx;+ JSNumericOperations bigint_ops;+ JSNumericOperations bigfloat_ops;+ JSNumericOperations bigdecimal_ops;+ uint32_t operator_count;+#endif+ void *user_opaque;+};++struct JSClass {+ uint32_t class_id; /* 0 means free entry */+ JSAtom class_name;+ JSClassFinalizer *finalizer;+ JSClassGCMark *gc_mark;+ JSClassCall *call;+ /* pointers for exotic behavior, can be NULL if none are present */+ const JSClassExoticMethods *exotic;+};++#define JS_MODE_STRICT (1 << 0)+#define JS_MODE_STRIP (1 << 1)+#define JS_MODE_MATH (1 << 2)++typedef struct JSStackFrame {+ struct JSStackFrame *prev_frame; /* NULL if first stack frame */+ JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */+ JSValue *arg_buf; /* arguments */+ JSValue *var_buf; /* variables */+ struct list_head var_ref_list; /* list of JSVarRef.link */+ const uint8_t *cur_pc; /* only used in bytecode functions : PC of the+ instruction after the call */+ int arg_count;+ int js_mode; /* 0 or JS_MODE_MATH for C functions */+ /* only used in generators. Current stack pointer value. NULL if+ the function is running. */ + JSValue *cur_sp;+} JSStackFrame;++typedef enum {+ JS_GC_OBJ_TYPE_JS_OBJECT,+ JS_GC_OBJ_TYPE_FUNCTION_BYTECODE,+ JS_GC_OBJ_TYPE_SHAPE,+ JS_GC_OBJ_TYPE_VAR_REF,+ JS_GC_OBJ_TYPE_ASYNC_FUNCTION,+ JS_GC_OBJ_TYPE_JS_CONTEXT,+} JSGCObjectTypeEnum;++/* header for GC objects. GC objects are C data structures with a+ reference count that can reference other GC objects. JS Objects are+ a particular type of GC object. */+struct JSGCObjectHeader {+ int ref_count; /* must come first, 32-bit */+ JSGCObjectTypeEnum gc_obj_type : 4;+ uint8_t mark : 4; /* used by the GC */+ uint8_t dummy1; /* not used by the GC */+ uint16_t dummy2; /* not used by the GC */+ struct list_head link;+};++typedef struct JSVarRef {+ union {+ JSGCObjectHeader header; /* must come first */+ struct {+ int __gc_ref_count; /* corresponds to header.ref_count */+ uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */++ /* 0 : the JSVarRef is on the stack. header.link is an element+ of JSStackFrame.var_ref_list.+ 1 : the JSVarRef is detached. header.link has the normal meanning + */+ uint8_t is_detached : 1; + uint8_t is_arg : 1;+ uint16_t var_idx; /* index of the corresponding function variable on+ the stack */+ };+ };+ JSValue *pvalue; /* pointer to the value, either on the stack or+ to 'value' */+ JSValue value; /* used when the variable is no longer on the stack */+} JSVarRef;++#ifdef CONFIG_BIGNUM+typedef struct JSFloatEnv {+ limb_t prec;+ bf_flags_t flags;+ unsigned int status;+} JSFloatEnv;++/* the same structure is used for big integers and big floats. Big+ integers are never infinite or NaNs */+typedef struct JSBigFloat {+ JSRefCountHeader header; /* must come first, 32-bit */+ bf_t num;+} JSBigFloat;++typedef struct JSBigDecimal {+ JSRefCountHeader header; /* must come first, 32-bit */+ bfdec_t num;+} JSBigDecimal;+#endif++typedef enum {+ JS_AUTOINIT_ID_PROTOTYPE,+ JS_AUTOINIT_ID_MODULE_NS,+ JS_AUTOINIT_ID_PROP,+} JSAutoInitIDEnum;++/* must be large enough to have a negligible runtime cost and small+ enough to call the interrupt callback often. */+#define JS_INTERRUPT_COUNTER_INIT 10000++struct JSContext {+ JSGCObjectHeader header; /* must come first */+ JSRuntime *rt;+ struct list_head link;++ uint16_t binary_object_count;+ int binary_object_size;++ JSShape *array_shape; /* initial shape for Array objects */++ JSValue *class_proto;+ JSValue function_proto;+ JSValue function_ctor;+ JSValue array_ctor;+ JSValue regexp_ctor;+ JSValue promise_ctor;+ JSValue native_error_proto[JS_NATIVE_ERROR_COUNT];+ JSValue iterator_proto;+ JSValue async_iterator_proto;+ JSValue array_proto_values;+ JSValue throw_type_error;+ JSValue eval_obj;++ JSValue global_obj; /* global object */+ JSValue global_var_obj; /* contains the global let/const definitions */++ uint64_t random_state;+#ifdef CONFIG_BIGNUM+ bf_context_t *bf_ctx; /* points to rt->bf_ctx, shared by all contexts */+ JSFloatEnv fp_env; /* global FP environment */+ BOOL bignum_ext : 8; /* enable math mode */+ BOOL allow_operator_overloading : 8;+#endif+ /* when the counter reaches zero, JSRutime.interrupt_handler is called */+ int interrupt_counter;+ BOOL is_error_property_enabled;++ struct list_head loaded_modules; /* list of JSModuleDef.link */++ /* if NULL, RegExp compilation is not supported */+ JSValue (*compile_regexp)(JSContext *ctx, JSValueConst pattern,+ JSValueConst flags);+ /* if NULL, eval is not supported */+ JSValue (*eval_internal)(JSContext *ctx, JSValueConst this_obj,+ const char *input, size_t input_len,+ const char *filename, int flags, int scope_idx);+ void *user_opaque;+};++typedef union JSFloat64Union {+ double d;+ uint64_t u64;+ uint32_t u32[2];+} JSFloat64Union;++enum {+ JS_ATOM_TYPE_STRING = 1,+ JS_ATOM_TYPE_GLOBAL_SYMBOL,+ JS_ATOM_TYPE_SYMBOL,+ JS_ATOM_TYPE_PRIVATE,+};++enum {+ JS_ATOM_HASH_SYMBOL,+ JS_ATOM_HASH_PRIVATE,+};++typedef enum {+ JS_ATOM_KIND_STRING,+ JS_ATOM_KIND_SYMBOL,+ JS_ATOM_KIND_PRIVATE,+} JSAtomKindEnum;++#define JS_ATOM_HASH_MASK ((1 << 30) - 1)++struct JSString {+ JSRefCountHeader header; /* must come first, 32-bit */+ uint32_t len : 31;+ uint8_t is_wide_char : 1; /* 0 = 8 bits, 1 = 16 bits characters */+ /* for JS_ATOM_TYPE_SYMBOL: hash = 0, atom_type = 3,+ for JS_ATOM_TYPE_PRIVATE: hash = 1, atom_type = 3+ XXX: could change encoding to have one more bit in hash */+ uint32_t hash : 30;+ uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */+ uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */+#ifdef DUMP_LEAKS+ struct list_head link; /* string list */+#endif+ union {+ uint8_t str8[0]; /* 8 bit strings will get an extra null terminator */+ uint16_t str16[0];+ } u;+};++typedef struct JSClosureVar {+ uint8_t is_local : 1;+ uint8_t is_arg : 1;+ uint8_t is_const : 1;+ uint8_t is_lexical : 1;+ uint8_t var_kind : 4; /* see JSVarKindEnum */+ /* 8 bits available */+ uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the+ parent function. otherwise: index to a closure+ variable of the parent function */+ JSAtom var_name;+} JSClosureVar;++#define ARG_SCOPE_INDEX 1+#define ARG_SCOPE_END (-2)++typedef struct JSVarScope {+ int parent; /* index into fd->scopes of the enclosing scope */+ int first; /* index into fd->vars of the last variable in this scope */+} JSVarScope;++typedef enum {+ /* XXX: add more variable kinds here instead of using bit fields */+ JS_VAR_NORMAL,+ JS_VAR_FUNCTION_DECL, /* lexical var with function declaration */+ JS_VAR_NEW_FUNCTION_DECL, /* lexical var with async/generator+ function declaration */+ JS_VAR_CATCH,+ JS_VAR_FUNCTION_NAME, /* function expression name */+ JS_VAR_PRIVATE_FIELD,+ JS_VAR_PRIVATE_METHOD,+ JS_VAR_PRIVATE_GETTER,+ JS_VAR_PRIVATE_SETTER, /* must come after JS_VAR_PRIVATE_GETTER */+ JS_VAR_PRIVATE_GETTER_SETTER, /* must come after JS_VAR_PRIVATE_SETTER */+} JSVarKindEnum;++/* XXX: could use a different structure in bytecode functions to save+ memory */+typedef struct JSVarDef {+ JSAtom var_name;+ /* index into fd->scopes of this variable lexical scope */+ int scope_level;+ /* during compilation: + - if scope_level = 0: scope in which the variable is defined+ - if scope_level != 0: index into fd->vars of the next+ variable in the same or enclosing lexical scope+ in a bytecode function:+ index into fd->vars of the next+ variable in the same or enclosing lexical scope+ */+ int scope_next; + uint8_t is_const : 1;+ uint8_t is_lexical : 1;+ uint8_t is_captured : 1;+ uint8_t var_kind : 4; /* see JSVarKindEnum */+ /* only used during compilation: function pool index for lexical+ variables with var_kind =+ JS_VAR_FUNCTION_DECL/JS_VAR_NEW_FUNCTION_DECL or scope level of+ the definition of the 'var' variables (they have scope_level =+ 0) */+ int func_pool_idx : 24; /* only used during compilation : index in+ the constant pool for hoisted function+ definition */+} JSVarDef;++/* for the encoding of the pc2line table */+#define PC2LINE_BASE (-1)+#define PC2LINE_RANGE 5+#define PC2LINE_OP_FIRST 1+#define PC2LINE_DIFF_PC_MAX ((255 - PC2LINE_OP_FIRST) / PC2LINE_RANGE)++typedef enum JSFunctionKindEnum {+ JS_FUNC_NORMAL = 0,+ JS_FUNC_GENERATOR = (1 << 0),+ JS_FUNC_ASYNC = (1 << 1),+ JS_FUNC_ASYNC_GENERATOR = (JS_FUNC_GENERATOR | JS_FUNC_ASYNC),+} JSFunctionKindEnum;++typedef struct JSFunctionBytecode {+ JSGCObjectHeader header; /* must come first */+ uint8_t js_mode;+ uint8_t has_prototype : 1; /* true if a prototype field is necessary */+ uint8_t has_simple_parameter_list : 1;+ uint8_t is_derived_class_constructor : 1;+ /* true if home_object needs to be initialized */+ uint8_t need_home_object : 1;+ uint8_t func_kind : 2;+ uint8_t new_target_allowed : 1;+ uint8_t super_call_allowed : 1;+ uint8_t super_allowed : 1;+ uint8_t arguments_allowed : 1;+ uint8_t has_debug : 1;+ uint8_t backtrace_barrier : 1; /* stop backtrace on this function */+ uint8_t read_only_bytecode : 1;+ /* XXX: 4 bits available */+ uint8_t *byte_code_buf; /* (self pointer) */+ int byte_code_len;+ JSAtom func_name;+ JSVarDef *vardefs; /* arguments + local variables (arg_count + var_count) (self pointer) */+ JSClosureVar *closure_var; /* list of variables in the closure (self pointer) */+ uint16_t arg_count;+ uint16_t var_count;+ uint16_t defined_arg_count; /* for length function property */+ uint16_t stack_size; /* maximum stack size */+ JSContext *realm; /* function realm */+ JSValue *cpool; /* constant pool (self pointer) */+ int cpool_count;+ int closure_var_count;+ struct {+ /* debug info, move to separate structure to save memory? */+ JSAtom filename;+ int line_num;+ int source_len;+ int pc2line_len;+ uint8_t *pc2line_buf;+ char *source;+ } debug;+} JSFunctionBytecode;++typedef struct JSBoundFunction {+ JSValue func_obj;+ JSValue this_val;+ int argc;+ JSValue argv[0];+} JSBoundFunction;++typedef enum JSIteratorKindEnum {+ JS_ITERATOR_KIND_KEY,+ JS_ITERATOR_KIND_VALUE,+ JS_ITERATOR_KIND_KEY_AND_VALUE,+} JSIteratorKindEnum;++typedef struct JSForInIterator {+ JSValue obj;+ BOOL is_array;+ uint32_t array_length;+ uint32_t idx;+} JSForInIterator;++typedef struct JSRegExp {+ JSString *pattern;+ JSString *bytecode; /* also contains the flags */+} JSRegExp;++typedef struct JSProxyData {+ JSValue target;+ JSValue handler;+ uint8_t is_func;+ uint8_t is_revoked;+} JSProxyData;++typedef struct JSArrayBuffer {+ int byte_length; /* 0 if detached */+ uint8_t detached;+ uint8_t shared; /* if shared, the array buffer cannot be detached */+ uint8_t *data; /* NULL if detached */+ struct list_head array_list;+ void *opaque;+ JSFreeArrayBufferDataFunc *free_func;+} JSArrayBuffer;++typedef struct JSTypedArray {+ struct list_head link; /* link to arraybuffer */+ JSObject *obj; /* back pointer to the TypedArray/DataView object */+ JSObject *buffer; /* based array buffer */+ uint32_t offset; /* offset in the array buffer */+ uint32_t length; /* length in the array buffer */+} JSTypedArray;++typedef struct JSAsyncFunctionState {+ JSValue this_val; /* 'this' generator argument */+ int argc; /* number of function arguments */+ BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */+ JSStackFrame frame;+} JSAsyncFunctionState;++/* XXX: could use an object instead to avoid the+ JS_TAG_ASYNC_FUNCTION tag for the GC */+typedef struct JSAsyncFunctionData {+ JSGCObjectHeader header; /* must come first */+ JSValue resolving_funcs[2];+ BOOL is_active; /* true if the async function state is valid */+ JSAsyncFunctionState func_state;+} JSAsyncFunctionData;++typedef enum {+ /* binary operators */+ JS_OVOP_ADD,+ JS_OVOP_SUB,+ JS_OVOP_MUL,+ JS_OVOP_DIV,+ JS_OVOP_MOD,+ JS_OVOP_POW,+ JS_OVOP_OR,+ JS_OVOP_AND,+ JS_OVOP_XOR,+ JS_OVOP_SHL,+ JS_OVOP_SAR,+ JS_OVOP_SHR,+ JS_OVOP_EQ,+ JS_OVOP_LESS,++ JS_OVOP_BINARY_COUNT,+ /* unary operators */+ JS_OVOP_POS = JS_OVOP_BINARY_COUNT,+ JS_OVOP_NEG,+ JS_OVOP_INC,+ JS_OVOP_DEC,+ JS_OVOP_NOT,++ JS_OVOP_COUNT,+} JSOverloadableOperatorEnum;++typedef struct {+ uint32_t operator_index;+ JSObject *ops[JS_OVOP_BINARY_COUNT]; /* self operators */+} JSBinaryOperatorDefEntry;++typedef struct {+ int count;+ JSBinaryOperatorDefEntry *tab;+} JSBinaryOperatorDef;++typedef struct {+ uint32_t operator_counter;+ BOOL is_primitive; /* OperatorSet for a primitive type */+ /* NULL if no operator is defined */+ JSObject *self_ops[JS_OVOP_COUNT]; /* self operators */+ JSBinaryOperatorDef left;+ JSBinaryOperatorDef right;+} JSOperatorSetData;++typedef struct JSReqModuleEntry {+ JSAtom module_name;+ JSModuleDef *module; /* used using resolution */+} JSReqModuleEntry;++typedef enum JSExportTypeEnum {+ JS_EXPORT_TYPE_LOCAL,+ JS_EXPORT_TYPE_INDIRECT,+} JSExportTypeEnum;++typedef struct JSExportEntry {+ union {+ struct {+ int var_idx; /* closure variable index */+ JSVarRef *var_ref; /* if != NULL, reference to the variable */+ } local; /* for local export */+ int req_module_idx; /* module for indirect export */+ } u;+ JSExportTypeEnum export_type;+ JSAtom local_name; /* '*' if export ns from. not used for local+ export after compilation */+ JSAtom export_name; /* exported variable name */+} JSExportEntry;++typedef struct JSStarExportEntry {+ int req_module_idx; /* in req_module_entries */+} JSStarExportEntry;++typedef struct JSImportEntry {+ int var_idx; /* closure variable index */+ JSAtom import_name;+ int req_module_idx; /* in req_module_entries */+} JSImportEntry;++struct JSModuleDef {+ JSRefCountHeader header; /* must come first, 32-bit */+ JSAtom module_name;+ struct list_head link;++ JSReqModuleEntry *req_module_entries;+ int req_module_entries_count;+ int req_module_entries_size;++ JSExportEntry *export_entries;+ int export_entries_count;+ int export_entries_size;++ JSStarExportEntry *star_export_entries;+ int star_export_entries_count;+ int star_export_entries_size;++ JSImportEntry *import_entries;+ int import_entries_count;+ int import_entries_size;++ JSValue module_ns;+ JSValue func_obj; /* only used for JS modules */+ JSModuleInitFunc *init_func; /* only used for C modules */+ BOOL resolved : 8;+ BOOL func_created : 8;+ BOOL instantiated : 8;+ BOOL evaluated : 8;+ BOOL eval_mark : 8; /* temporary use during js_evaluate_module() */+ /* true if evaluation yielded an exception. It is saved in+ eval_exception */+ BOOL eval_has_exception : 8; + JSValue eval_exception;+ JSValue meta_obj; /* for import.meta */+};++typedef struct JSJobEntry {+ struct list_head link;+ JSContext *ctx;+ JSJobFunc *job_func;+ int argc;+ JSValue argv[0];+} JSJobEntry;++typedef struct JSProperty {+ union {+ JSValue value; /* JS_PROP_NORMAL */+ struct { /* JS_PROP_GETSET */+ JSObject *getter; /* NULL if undefined */+ JSObject *setter; /* NULL if undefined */+ } getset;+ JSVarRef *var_ref; /* JS_PROP_VARREF */+ struct { /* JS_PROP_AUTOINIT */+ /* in order to use only 2 pointers, we compress the realm+ and the init function pointer */+ uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x)+ in the 2 low bits */+ void *opaque;+ } init;+ } u;+} JSProperty;++#define JS_PROP_INITIAL_SIZE 2+#define JS_PROP_INITIAL_HASH_SIZE 4 /* must be a power of two */+#define JS_ARRAY_INITIAL_SIZE 2++typedef struct JSShapeProperty {+ uint32_t hash_next : 26; /* 0 if last in list */+ uint32_t flags : 6; /* JS_PROP_XXX */+ JSAtom atom; /* JS_ATOM_NULL = free property entry */+} JSShapeProperty;++struct JSShape {+ /* hash table of size hash_mask + 1 before the start of the+ structure (see prop_hash_end()). */+ JSGCObjectHeader header;+ /* true if the shape is inserted in the shape hash table. If not,+ JSShape.hash is not valid */+ uint8_t is_hashed;+ /* If true, the shape may have small array index properties 'n' with 0+ <= n <= 2^31-1. If false, the shape is guaranteed not to have+ small array index properties */+ uint8_t has_small_array_index;+ uint32_t hash; /* current hash value */+ uint32_t prop_hash_mask;+ int prop_size; /* allocated properties */+ int prop_count; /* include deleted properties */+ int deleted_prop_count;+ JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */+ JSObject *proto;+ JSShapeProperty prop[0]; /* prop_size elements */+};++struct JSObject {+ union {+ JSGCObjectHeader header;+ struct {+ int __gc_ref_count; /* corresponds to header.ref_count */+ uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */+ + uint8_t extensible : 1;+ uint8_t free_mark : 1; /* only used when freeing objects with cycles */+ uint8_t is_exotic : 1; /* TRUE if object has exotic property handlers */+ uint8_t fast_array : 1; /* TRUE if u.array is used for get/put (for JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS and typed arrays) */+ uint8_t is_constructor : 1; /* TRUE if object is a constructor function */+ uint8_t is_uncatchable_error : 1; /* if TRUE, error is not catchable */+ uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */+ uint8_t is_HTMLDDA : 1; /* specific annex B IsHtmlDDA behavior */+ uint16_t class_id; /* see JS_CLASS_x */+ };+ };+ /* byte offsets: 16/24 */+ JSShape *shape; /* prototype and property names + flag */+ JSProperty *prop; /* array of properties */+ /* byte offsets: 24/40 */+ struct JSMapRecord *first_weak_ref; /* XXX: use a bit and an external hash table? */+ /* byte offsets: 28/48 */+ union {+ void *opaque;+ struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */+ struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */+ struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */+ struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */+ struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */+#ifdef CONFIG_BIGNUM+ struct JSFloatEnv *float_env; /* JS_CLASS_FLOAT_ENV */+ struct JSOperatorSetData *operator_set; /* JS_CLASS_OPERATOR_SET */+#endif+ struct JSMapState *map_state; /* JS_CLASS_MAP..JS_CLASS_WEAKSET */+ struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */+ struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */+ struct JSRegExpStringIteratorData *regexp_string_iterator_data; /* JS_CLASS_REGEXP_STRING_ITERATOR */+ struct JSGeneratorData *generator_data; /* JS_CLASS_GENERATOR */+ struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */+ struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */+ struct JSPromiseFunctionData *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, JS_CLASS_PROMISE_REJECT_FUNCTION */+ struct JSAsyncFunctionData *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */+ struct JSAsyncFromSyncIteratorData *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */+ struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */+ struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */+ /* also used by JS_CLASS_GENERATOR_FUNCTION, JS_CLASS_ASYNC_FUNCTION and JS_CLASS_ASYNC_GENERATOR_FUNCTION */+ struct JSFunctionBytecode *function_bytecode;+ JSVarRef **var_refs;+ JSObject *home_object; /* for 'super' access */+ } func;+ struct { /* JS_CLASS_C_FUNCTION: 12/20 bytes */+ JSContext *realm;+ JSCFunctionType c_function;+ uint8_t length;+ uint8_t cproto;+ int16_t magic;+ } cfunc;+ /* array part for fast arrays and typed arrays */+ struct { /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */+ union {+ uint32_t size; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */+ struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */+ } u1;+ union {+ JSValue *values; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ + void *ptr; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */+ int8_t *int8_ptr; /* JS_CLASS_INT8_ARRAY */+ uint8_t *uint8_ptr; /* JS_CLASS_UINT8_ARRAY, JS_CLASS_UINT8C_ARRAY */+ int16_t *int16_ptr; /* JS_CLASS_INT16_ARRAY */+ uint16_t *uint16_ptr; /* JS_CLASS_UINT16_ARRAY */+ int32_t *int32_ptr; /* JS_CLASS_INT32_ARRAY */+ uint32_t *uint32_ptr; /* JS_CLASS_UINT32_ARRAY */+ int64_t *int64_ptr; /* JS_CLASS_INT64_ARRAY */+ uint64_t *uint64_ptr; /* JS_CLASS_UINT64_ARRAY */+ float *float_ptr; /* JS_CLASS_FLOAT32_ARRAY */+ double *double_ptr; /* JS_CLASS_FLOAT64_ARRAY */+ } u;+ uint32_t count; /* <= 2^31-1. 0 for a detached typed array */+ } array; /* 12/20 bytes */+ JSRegExp regexp; /* JS_CLASS_REGEXP: 8/16 bytes */+ JSValue object_data; /* for JS_SetObjectData(): 8/16/16 bytes */+ } u;+ /* byte sizes: 40/48/72 */+};+enum {+ __JS_ATOM_NULL = JS_ATOM_NULL,+#define DEF(name, str) JS_ATOM_ ## name,+#include "quickjs-atom.h"+#undef DEF+ JS_ATOM_END,+};+#define JS_ATOM_LAST_KEYWORD JS_ATOM_super+#define JS_ATOM_LAST_STRICT_KEYWORD JS_ATOM_yield++static const char js_atom_init[] =+#define DEF(name, str) str "\0"+#include "quickjs-atom.h"+#undef DEF+;++typedef enum OPCodeFormat {+#define FMT(f) OP_FMT_ ## f,+#define DEF(id, size, n_pop, n_push, f)+#include "quickjs-opcode.h"+#undef DEF+#undef FMT+} OPCodeFormat;++enum OPCodeEnum {+#define FMT(f)+#define DEF(id, size, n_pop, n_push, f) OP_ ## id,+#define def(id, size, n_pop, n_push, f)+#include "quickjs-opcode.h"+#undef def+#undef DEF+#undef FMT+ OP_COUNT, /* excluding temporary opcodes */+ /* temporary opcodes : overlap with the short opcodes */+ OP_TEMP_START = OP_nop + 1,+ OP___dummy = OP_TEMP_START - 1,+#define FMT(f)+#define DEF(id, size, n_pop, n_push, f)+#define def(id, size, n_pop, n_push, f) OP_ ## id,+#include "quickjs-opcode.h"+#undef def+#undef DEF+#undef FMT+ OP_TEMP_END,+};++static int JS_InitAtoms(JSRuntime *rt);+static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len,+ int atom_type);+static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p);+static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b);+static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,+ JSValueConst this_obj,+ int argc, JSValueConst *argv, int flags);+static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj,+ JSValueConst this_obj,+ int argc, JSValueConst *argv, int flags);+static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj,+ JSValueConst this_obj, JSValueConst new_target,+ int argc, JSValue *argv, int flags);+static JSValue JS_CallConstructorInternal(JSContext *ctx,+ JSValueConst func_obj,+ JSValueConst new_target,+ int argc, JSValue *argv, int flags);+static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj,+ int argc, JSValueConst *argv);+static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,+ int argc, JSValueConst *argv);+static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,+ JSValue val, BOOL is_array_ctor);+static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,+ JSValueConst val, int flags, int scope_idx);+JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...);+static __maybe_unused void JS_DumpAtoms(JSRuntime *rt);+static __maybe_unused void JS_DumpString(JSRuntime *rt,+ const JSString *p);+static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt);+static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p);+static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p);+static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,+ JSValueConst val);+static __maybe_unused void JS_DumpValue(JSContext *ctx, JSValueConst val);+static __maybe_unused void JS_PrintValue(JSContext *ctx,+ const char *str,+ JSValueConst val);+static __maybe_unused void JS_DumpShapes(JSRuntime *rt);+static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic);+static void js_array_finalizer(JSRuntime *rt, JSValue val);+static void js_array_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func);+static void js_object_data_finalizer(JSRuntime *rt, JSValue val);+static void js_object_data_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func);+static void js_c_function_finalizer(JSRuntime *rt, JSValue val);+static void js_c_function_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func);+static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val);+static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func);+static void js_bound_function_finalizer(JSRuntime *rt, JSValue val);+static void js_bound_function_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func);+static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val);+static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func);+static void js_regexp_finalizer(JSRuntime *rt, JSValue val);+static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val);+static void js_typed_array_finalizer(JSRuntime *rt, JSValue val);+static void js_typed_array_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func);+static void js_proxy_finalizer(JSRuntime *rt, JSValue val);+static void js_proxy_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func);+static void js_map_finalizer(JSRuntime *rt, JSValue val);+static void js_map_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func);+static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val);+static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func);+static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val);+static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func);+static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val);+static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func);+static void js_generator_finalizer(JSRuntime *rt, JSValue obj);+static void js_generator_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func);+static void js_promise_finalizer(JSRuntime *rt, JSValue val);+static void js_promise_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func);+static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val);+static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func);+#ifdef CONFIG_BIGNUM+static void js_operator_set_finalizer(JSRuntime *rt, JSValue val);+static void js_operator_set_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func);+#endif+static JSValue JS_ToStringFree(JSContext *ctx, JSValue val);+static int JS_ToBoolFree(JSContext *ctx, JSValue val);+static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val);+static int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val);+static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val);+static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern,+ JSValueConst flags);+static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor,+ JSValue pattern, JSValue bc);+static void gc_decref(JSRuntime *rt);+static int JS_NewClass1(JSRuntime *rt, JSClassID class_id,+ const JSClassDef *class_def, JSAtom name);++typedef enum JSStrictEqModeEnum {+ JS_EQ_STRICT,+ JS_EQ_SAME_VALUE,+ JS_EQ_SAME_VALUE_ZERO,+} JSStrictEqModeEnum;++static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,+ JSStrictEqModeEnum eq_mode);+static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2);+static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2);+static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2);+static JSValue JS_ToObject(JSContext *ctx, JSValueConst val);+static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val);+static JSProperty *add_property(JSContext *ctx,+ JSObject *p, JSAtom prop, int prop_flags);+#ifdef CONFIG_BIGNUM+static void js_float_env_finalizer(JSRuntime *rt, JSValue val);+static JSValue JS_NewBigFloat(JSContext *ctx);+static inline bf_t *JS_GetBigFloat(JSValueConst val)+{+ JSBigFloat *p = JS_VALUE_GET_PTR(val);+ return &p->num;+}+static JSValue JS_NewBigDecimal(JSContext *ctx);+static inline bfdec_t *JS_GetBigDecimal(JSValueConst val)+{+ JSBigDecimal *p = JS_VALUE_GET_PTR(val);+ return &p->num;+}+static JSValue JS_NewBigInt(JSContext *ctx);+static inline bf_t *JS_GetBigInt(JSValueConst val)+{+ JSBigFloat *p = JS_VALUE_GET_PTR(val);+ return &p->num;+}+static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val,+ BOOL convert_to_safe_integer);+static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val);+static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val);+static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val);+static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf);+static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val);+static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,+ BOOL allow_null_or_undefined);+static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val);+#endif+JSValue JS_ThrowOutOfMemory(JSContext *ctx);+static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx);+static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj);+static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,+ JSValueConst proto_val, BOOL throw_flag);+static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj);+static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj);+static int js_proxy_isArray(JSContext *ctx, JSValueConst obj);+static int JS_CreateProperty(JSContext *ctx, JSObject *p,+ JSAtom prop, JSValueConst val,+ JSValueConst getter, JSValueConst setter,+ int flags);+static int js_string_memcmp(const JSString *p1, const JSString *p2, int len);+static void reset_weak_ref(JSRuntime *rt, JSObject *p);+static JSValue js_array_buffer_constructor3(JSContext *ctx,+ JSValueConst new_target,+ uint64_t len, JSClassID class_id,+ uint8_t *buf,+ JSFreeArrayBufferDataFunc *free_func,+ void *opaque, BOOL alloc_flag);+static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj);+static JSValue js_typed_array_constructor(JSContext *ctx,+ JSValueConst this_val,+ int argc, JSValueConst *argv,+ int classid);+static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p);+static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p);+static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx);+static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx,+ BOOL is_arg);+static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,+ JSValueConst this_obj,+ int argc, JSValueConst *argv,+ int flags);+static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val);+static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func);+static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,+ const char *input, size_t input_len,+ const char *filename, int flags, int scope_idx);+static void js_free_module_def(JSContext *ctx, JSModuleDef *m);+static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,+ JS_MarkFunc *mark_func);+static JSValue js_import_meta(JSContext *ctx);+static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier);+static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref);+static JSValue js_new_promise_capability(JSContext *ctx,+ JSValue *resolving_funcs,+ JSValueConst ctor);+static __exception int perform_promise_then(JSContext *ctx,+ JSValueConst promise,+ JSValueConst *resolve_reject,+ JSValueConst *cap_resolving_funcs);+static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic);+static int js_string_compare(JSContext *ctx,+ const JSString *p1, const JSString *p2);+static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val);+static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,+ JSValue prop, JSValue val, int flags);+static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val);+static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val);+static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val);+static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,+ JSObject *p, JSAtom prop);+static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc);+static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s,+ JS_MarkFunc *mark_func);+static void JS_AddIntrinsicBasicObjects(JSContext *ctx);+static void js_free_shape(JSRuntime *rt, JSShape *sh);+static void js_free_shape_null(JSRuntime *rt, JSShape *sh);+static int js_shape_prepare_update(JSContext *ctx, JSObject *p,+ JSShapeProperty **pprs);+static int init_shape_hash(JSRuntime *rt);+static __exception int js_get_length32(JSContext *ctx, uint32_t *pres,+ JSValueConst obj);+static __exception int js_get_length64(JSContext *ctx, int64_t *pres,+ JSValueConst obj);+static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len);+static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen,+ JSValueConst array_arg);+static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj,+ JSValue **arrpp, uint32_t *countp);+static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx,+ JSValueConst sync_iter);+static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val);+static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func);+static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj,+ JSValueConst this_val,+ int argc, JSValueConst *argv, int flags);+static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val);+static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,+ JSGCObjectTypeEnum type);+static void remove_gc_object(JSGCObjectHeader *h);+static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s);+static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque);+static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,+ void *opaque);+static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p,+ JSAtom atom, void *opaque);+void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag);++static const JSClassExoticMethods js_arguments_exotic_methods;+static const JSClassExoticMethods js_string_exotic_methods;+static const JSClassExoticMethods js_proxy_exotic_methods;+static const JSClassExoticMethods js_module_ns_exotic_methods;+static JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT;++static void js_trigger_gc(JSRuntime *rt, size_t size)+{+ BOOL force_gc;+#ifdef FORCE_GC_AT_MALLOC+ force_gc = TRUE;+#else+ force_gc = ((rt->malloc_state.malloc_size + size) >+ rt->malloc_gc_threshold);+#endif+ if (force_gc) {+#ifdef DUMP_GC+ printf("GC: size=%" PRIu64 "\n",+ (uint64_t)rt->malloc_state.malloc_size);+#endif+ JS_RunGC(rt);+ rt->malloc_gc_threshold = rt->malloc_state.malloc_size ++ (rt->malloc_state.malloc_size >> 1);+ }+}++static size_t js_malloc_usable_size_unknown(const void *ptr)+{+ return 0;+}++void *js_malloc_rt(JSRuntime *rt, size_t size)+{+ return rt->mf.js_malloc(&rt->malloc_state, size);+}++void js_free_rt(JSRuntime *rt, void *ptr)+{+ rt->mf.js_free(&rt->malloc_state, ptr);+}++void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size)+{+ return rt->mf.js_realloc(&rt->malloc_state, ptr, size);+}++size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr)+{+ return rt->mf.js_malloc_usable_size(ptr);+}++void *js_mallocz_rt(JSRuntime *rt, size_t size)+{+ void *ptr;+ ptr = js_malloc_rt(rt, size);+ if (!ptr)+ return NULL;+ return memset(ptr, 0, size);+}++#ifdef CONFIG_BIGNUM+/* called by libbf */+static void *js_bf_realloc(void *opaque, void *ptr, size_t size)+{+ JSRuntime *rt = opaque;+ return js_realloc_rt(rt, ptr, size);+}+#endif /* CONFIG_BIGNUM */++/* Throw out of memory in case of error */+void *js_malloc(JSContext *ctx, size_t size)+{+ void *ptr;+ ptr = js_malloc_rt(ctx->rt, size);+ if (unlikely(!ptr)) {+ JS_ThrowOutOfMemory(ctx);+ return NULL;+ }+ return ptr;+}++/* Throw out of memory in case of error */+void *js_mallocz(JSContext *ctx, size_t size)+{+ void *ptr;+ ptr = js_mallocz_rt(ctx->rt, size);+ if (unlikely(!ptr)) {+ JS_ThrowOutOfMemory(ctx);+ return NULL;+ }+ return ptr;+}++void js_free(JSContext *ctx, void *ptr)+{+ js_free_rt(ctx->rt, ptr);+}++/* Throw out of memory in case of error */+void *js_realloc(JSContext *ctx, void *ptr, size_t size)+{+ void *ret;+ ret = js_realloc_rt(ctx->rt, ptr, size);+ if (unlikely(!ret && size != 0)) {+ JS_ThrowOutOfMemory(ctx);+ return NULL;+ }+ return ret;+}++/* store extra allocated size in *pslack if successful */+void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack)+{+ void *ret;+ ret = js_realloc_rt(ctx->rt, ptr, size);+ if (unlikely(!ret && size != 0)) {+ JS_ThrowOutOfMemory(ctx);+ return NULL;+ }+ if (pslack) {+ size_t new_size = js_malloc_usable_size_rt(ctx->rt, ret);+ *pslack = (new_size > size) ? new_size - size : 0;+ }+ return ret;+}++size_t js_malloc_usable_size(JSContext *ctx, const void *ptr)+{+ return js_malloc_usable_size_rt(ctx->rt, ptr);+}++/* Throw out of memory exception in case of error */+char *js_strndup(JSContext *ctx, const char *s, size_t n)+{+ char *ptr;+ ptr = js_malloc(ctx, n + 1);+ if (ptr) {+ memcpy(ptr, s, n);+ ptr[n] = '\0';+ }+ return ptr;+}++char *js_strdup(JSContext *ctx, const char *str)+{+ return js_strndup(ctx, str, strlen(str));+}++static no_inline int js_realloc_array(JSContext *ctx, void **parray,+ int elem_size, int *psize, int req_size)+{+ int new_size;+ size_t slack;+ void *new_array;+ /* XXX: potential arithmetic overflow */+ new_size = max_int(req_size, *psize * 3 / 2);+ new_array = js_realloc2(ctx, *parray, new_size * elem_size, &slack);+ if (!new_array)+ return -1;+ new_size += slack / elem_size;+ *psize = new_size;+ *parray = new_array;+ return 0;+}++/* resize the array and update its size if req_size > *psize */+static inline int js_resize_array(JSContext *ctx, void **parray, int elem_size,+ int *psize, int req_size)+{+ if (unlikely(req_size > *psize))+ return js_realloc_array(ctx, parray, elem_size, psize, req_size);+ else+ return 0;+}++static inline void js_dbuf_init(JSContext *ctx, DynBuf *s)+{+ dbuf_init2(s, ctx->rt, (DynBufReallocFunc *)js_realloc_rt);+}++static inline int is_digit(int c) {+ return c >= '0' && c <= '9';+}++typedef struct JSClassShortDef {+ JSAtom class_name;+ JSClassFinalizer *finalizer;+ JSClassGCMark *gc_mark;+} JSClassShortDef;++static JSClassShortDef const js_std_class_def[] = {+ { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_OBJECT */+ { JS_ATOM_Array, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARRAY */+ { JS_ATOM_Error, NULL, NULL }, /* JS_CLASS_ERROR */+ { JS_ATOM_Number, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_NUMBER */+ { JS_ATOM_String, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_STRING */+ { JS_ATOM_Boolean, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BOOLEAN */+ { JS_ATOM_Symbol, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_SYMBOL */+ { JS_ATOM_Arguments, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARGUMENTS */+ { JS_ATOM_Arguments, NULL, NULL }, /* JS_CLASS_MAPPED_ARGUMENTS */+ { JS_ATOM_Date, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_DATE */+ { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_MODULE_NS */+ { JS_ATOM_Function, js_c_function_finalizer, js_c_function_mark }, /* JS_CLASS_C_FUNCTION */+ { JS_ATOM_Function, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_BYTECODE_FUNCTION */+ { JS_ATOM_Function, js_bound_function_finalizer, js_bound_function_mark }, /* JS_CLASS_BOUND_FUNCTION */+ { JS_ATOM_Function, js_c_function_data_finalizer, js_c_function_data_mark }, /* JS_CLASS_C_FUNCTION_DATA */+ { JS_ATOM_GeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_GENERATOR_FUNCTION */+ { JS_ATOM_ForInIterator, js_for_in_iterator_finalizer, js_for_in_iterator_mark }, /* JS_CLASS_FOR_IN_ITERATOR */+ { JS_ATOM_RegExp, js_regexp_finalizer, NULL }, /* JS_CLASS_REGEXP */+ { JS_ATOM_ArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_ARRAY_BUFFER */+ { JS_ATOM_SharedArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_SHARED_ARRAY_BUFFER */+ { JS_ATOM_Uint8ClampedArray, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8C_ARRAY */+ { JS_ATOM_Int8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT8_ARRAY */+ { JS_ATOM_Uint8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8_ARRAY */+ { JS_ATOM_Int16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT16_ARRAY */+ { JS_ATOM_Uint16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT16_ARRAY */+ { JS_ATOM_Int32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT32_ARRAY */+ { JS_ATOM_Uint32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT32_ARRAY */+#ifdef CONFIG_BIGNUM+ { JS_ATOM_BigInt64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_INT64_ARRAY */+ { JS_ATOM_BigUint64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_UINT64_ARRAY */+#endif+ { JS_ATOM_Float32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT32_ARRAY */+ { JS_ATOM_Float64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT64_ARRAY */+ { JS_ATOM_DataView, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_DATAVIEW */+#ifdef CONFIG_BIGNUM+ { JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_INT */+ { JS_ATOM_BigFloat, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_FLOAT */+ { JS_ATOM_BigFloatEnv, js_float_env_finalizer, NULL }, /* JS_CLASS_FLOAT_ENV */+ { JS_ATOM_BigDecimal, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_DECIMAL */+ { JS_ATOM_OperatorSet, js_operator_set_finalizer, js_operator_set_mark }, /* JS_CLASS_OPERATOR_SET */+#endif+ { JS_ATOM_Map, js_map_finalizer, js_map_mark }, /* JS_CLASS_MAP */+ { JS_ATOM_Set, js_map_finalizer, js_map_mark }, /* JS_CLASS_SET */+ { JS_ATOM_WeakMap, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKMAP */+ { JS_ATOM_WeakSet, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKSET */+ { JS_ATOM_Map_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_MAP_ITERATOR */+ { JS_ATOM_Set_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_SET_ITERATOR */+ { JS_ATOM_Array_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_ARRAY_ITERATOR */+ { JS_ATOM_String_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_STRING_ITERATOR */+ { JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */+ { JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /* JS_CLASS_GENERATOR */+};++static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab,+ int start, int count)+{+ JSClassDef cm_s, *cm = &cm_s;+ int i, class_id;++ for(i = 0; i < count; i++) {+ class_id = i + start;+ memset(cm, 0, sizeof(*cm));+ cm->finalizer = tab[i].finalizer;+ cm->gc_mark = tab[i].gc_mark;+ if (JS_NewClass1(rt, class_id, cm, tab[i].class_name) < 0)+ return -1;+ }+ return 0;+}++#ifdef CONFIG_BIGNUM+static JSValue JS_ThrowUnsupportedOperation(JSContext *ctx)+{+ return JS_ThrowTypeError(ctx, "unsupported operation");+}++static JSValue invalid_to_string(JSContext *ctx, JSValueConst val)+{+ return JS_ThrowUnsupportedOperation(ctx);+}++static JSValue invalid_from_string(JSContext *ctx, const char *buf,+ int radix, int flags, slimb_t *pexponent)+{+ return JS_NAN;+}++static int invalid_unary_arith(JSContext *ctx,+ JSValue *pres, OPCodeEnum op, JSValue op1)+{+ JS_FreeValue(ctx, op1);+ JS_ThrowUnsupportedOperation(ctx);+ return -1;+}++static int invalid_binary_arith(JSContext *ctx, OPCodeEnum op,+ JSValue *pres, JSValue op1, JSValue op2)+{+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ JS_ThrowUnsupportedOperation(ctx);+ return -1;+}++static JSValue invalid_mul_pow10_to_float64(JSContext *ctx, const bf_t *a,+ int64_t exponent)+{+ return JS_ThrowUnsupportedOperation(ctx);+}++static int invalid_mul_pow10(JSContext *ctx, JSValue *sp)+{+ JS_ThrowUnsupportedOperation(ctx);+ return -1;+}++static void set_dummy_numeric_ops(JSNumericOperations *ops)+{+ ops->to_string = invalid_to_string;+ ops->from_string = invalid_from_string;+ ops->unary_arith = invalid_unary_arith;+ ops->binary_arith = invalid_binary_arith;+ ops->mul_pow10_to_float64 = invalid_mul_pow10_to_float64;+ ops->mul_pow10 = invalid_mul_pow10;+}++#endif /* CONFIG_BIGNUM */++#if !defined(CONFIG_STACK_CHECK)+/* no stack limitation */+static inline uint8_t *js_get_stack_pointer(void)+{+ return NULL;+}++static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)+{+ return FALSE;+}+#else+/* Note: OS and CPU dependent */+static inline uint8_t *js_get_stack_pointer(void)+{+ return __builtin_frame_address(0);+}++static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)+{+ size_t size;+ size = rt->stack_top - js_get_stack_pointer();+ return unlikely((size + alloca_size) > rt->stack_size);+}+#endif++JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque)+{+ JSRuntime *rt;+ JSMallocState ms;++ memset(&ms, 0, sizeof(ms));+ ms.opaque = opaque;+ ms.malloc_limit = -1;++ rt = mf->js_malloc(&ms, sizeof(JSRuntime));+ if (!rt)+ return NULL;+ memset(rt, 0, sizeof(*rt));+ rt->mf = *mf;+ if (!rt->mf.js_malloc_usable_size) {+ /* use dummy function if none provided */+ rt->mf.js_malloc_usable_size = js_malloc_usable_size_unknown;+ }+ rt->malloc_state = ms;+ rt->malloc_gc_threshold = 256 * 1024;++#ifdef CONFIG_BIGNUM+ bf_context_init(&rt->bf_ctx, js_bf_realloc, rt);+ set_dummy_numeric_ops(&rt->bigint_ops);+ set_dummy_numeric_ops(&rt->bigfloat_ops);+ set_dummy_numeric_ops(&rt->bigdecimal_ops);+#endif++ init_list_head(&rt->context_list);+ init_list_head(&rt->gc_obj_list);+ init_list_head(&rt->gc_zero_ref_count_list);+ rt->gc_phase = JS_GC_PHASE_NONE;+ +#ifdef DUMP_LEAKS+ init_list_head(&rt->string_list);+#endif+ init_list_head(&rt->job_list);++ if (JS_InitAtoms(rt))+ goto fail;++ /* create the object, array and function classes */+ if (init_class_range(rt, js_std_class_def, JS_CLASS_OBJECT,+ countof(js_std_class_def)) < 0)+ goto fail;+ rt->class_array[JS_CLASS_ARGUMENTS].exotic = &js_arguments_exotic_methods;+ rt->class_array[JS_CLASS_STRING].exotic = &js_string_exotic_methods;+ rt->class_array[JS_CLASS_MODULE_NS].exotic = &js_module_ns_exotic_methods;++ rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function;+ rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_c_function_data_call;+ rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function;+ rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_generator_function_call;+ if (init_shape_hash(rt))+ goto fail;++ rt->stack_top = js_get_stack_pointer();+ rt->stack_size = JS_DEFAULT_STACK_SIZE;+ rt->current_exception = JS_NULL;++ return rt;+ fail:+ JS_FreeRuntime(rt);+ return NULL;+}++void *JS_GetRuntimeOpaque(JSRuntime *rt)+{+ return rt->user_opaque;+}++void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque)+{+ rt->user_opaque = opaque;+}++/* default memory allocation functions with memory limitation */+static inline size_t js_def_malloc_usable_size(void *ptr)+{+#if defined(__APPLE__)+ return malloc_size(ptr);+#elif defined(_WIN32)+ return _msize(ptr);+#elif defined(EMSCRIPTEN)+ return 0;+#elif defined(__linux__)+ return malloc_usable_size(ptr);+#else+ /* change this to `return 0;` if compilation fails */+ return malloc_usable_size(ptr);+#endif+}++static void *js_def_malloc(JSMallocState *s, size_t size)+{+ void *ptr;++ /* Do not allocate zero bytes: behavior is platform dependent */+ assert(size != 0);++ if (unlikely(s->malloc_size + size > s->malloc_limit))+ return NULL;++ ptr = malloc(size);+ if (!ptr)+ return NULL;++ s->malloc_count++;+ s->malloc_size += js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD;+ return ptr;+}++static void js_def_free(JSMallocState *s, void *ptr)+{+ if (!ptr)+ return;++ s->malloc_count--;+ s->malloc_size -= js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD;+ free(ptr);+}++static void *js_def_realloc(JSMallocState *s, void *ptr, size_t size)+{+ size_t old_size;++ if (!ptr) {+ if (size == 0)+ return NULL;+ return js_def_malloc(s, size);+ }+ old_size = js_def_malloc_usable_size(ptr);+ if (size == 0) {+ s->malloc_count--;+ s->malloc_size -= old_size + MALLOC_OVERHEAD;+ free(ptr);+ return NULL;+ }+ if (s->malloc_size + size - old_size > s->malloc_limit)+ return NULL;++ ptr = realloc(ptr, size);+ if (!ptr)+ return NULL;++ s->malloc_size += js_def_malloc_usable_size(ptr) - old_size;+ return ptr;+}++static const JSMallocFunctions def_malloc_funcs = {+ js_def_malloc,+ js_def_free,+ js_def_realloc,+#if defined(__APPLE__)+ malloc_size,+#elif defined(_WIN32)+ (size_t (*)(const void *))_msize,+#elif defined(EMSCRIPTEN)+ NULL,+#elif defined(__linux__)+ (size_t (*)(const void *))malloc_usable_size,+#else+ /* change this to `NULL,` if compilation fails */+ malloc_usable_size,+#endif+};++JSRuntime *JS_NewRuntime(void)+{+ return JS_NewRuntime2(&def_malloc_funcs, NULL);+}++void JS_SetMemoryLimit(JSRuntime *rt, size_t limit)+{+ rt->malloc_state.malloc_limit = limit;+}++/* use -1 to disable automatic GC */+void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold)+{+ rt->malloc_gc_threshold = gc_threshold;+}++#define malloc(s) malloc_is_forbidden(s)+#define free(p) free_is_forbidden(p)+#define realloc(p,s) realloc_is_forbidden(p,s)++void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque)+{+ rt->interrupt_handler = cb;+ rt->interrupt_opaque = opaque;+}++void JS_SetCanBlock(JSRuntime *rt, BOOL can_block)+{+ rt->can_block = can_block;+}++void JS_SetSharedArrayBufferFunctions(JSRuntime *rt,+ const JSSharedArrayBufferFunctions *sf)+{+ rt->sab_funcs = *sf;+}++/* return 0 if OK, < 0 if exception */+int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func,+ int argc, JSValueConst *argv)+{+ JSRuntime *rt = ctx->rt;+ JSJobEntry *e;+ int i;++ e = js_malloc(ctx, sizeof(*e) + argc * sizeof(JSValue));+ if (!e)+ return -1;+ e->ctx = ctx;+ e->job_func = job_func;+ e->argc = argc;+ for(i = 0; i < argc; i++) {+ e->argv[i] = JS_DupValue(ctx, argv[i]);+ }+ list_add_tail(&e->link, &rt->job_list);+ return 0;+}++BOOL JS_IsJobPending(JSRuntime *rt)+{+ return !list_empty(&rt->job_list);+}++/* return < 0 if exception, 0 if no job pending, 1 if a job was+ executed successfully. the context of the job is stored in '*pctx' */+int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx)+{+ JSContext *ctx;+ JSJobEntry *e;+ JSValue res;+ int i, ret;++ if (list_empty(&rt->job_list)) {+ *pctx = NULL;+ return 0;+ }++ /* get the first pending job and execute it */+ e = list_entry(rt->job_list.next, JSJobEntry, link);+ list_del(&e->link);+ ctx = e->ctx;+ res = e->job_func(e->ctx, e->argc, (JSValueConst *)e->argv);+ for(i = 0; i < e->argc; i++)+ JS_FreeValue(ctx, e->argv[i]);+ if (JS_IsException(res))+ ret = -1;+ else+ ret = 1;+ JS_FreeValue(ctx, res);+ js_free(ctx, e);+ *pctx = ctx;+ return ret;+}++static inline uint32_t atom_get_free(const JSAtomStruct *p)+{+ return (uintptr_t)p >> 1;+}++static inline BOOL atom_is_free(const JSAtomStruct *p)+{+ return (uintptr_t)p & 1;+}++static inline JSAtomStruct *atom_set_free(uint32_t v)+{+ return (JSAtomStruct *)(((uintptr_t)v << 1) | 1);+}++/* Note: the string contents are uninitialized */+static JSString *js_alloc_string_rt(JSRuntime *rt, int max_len, int is_wide_char)+{+ JSString *str;+ str = js_malloc_rt(rt, sizeof(JSString) + (max_len << is_wide_char) + 1 - is_wide_char);+ if (unlikely(!str))+ return NULL;+ str->header.ref_count = 1;+ str->is_wide_char = is_wide_char;+ str->len = max_len;+ str->atom_type = 0;+ str->hash = 0; /* optional but costless */+ str->hash_next = 0; /* optional */+#ifdef DUMP_LEAKS+ list_add_tail(&str->link, &rt->string_list);+#endif+ return str;+}++static JSString *js_alloc_string(JSContext *ctx, int max_len, int is_wide_char)+{+ JSString *p;+ p = js_alloc_string_rt(ctx->rt, max_len, is_wide_char);+ if (unlikely(!p)) {+ JS_ThrowOutOfMemory(ctx);+ return NULL;+ }+ return p;+}++/* same as JS_FreeValueRT() but faster */+static inline void js_free_string(JSRuntime *rt, JSString *str)+{+ if (--str->header.ref_count <= 0) {+ if (str->atom_type) {+ JS_FreeAtomStruct(rt, str);+ } else {+#ifdef DUMP_LEAKS+ list_del(&str->link);+#endif+ js_free_rt(rt, str);+ }+ }+}++void JS_SetRuntimeInfo(JSRuntime *rt, const char *s)+{+ if (rt)+ rt->rt_info = s;+}++void JS_FreeRuntime(JSRuntime *rt)+{+ struct list_head *el, *el1;+ int i;++ JS_FreeValueRT(rt, rt->current_exception);++ list_for_each_safe(el, el1, &rt->job_list) {+ JSJobEntry *e = list_entry(el, JSJobEntry, link);+ for(i = 0; i < e->argc; i++)+ JS_FreeValueRT(rt, e->argv[i]);+ js_free_rt(rt, e);+ }+ init_list_head(&rt->job_list);++ JS_RunGC(rt);++#ifdef DUMP_LEAKS+ /* leaking objects */+ {+ BOOL header_done;+ JSGCObjectHeader *p;+ int count;++ /* remove the internal refcounts to display only the object+ referenced externally */+ list_for_each(el, &rt->gc_obj_list) {+ p = list_entry(el, JSGCObjectHeader, link);+ p->mark = 0;+ }+ gc_decref(rt);++ header_done = FALSE;+ list_for_each(el, &rt->gc_obj_list) {+ p = list_entry(el, JSGCObjectHeader, link);+ if (p->ref_count != 0) {+ if (!header_done) {+ printf("Object leaks:\n");+ JS_DumpObjectHeader(rt);+ header_done = TRUE;+ }+ JS_DumpGCObject(rt, p);+ }+ }++ count = 0;+ list_for_each(el, &rt->gc_obj_list) {+ p = list_entry(el, JSGCObjectHeader, link);+ if (p->ref_count == 0) {+ count++;+ }+ }+ if (count != 0)+ printf("Secondary object leaks: %d\n", count);+ }+#endif+ assert(list_empty(&rt->gc_obj_list));++ /* free the classes */+ for(i = 0; i < rt->class_count; i++) {+ JSClass *cl = &rt->class_array[i];+ if (cl->class_id != 0) {+ JS_FreeAtomRT(rt, cl->class_name);+ }+ }+ js_free_rt(rt, rt->class_array);++#ifdef CONFIG_BIGNUM+ bf_context_end(&rt->bf_ctx);+#endif++#ifdef DUMP_LEAKS+ /* only the atoms defined in JS_InitAtoms() should be left */+ {+ BOOL header_done = FALSE;++ for(i = 0; i < rt->atom_size; i++) {+ JSAtomStruct *p = rt->atom_array[i];+ if (!atom_is_free(p) /* && p->str*/) {+ if (i >= JS_ATOM_END || p->header.ref_count != 1) {+ if (!header_done) {+ header_done = TRUE;+ if (rt->rt_info) {+ printf("%s:1: atom leakage:", rt->rt_info);+ } else {+ printf("Atom leaks:\n"+ " %6s %6s %s\n",+ "ID", "REFCNT", "NAME");+ }+ }+ if (rt->rt_info) {+ printf(" ");+ } else {+ printf(" %6u %6u ", i, p->header.ref_count);+ }+ switch (p->atom_type) {+ case JS_ATOM_TYPE_STRING:+ JS_DumpString(rt, p);+ break;+ case JS_ATOM_TYPE_GLOBAL_SYMBOL:+ printf("Symbol.for(");+ JS_DumpString(rt, p);+ printf(")");+ break;+ case JS_ATOM_TYPE_SYMBOL:+ if (p->hash == JS_ATOM_HASH_SYMBOL) {+ printf("Symbol(");+ JS_DumpString(rt, p);+ printf(")");+ } else {+ printf("Private(");+ JS_DumpString(rt, p);+ printf(")");+ }+ break;+ }+ if (rt->rt_info) {+ printf(":%u", p->header.ref_count);+ } else {+ printf("\n");+ }+ }+ }+ }+ if (rt->rt_info && header_done)+ printf("\n");+ }+#endif++ /* free the atoms */+ for(i = 0; i < rt->atom_size; i++) {+ JSAtomStruct *p = rt->atom_array[i];+ if (!atom_is_free(p)) {+#ifdef DUMP_LEAKS+ list_del(&p->link);+#endif+ js_free_rt(rt, p);+ }+ }+ js_free_rt(rt, rt->atom_array);+ js_free_rt(rt, rt->atom_hash);+ js_free_rt(rt, rt->shape_hash);+#ifdef DUMP_LEAKS+ if (!list_empty(&rt->string_list)) {+ if (rt->rt_info) {+ printf("%s:1: string leakage:", rt->rt_info);+ } else {+ printf("String leaks:\n"+ " %6s %s\n",+ "REFCNT", "VALUE");+ }+ list_for_each_safe(el, el1, &rt->string_list) {+ JSString *str = list_entry(el, JSString, link);+ if (rt->rt_info) {+ printf(" ");+ } else {+ printf(" %6u ", str->header.ref_count);+ }+ JS_DumpString(rt, str);+ if (rt->rt_info) {+ printf(":%u", str->header.ref_count);+ } else {+ printf("\n");+ }+ list_del(&str->link);+ js_free_rt(rt, str);+ }+ if (rt->rt_info)+ printf("\n");+ }+ {+ JSMallocState *s = &rt->malloc_state;+ if (s->malloc_count > 1) {+ if (rt->rt_info)+ printf("%s:1: ", rt->rt_info);+ printf("Memory leak: %"PRIu64" bytes lost in %"PRIu64" block%s\n",+ (uint64_t)(s->malloc_size - sizeof(JSRuntime)),+ (uint64_t)(s->malloc_count - 1), &"s"[s->malloc_count == 2]);+ }+ }+#endif++ {+ JSMallocState ms = rt->malloc_state;+ rt->mf.js_free(&ms, rt);+ }+}++JSContext *JS_NewContextRaw(JSRuntime *rt)+{+ JSContext *ctx;+ int i;++ ctx = js_mallocz_rt(rt, sizeof(JSContext));+ if (!ctx)+ return NULL;+ ctx->header.ref_count = 1;+ add_gc_object(rt, &ctx->header, JS_GC_OBJ_TYPE_JS_CONTEXT);++ ctx->class_proto = js_malloc_rt(rt, sizeof(ctx->class_proto[0]) *+ rt->class_count);+ if (!ctx->class_proto) {+ js_free_rt(rt, ctx);+ return NULL;+ }+ ctx->rt = rt;+ list_add_tail(&ctx->link, &rt->context_list);+#ifdef CONFIG_BIGNUM+ ctx->bf_ctx = &rt->bf_ctx;+ ctx->fp_env.prec = 113;+ ctx->fp_env.flags = bf_set_exp_bits(15) | BF_RNDN | BF_FLAG_SUBNORMAL;+#endif+ for(i = 0; i < rt->class_count; i++)+ ctx->class_proto[i] = JS_NULL;+ ctx->array_ctor = JS_NULL;+ ctx->regexp_ctor = JS_NULL;+ ctx->promise_ctor = JS_NULL;+ init_list_head(&ctx->loaded_modules);++ JS_AddIntrinsicBasicObjects(ctx);+ return ctx;+}++JSContext *JS_NewContext(JSRuntime *rt)+{+ JSContext *ctx;++ ctx = JS_NewContextRaw(rt);+ if (!ctx)+ return NULL;++ JS_AddIntrinsicBaseObjects(ctx);+ JS_AddIntrinsicDate(ctx);+ JS_AddIntrinsicEval(ctx);+ JS_AddIntrinsicStringNormalize(ctx);+ JS_AddIntrinsicRegExp(ctx);+ JS_AddIntrinsicJSON(ctx);+ JS_AddIntrinsicProxy(ctx);+ JS_AddIntrinsicMapSet(ctx);+ JS_AddIntrinsicTypedArrays(ctx);+ JS_AddIntrinsicPromise(ctx);+#ifdef CONFIG_BIGNUM+ JS_AddIntrinsicBigInt(ctx);+#endif+ return ctx;+}++void *JS_GetContextOpaque(JSContext *ctx)+{+ return ctx->user_opaque;+}++void JS_SetContextOpaque(JSContext *ctx, void *opaque)+{+ ctx->user_opaque = opaque;+}++/* set the new value and free the old value after (freeing the value+ can reallocate the object data) */+static inline void set_value(JSContext *ctx, JSValue *pval, JSValue new_val)+{+ JSValue old_val;+ old_val = *pval;+ *pval = new_val;+ JS_FreeValue(ctx, old_val);+}++void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj)+{+ JSRuntime *rt = ctx->rt;+ assert(class_id < rt->class_count);+ set_value(ctx, &ctx->class_proto[class_id], obj);+}++JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id)+{+ JSRuntime *rt = ctx->rt;+ assert(class_id < rt->class_count);+ return JS_DupValue(ctx, ctx->class_proto[class_id]);+}++typedef enum JSFreeModuleEnum {+ JS_FREE_MODULE_ALL,+ JS_FREE_MODULE_NOT_RESOLVED,+ JS_FREE_MODULE_NOT_EVALUATED,+} JSFreeModuleEnum;++/* XXX: would be more efficient with separate module lists */+static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag)+{+ struct list_head *el, *el1;+ list_for_each_safe(el, el1, &ctx->loaded_modules) {+ JSModuleDef *m = list_entry(el, JSModuleDef, link);+ if (flag == JS_FREE_MODULE_ALL ||+ (flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved) ||+ (flag == JS_FREE_MODULE_NOT_EVALUATED && !m->evaluated)) {+ js_free_module_def(ctx, m);+ }+ }+}++JSContext *JS_DupContext(JSContext *ctx)+{+ ctx->header.ref_count++;+ return ctx;+}++/* used by the GC */+static void JS_MarkContext(JSRuntime *rt, JSContext *ctx,+ JS_MarkFunc *mark_func)+{+ int i;+ struct list_head *el;++ /* modules are not seen by the GC, so we directly mark the objects+ referenced by each module */+ list_for_each(el, &ctx->loaded_modules) {+ JSModuleDef *m = list_entry(el, JSModuleDef, link);+ js_mark_module_def(rt, m, mark_func);+ }++ JS_MarkValue(rt, ctx->global_obj, mark_func);+ JS_MarkValue(rt, ctx->global_var_obj, mark_func);++ JS_MarkValue(rt, ctx->throw_type_error, mark_func);+ JS_MarkValue(rt, ctx->eval_obj, mark_func);++ JS_MarkValue(rt, ctx->array_proto_values, mark_func);+ for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {+ JS_MarkValue(rt, ctx->native_error_proto[i], mark_func);+ }+ for(i = 0; i < rt->class_count; i++) {+ JS_MarkValue(rt, ctx->class_proto[i], mark_func);+ }+ JS_MarkValue(rt, ctx->iterator_proto, mark_func);+ JS_MarkValue(rt, ctx->async_iterator_proto, mark_func);+ JS_MarkValue(rt, ctx->promise_ctor, mark_func);+ JS_MarkValue(rt, ctx->array_ctor, mark_func);+ JS_MarkValue(rt, ctx->regexp_ctor, mark_func);+ JS_MarkValue(rt, ctx->function_ctor, mark_func);+ JS_MarkValue(rt, ctx->function_proto, mark_func);++ if (ctx->array_shape)+ mark_func(rt, &ctx->array_shape->header);+}++void JS_FreeContext(JSContext *ctx)+{+ JSRuntime *rt = ctx->rt;+ int i;++ if (--ctx->header.ref_count > 0)+ return;+ assert(ctx->header.ref_count == 0);+ +#ifdef DUMP_ATOMS+ JS_DumpAtoms(ctx->rt);+#endif+#ifdef DUMP_SHAPES+ JS_DumpShapes(ctx->rt);+#endif+#ifdef DUMP_OBJECTS+ {+ struct list_head *el;+ JSGCObjectHeader *p;+ printf("JSObjects: {\n");+ JS_DumpObjectHeader(ctx->rt);+ list_for_each(el, &rt->gc_obj_list) {+ p = list_entry(el, JSGCObjectHeader, link);+ JS_DumpGCObject(rt, p);+ }+ printf("}\n");+ }+#endif+#ifdef DUMP_MEM+ {+ JSMemoryUsage stats;+ JS_ComputeMemoryUsage(rt, &stats);+ JS_DumpMemoryUsage(stdout, &stats, rt);+ }+#endif++ js_free_modules(ctx, JS_FREE_MODULE_ALL);++ JS_FreeValue(ctx, ctx->global_obj);+ JS_FreeValue(ctx, ctx->global_var_obj);++ JS_FreeValue(ctx, ctx->throw_type_error);+ JS_FreeValue(ctx, ctx->eval_obj);++ JS_FreeValue(ctx, ctx->array_proto_values);+ for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {+ JS_FreeValue(ctx, ctx->native_error_proto[i]);+ }+ for(i = 0; i < rt->class_count; i++) {+ JS_FreeValue(ctx, ctx->class_proto[i]);+ }+ js_free_rt(rt, ctx->class_proto);+ JS_FreeValue(ctx, ctx->iterator_proto);+ JS_FreeValue(ctx, ctx->async_iterator_proto);+ JS_FreeValue(ctx, ctx->promise_ctor);+ JS_FreeValue(ctx, ctx->array_ctor);+ JS_FreeValue(ctx, ctx->regexp_ctor);+ JS_FreeValue(ctx, ctx->function_ctor);+ JS_FreeValue(ctx, ctx->function_proto);++ js_free_shape_null(ctx->rt, ctx->array_shape);++ list_del(&ctx->link);+ remove_gc_object(&ctx->header);+ js_free_rt(ctx->rt, ctx);+}++JSRuntime *JS_GetRuntime(JSContext *ctx)+{+ return ctx->rt;+}++void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size)+{+ rt->stack_size = stack_size;+}++static inline BOOL is_strict_mode(JSContext *ctx)+{+ JSStackFrame *sf = ctx->rt->current_stack_frame;+ return (sf && (sf->js_mode & JS_MODE_STRICT));+}++#ifdef CONFIG_BIGNUM+static inline BOOL is_math_mode(JSContext *ctx)+{+ JSStackFrame *sf = ctx->rt->current_stack_frame;+ return (sf && (sf->js_mode & JS_MODE_MATH));+}+#endif++/* JSAtom support */++#define JS_ATOM_TAG_INT (1U << 31)+#define JS_ATOM_MAX_INT (JS_ATOM_TAG_INT - 1)+#define JS_ATOM_MAX ((1U << 30) - 1)++/* return the max count from the hash size */+#define JS_ATOM_COUNT_RESIZE(n) ((n) * 2)++static inline BOOL __JS_AtomIsConst(JSAtom v)+{+#if defined(DUMP_LEAKS) && DUMP_LEAKS > 1+ return (int32_t)v <= 0;+#else+ return (int32_t)v < JS_ATOM_END;+#endif+}++static inline BOOL __JS_AtomIsTaggedInt(JSAtom v)+{+ return (v & JS_ATOM_TAG_INT) != 0;+}++static inline JSAtom __JS_AtomFromUInt32(uint32_t v)+{+ return v | JS_ATOM_TAG_INT;+}++static inline uint32_t __JS_AtomToUInt32(JSAtom atom)+{+ return atom & ~JS_ATOM_TAG_INT;+}++static inline int is_num(int c)+{+ return c >= '0' && c <= '9';+}++/* return TRUE if the string is a number n with 0 <= n <= 2^32-1 */+static inline BOOL is_num_string(uint32_t *pval, const JSString *p)+{+ uint32_t n;+ uint64_t n64;+ int c, i, len;++ len = p->len;+ if (len == 0 || len > 10)+ return FALSE;+ if (p->is_wide_char)+ c = p->u.str16[0];+ else+ c = p->u.str8[0];+ if (is_num(c)) {+ if (c == '0') {+ if (len != 1)+ return FALSE;+ n = 0;+ } else {+ n = c - '0';+ for(i = 1; i < len; i++) {+ if (p->is_wide_char)+ c = p->u.str16[i];+ else+ c = p->u.str8[i];+ if (!is_num(c))+ return FALSE;+ n64 = (uint64_t)n * 10 + (c - '0');+ if ((n64 >> 32) != 0)+ return FALSE;+ n = n64;+ }+ }+ *pval = n;+ return TRUE;+ } else {+ return FALSE;+ }+}++/* XXX: could use faster version ? */+static inline uint32_t hash_string8(const uint8_t *str, size_t len, uint32_t h)+{+ size_t i;++ for(i = 0; i < len; i++)+ h = h * 263 + str[i];+ return h;+}++static inline uint32_t hash_string16(const uint16_t *str,+ size_t len, uint32_t h)+{+ size_t i;++ for(i = 0; i < len; i++)+ h = h * 263 + str[i];+ return h;+}++static uint32_t hash_string(const JSString *str, uint32_t h)+{+ if (str->is_wide_char)+ h = hash_string16(str->u.str16, str->len, h);+ else+ h = hash_string8(str->u.str8, str->len, h);+ return h;+}++static __maybe_unused void JS_DumpString(JSRuntime *rt,+ const JSString *p)+{+ int i, c, sep;++ if (p == NULL) {+ printf("<null>");+ return;+ }+ printf("%d", p->header.ref_count);+ sep = (p->header.ref_count == 1) ? '\"' : '\'';+ putchar(sep);+ for(i = 0; i < p->len; i++) {+ if (p->is_wide_char)+ c = p->u.str16[i];+ else+ c = p->u.str8[i];+ if (c == sep || c == '\\') {+ putchar('\\');+ putchar(c);+ } else if (c >= ' ' && c <= 126) {+ putchar(c);+ } else if (c == '\n') {+ putchar('\\');+ putchar('n');+ } else {+ printf("\\u%04x", c);+ }+ }+ putchar(sep);+}++static __maybe_unused void JS_DumpAtoms(JSRuntime *rt)+{+ JSAtomStruct *p;+ int h, i;+ /* This only dumps hashed atoms, not JS_ATOM_TYPE_SYMBOL atoms */+ printf("JSAtom count=%d size=%d hash_size=%d:\n",+ rt->atom_count, rt->atom_size, rt->atom_hash_size);+ printf("JSAtom hash table: {\n");+ for(i = 0; i < rt->atom_hash_size; i++) {+ h = rt->atom_hash[i];+ if (h) {+ printf(" %d:", i);+ while (h) {+ p = rt->atom_array[h];+ printf(" ");+ JS_DumpString(rt, p);+ h = p->hash_next;+ }+ printf("\n");+ }+ }+ printf("}\n");+ printf("JSAtom table: {\n");+ for(i = 0; i < rt->atom_size; i++) {+ p = rt->atom_array[i];+ if (!atom_is_free(p)) {+ printf(" %d: { %d %08x ", i, p->atom_type, p->hash);+ if (!(p->len == 0 && p->is_wide_char != 0))+ JS_DumpString(rt, p);+ printf(" %d }\n", p->hash_next);+ }+ }+ printf("}\n");+}++static int JS_ResizeAtomHash(JSRuntime *rt, int new_hash_size)+{+ JSAtomStruct *p;+ uint32_t new_hash_mask, h, i, hash_next1, j, *new_hash;++ assert((new_hash_size & (new_hash_size - 1)) == 0); /* power of two */+ new_hash_mask = new_hash_size - 1;+ new_hash = js_mallocz_rt(rt, sizeof(rt->atom_hash[0]) * new_hash_size);+ if (!new_hash)+ return -1;+ for(i = 0; i < rt->atom_hash_size; i++) {+ h = rt->atom_hash[i];+ while (h != 0) {+ p = rt->atom_array[h];+ hash_next1 = p->hash_next;+ /* add in new hash table */+ j = p->hash & new_hash_mask;+ p->hash_next = new_hash[j];+ new_hash[j] = h;+ h = hash_next1;+ }+ }+ js_free_rt(rt, rt->atom_hash);+ rt->atom_hash = new_hash;+ rt->atom_hash_size = new_hash_size;+ rt->atom_count_resize = JS_ATOM_COUNT_RESIZE(new_hash_size);+ // JS_DumpAtoms(rt);+ return 0;+}++static int JS_InitAtoms(JSRuntime *rt)+{+ int i, len, atom_type;+ const char *p;++ rt->atom_hash_size = 0;+ rt->atom_hash = NULL;+ rt->atom_count = 0;+ rt->atom_size = 0;+ rt->atom_free_index = 0;+ if (JS_ResizeAtomHash(rt, 256)) /* there are at least 195 predefined atoms */+ return -1;++ p = js_atom_init;+ for(i = 1; i < JS_ATOM_END; i++) {+ if (i == JS_ATOM_Private_brand)+ atom_type = JS_ATOM_TYPE_PRIVATE;+ else if (i >= JS_ATOM_Symbol_toPrimitive)+ atom_type = JS_ATOM_TYPE_SYMBOL;+ else+ atom_type = JS_ATOM_TYPE_STRING;+ len = strlen(p);+ if (__JS_NewAtomInit(rt, p, len, atom_type) == JS_ATOM_NULL)+ return -1;+ p = p + len + 1;+ }+ return 0;+}++static JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v)+{+ JSAtomStruct *p;++ if (!__JS_AtomIsConst(v)) {+ p = rt->atom_array[v];+ p->header.ref_count++;+ }+ return v;+}++JSAtom JS_DupAtom(JSContext *ctx, JSAtom v)+{+ JSRuntime *rt;+ JSAtomStruct *p;++ if (!__JS_AtomIsConst(v)) {+ rt = ctx->rt;+ p = rt->atom_array[v];+ p->header.ref_count++;+ }+ return v;+}++static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v)+{+ JSRuntime *rt;+ JSAtomStruct *p;++ rt = ctx->rt;+ if (__JS_AtomIsTaggedInt(v))+ return JS_ATOM_KIND_STRING;+ p = rt->atom_array[v];+ switch(p->atom_type) {+ case JS_ATOM_TYPE_STRING:+ return JS_ATOM_KIND_STRING;+ case JS_ATOM_TYPE_GLOBAL_SYMBOL:+ return JS_ATOM_KIND_SYMBOL;+ case JS_ATOM_TYPE_SYMBOL:+ switch(p->hash) {+ case JS_ATOM_HASH_SYMBOL:+ return JS_ATOM_KIND_SYMBOL;+ case JS_ATOM_HASH_PRIVATE:+ return JS_ATOM_KIND_PRIVATE;+ default:+ abort();+ }+ default:+ abort();+ }+}++static BOOL JS_AtomIsString(JSContext *ctx, JSAtom v)+{+ return JS_AtomGetKind(ctx, v) == JS_ATOM_KIND_STRING;+}++static JSAtom js_get_atom_index(JSRuntime *rt, JSAtomStruct *p)+{+ uint32_t i = p->hash_next; /* atom_index */+ if (p->atom_type != JS_ATOM_TYPE_SYMBOL) {+ JSAtomStruct *p1;++ i = rt->atom_hash[p->hash & (rt->atom_hash_size - 1)];+ p1 = rt->atom_array[i];+ while (p1 != p) {+ assert(i != 0);+ i = p1->hash_next;+ p1 = rt->atom_array[i];+ }+ }+ return i;+}++/* string case (internal). Return JS_ATOM_NULL if error. 'str' is+ freed. */+static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type)+{+ uint32_t h, h1, i;+ JSAtomStruct *p;+ int len;++#if 0+ printf("__JS_NewAtom: "); JS_DumpString(rt, str); printf("\n");+#endif+ if (atom_type < JS_ATOM_TYPE_SYMBOL) {+ /* str is not NULL */+ if (str->atom_type == atom_type) {+ /* str is the atom, return its index */+ i = js_get_atom_index(rt, str);+ /* reduce string refcount and increase atom's unless constant */+ if (__JS_AtomIsConst(i))+ str->header.ref_count--;+ return i;+ }+ /* try and locate an already registered atom */+ len = str->len;+ h = hash_string(str, atom_type);+ h &= JS_ATOM_HASH_MASK;+ h1 = h & (rt->atom_hash_size - 1);+ i = rt->atom_hash[h1];+ while (i != 0) {+ p = rt->atom_array[i];+ if (p->hash == h &&+ p->atom_type == atom_type &&+ p->len == len &&+ js_string_memcmp(p, str, len) == 0) {+ if (!__JS_AtomIsConst(i))+ p->header.ref_count++;+ goto done;+ }+ i = p->hash_next;+ }+ } else {+ h1 = 0; /* avoid warning */+ if (atom_type == JS_ATOM_TYPE_SYMBOL) {+ h = JS_ATOM_HASH_SYMBOL;+ } else {+ h = JS_ATOM_HASH_PRIVATE;+ atom_type = JS_ATOM_TYPE_SYMBOL;+ }+ }++ if (rt->atom_free_index == 0) {+ /* allow new atom entries */+ uint32_t new_size, start;+ JSAtomStruct **new_array;++ /* alloc new with size progression 3/2:+ 4 6 9 13 19 28 42 63 94 141 211 316 474 711 1066 1599 2398 3597 5395 8092+ preallocating space for predefined atoms (at least 195).+ */+ new_size = max_int(211, rt->atom_size * 3 / 2);+ if (new_size > JS_ATOM_MAX)+ goto fail;+ /* XXX: should use realloc2 to use slack space */+ new_array = js_realloc_rt(rt, rt->atom_array, sizeof(*new_array) * new_size);+ if (!new_array)+ goto fail;+ /* Note: the atom 0 is not used */+ start = rt->atom_size;+ if (start == 0) {+ /* JS_ATOM_NULL entry */+ p = js_mallocz_rt(rt, sizeof(JSAtomStruct));+ if (!p) {+ js_free_rt(rt, new_array);+ goto fail;+ }+ p->header.ref_count = 1; /* not refcounted */+ p->atom_type = JS_ATOM_TYPE_SYMBOL;+#ifdef DUMP_LEAKS+ list_add_tail(&p->link, &rt->string_list);+#endif+ new_array[0] = p;+ rt->atom_count++;+ start = 1;+ }+ rt->atom_size = new_size;+ rt->atom_array = new_array;+ rt->atom_free_index = start;+ for(i = start; i < new_size; i++) {+ uint32_t next;+ if (i == (new_size - 1))+ next = 0;+ else+ next = i + 1;+ rt->atom_array[i] = atom_set_free(next);+ }+ }++ if (str) {+ if (str->atom_type == 0) {+ p = str;+ p->atom_type = atom_type;+ } else {+ p = js_malloc_rt(rt, sizeof(JSString) ++ (str->len << str->is_wide_char) ++ 1 - str->is_wide_char);+ if (unlikely(!p))+ goto fail;+ p->header.ref_count = 1;+ p->is_wide_char = str->is_wide_char;+ p->len = str->len;+#ifdef DUMP_LEAKS+ list_add_tail(&p->link, &rt->string_list);+#endif+ memcpy(p->u.str8, str->u.str8, (str->len << str->is_wide_char) ++ 1 - str->is_wide_char);+ js_free_string(rt, str);+ }+ } else {+ p = js_malloc_rt(rt, sizeof(JSAtomStruct)); /* empty wide string */+ if (!p)+ return JS_ATOM_NULL;+ p->header.ref_count = 1;+ p->is_wide_char = 1; /* Hack to represent NULL as a JSString */+ p->len = 0;+#ifdef DUMP_LEAKS+ list_add_tail(&p->link, &rt->string_list);+#endif+ }++ /* use an already free entry */+ i = rt->atom_free_index;+ rt->atom_free_index = atom_get_free(rt->atom_array[i]);+ rt->atom_array[i] = p;++ p->hash = h;+ p->hash_next = i; /* atom_index */+ p->atom_type = atom_type;++ rt->atom_count++;++ if (atom_type != JS_ATOM_TYPE_SYMBOL) {+ p->hash_next = rt->atom_hash[h1];+ rt->atom_hash[h1] = i;+ if (unlikely(rt->atom_count >= rt->atom_count_resize))+ JS_ResizeAtomHash(rt, rt->atom_hash_size * 2);+ }++ // JS_DumpAtoms(rt);+ return i;++ fail:+ i = JS_ATOM_NULL;+ done:+ if (str)+ js_free_string(rt, str);+ return i;+}++/* only works with zero terminated 8 bit strings */+static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len,+ int atom_type)+{+ JSString *p;+ p = js_alloc_string_rt(rt, len, 0);+ if (!p)+ return JS_ATOM_NULL;+ memcpy(p->u.str8, str, len);+ p->u.str8[len] = '\0';+ return __JS_NewAtom(rt, p, atom_type);+}++static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len,+ int atom_type)+{+ uint32_t h, h1, i;+ JSAtomStruct *p;++ h = hash_string8((const uint8_t *)str, len, JS_ATOM_TYPE_STRING);+ h &= JS_ATOM_HASH_MASK;+ h1 = h & (rt->atom_hash_size - 1);+ i = rt->atom_hash[h1];+ while (i != 0) {+ p = rt->atom_array[i];+ if (p->hash == h &&+ p->atom_type == JS_ATOM_TYPE_STRING &&+ p->len == len &&+ p->is_wide_char == 0 &&+ memcmp(p->u.str8, str, len) == 0) {+ if (!__JS_AtomIsConst(i))+ p->header.ref_count++;+ return i;+ }+ i = p->hash_next;+ }+ return JS_ATOM_NULL;+}++static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p)+{+#if 0 /* JS_ATOM_NULL is not refcounted: __JS_AtomIsConst() includes 0 */+ if (unlikely(i == JS_ATOM_NULL)) {+ p->header.ref_count = INT32_MAX / 2;+ return;+ }+#endif+ uint32_t i = p->hash_next; /* atom_index */+ if (p->atom_type != JS_ATOM_TYPE_SYMBOL) {+ JSAtomStruct *p0, *p1;+ uint32_t h0;++ h0 = p->hash & (rt->atom_hash_size - 1);+ i = rt->atom_hash[h0];+ p1 = rt->atom_array[i];+ if (p1 == p) {+ rt->atom_hash[h0] = p1->hash_next;+ } else {+ for(;;) {+ assert(i != 0);+ p0 = p1;+ i = p1->hash_next;+ p1 = rt->atom_array[i];+ if (p1 == p) {+ p0->hash_next = p1->hash_next;+ break;+ }+ }+ }+ }+ /* insert in free atom list */+ rt->atom_array[i] = atom_set_free(rt->atom_free_index);+ rt->atom_free_index = i;+ /* free the string structure */+#ifdef DUMP_LEAKS+ list_del(&p->link);+#endif+ js_free_rt(rt, p);+ rt->atom_count--;+ assert(rt->atom_count >= 0);+}++static void __JS_FreeAtom(JSRuntime *rt, uint32_t i)+{+ JSAtomStruct *p;++ p = rt->atom_array[i];+ if (--p->header.ref_count > 0)+ return;+ JS_FreeAtomStruct(rt, p);+}++/* Warning: 'p' is freed */+static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p)+{+ JSRuntime *rt = ctx->rt;+ uint32_t n;+ if (is_num_string(&n, p)) {+ if (n <= JS_ATOM_MAX_INT) {+ js_free_string(rt, p);+ return __JS_AtomFromUInt32(n);+ }+ }+ /* XXX: should generate an exception */+ return __JS_NewAtom(rt, p, JS_ATOM_TYPE_STRING);+}++JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len)+{+ JSValue val;++ if (len == 0 || !is_digit(*str)) {+ JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING);+ if (atom)+ return atom;+ }+ val = JS_NewStringLen(ctx, str, len);+ if (JS_IsException(val))+ return JS_ATOM_NULL;+ return JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(val));+}++JSAtom JS_NewAtom(JSContext *ctx, const char *str)+{+ return JS_NewAtomLen(ctx, str, strlen(str));+}++JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n)+{+ if (n <= JS_ATOM_MAX_INT) {+ return __JS_AtomFromUInt32(n);+ } else {+ char buf[11];+ JSValue val;+ snprintf(buf, sizeof(buf), "%u", n);+ val = JS_NewString(ctx, buf);+ if (JS_IsException(val))+ return JS_ATOM_NULL;+ return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val),+ JS_ATOM_TYPE_STRING);+ }+}++static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n)+{+ if ((uint64_t)n <= JS_ATOM_MAX_INT) {+ return __JS_AtomFromUInt32((uint32_t)n);+ } else {+ char buf[24];+ JSValue val;+ snprintf(buf, sizeof(buf), "%" PRId64 , n);+ val = JS_NewString(ctx, buf);+ if (JS_IsException(val))+ return JS_ATOM_NULL;+ return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val),+ JS_ATOM_TYPE_STRING);+ }+}++/* 'p' is freed */+static JSValue JS_NewSymbol(JSContext *ctx, JSString *p, int atom_type)+{+ JSRuntime *rt = ctx->rt;+ JSAtom atom;+ atom = __JS_NewAtom(rt, p, atom_type);+ if (atom == JS_ATOM_NULL)+ return JS_ThrowOutOfMemory(ctx);+ return JS_MKPTR(JS_TAG_SYMBOL, rt->atom_array[atom]);+}++/* descr must be a non-numeric string atom */+static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr,+ int atom_type)+{+ JSRuntime *rt = ctx->rt;+ JSString *p;++ assert(!__JS_AtomIsTaggedInt(descr));+ assert(descr < rt->atom_size);+ p = rt->atom_array[descr];+ JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));+ return JS_NewSymbol(ctx, p, atom_type);+}++#define ATOM_GET_STR_BUF_SIZE 64++/* Should only be used for debug. */+static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size,+ JSAtom atom)+{+ if (__JS_AtomIsTaggedInt(atom)) {+ snprintf(buf, buf_size, "%u", __JS_AtomToUInt32(atom));+ } else {+ JSAtomStruct *p;+ assert(atom < rt->atom_size);+ if (atom == JS_ATOM_NULL) {+ snprintf(buf, buf_size, "<null>");+ } else {+ int i, c;+ char *q;+ JSString *str;++ q = buf;+ p = rt->atom_array[atom];+ assert(!atom_is_free(p));+ str = p;+ if (str) {+ if (!str->is_wide_char) {+ /* special case ASCII strings */+ c = 0;+ for(i = 0; i < str->len; i++) {+ c |= str->u.str8[i];+ }+ if (c < 0x80)+ return (const char *)str->u.str8;+ }+ for(i = 0; i < str->len; i++) {+ if (str->is_wide_char)+ c = str->u.str16[i];+ else+ c = str->u.str8[i];+ if ((q - buf) >= buf_size - UTF8_CHAR_LEN_MAX)+ break;+ if (c < 128) {+ *q++ = c;+ } else {+ q += unicode_to_utf8((uint8_t *)q, c);+ }+ }+ }+ *q = '\0';+ }+ }+ return buf;+}++static const char *JS_AtomGetStr(JSContext *ctx, char *buf, int buf_size, JSAtom atom)+{+ return JS_AtomGetStrRT(ctx->rt, buf, buf_size, atom);+}++static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, BOOL force_string)+{+ char buf[ATOM_GET_STR_BUF_SIZE];++ if (__JS_AtomIsTaggedInt(atom)) {+ snprintf(buf, sizeof(buf), "%u", __JS_AtomToUInt32(atom));+ return JS_NewString(ctx, buf);+ } else {+ JSRuntime *rt = ctx->rt;+ JSAtomStruct *p;+ assert(atom < rt->atom_size);+ p = rt->atom_array[atom];+ if (p->atom_type == JS_ATOM_TYPE_STRING) {+ goto ret_string;+ } else if (force_string) {+ if (p->len == 0 && p->is_wide_char != 0) {+ /* no description string */+ p = rt->atom_array[JS_ATOM_empty_string];+ }+ ret_string:+ return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));+ } else {+ return JS_DupValue(ctx, JS_MKPTR(JS_TAG_SYMBOL, p));+ }+ }+}++JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom)+{+ return __JS_AtomToValue(ctx, atom, FALSE);+}++JSValue JS_AtomToString(JSContext *ctx, JSAtom atom)+{+ return __JS_AtomToValue(ctx, atom, TRUE);+}++/* return TRUE if the atom is an array index (i.e. 0 <= index <=+ 2^32-2 and return its value */+static BOOL JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom)+{+ if (__JS_AtomIsTaggedInt(atom)) {+ *pval = __JS_AtomToUInt32(atom);+ return TRUE;+ } else {+ JSRuntime *rt = ctx->rt;+ JSAtomStruct *p;+ uint32_t val;++ assert(atom < rt->atom_size);+ p = rt->atom_array[atom];+ if (p->atom_type == JS_ATOM_TYPE_STRING &&+ is_num_string(&val, p) && val != -1) {+ *pval = val;+ return TRUE;+ } else {+ *pval = 0;+ return FALSE;+ }+ }+}++/* This test must be fast if atom is not a numeric index (e.g. a+ method name). Return JS_UNDEFINED if not a numeric+ index. JS_EXCEPTION can also be returned. */+static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom)+{+ JSRuntime *rt = ctx->rt;+ JSAtomStruct *p1;+ JSString *p;+ int c, len, ret;+ JSValue num, str;++ if (__JS_AtomIsTaggedInt(atom))+ return JS_NewInt32(ctx, __JS_AtomToUInt32(atom));+ assert(atom < rt->atom_size);+ p1 = rt->atom_array[atom];+ if (p1->atom_type != JS_ATOM_TYPE_STRING)+ return JS_UNDEFINED;+ p = p1;+ len = p->len;+ if (p->is_wide_char) {+ const uint16_t *r = p->u.str16, *r_end = p->u.str16 + len;+ if (r >= r_end)+ return JS_UNDEFINED;+ c = *r;+ if (c == '-') {+ if (r >= r_end)+ return JS_UNDEFINED;+ r++;+ c = *r;+ /* -0 case is specific */+ if (c == '0' && len == 2)+ goto minus_zero;+ }+ /* XXX: should test NaN, but the tests do not check it */+ if (!is_num(c)) {+ /* XXX: String should be normalized, therefore 8-bit only */+ const uint16_t nfinity16[7] = { 'n', 'f', 'i', 'n', 'i', 't', 'y' };+ if (!(c =='I' && (r_end - r) == 8 &&+ !memcmp(r + 1, nfinity16, sizeof(nfinity16))))+ return JS_UNDEFINED;+ }+ } else {+ const uint8_t *r = p->u.str8, *r_end = p->u.str8 + len;+ if (r >= r_end)+ return JS_UNDEFINED;+ c = *r;+ if (c == '-') {+ if (r >= r_end)+ return JS_UNDEFINED;+ r++;+ c = *r;+ /* -0 case is specific */+ if (c == '0' && len == 2) {+ minus_zero:+ return __JS_NewFloat64(ctx, -0.0);+ }+ }+ if (!is_num(c)) {+ if (!(c =='I' && (r_end - r) == 8 &&+ !memcmp(r + 1, "nfinity", 7)))+ return JS_UNDEFINED;+ }+ }+ /* XXX: bignum: would be better to only accept integer to avoid+ relying on current floating point precision */+ /* this is ECMA CanonicalNumericIndexString primitive */+ num = JS_ToNumber(ctx, JS_MKPTR(JS_TAG_STRING, p));+ if (JS_IsException(num))+ return num;+ str = JS_ToString(ctx, num);+ if (JS_IsException(str)) {+ JS_FreeValue(ctx, num);+ return str;+ }+ ret = js_string_compare(ctx, p, JS_VALUE_GET_STRING(str));+ JS_FreeValue(ctx, str);+ if (ret == 0) {+ return num;+ } else {+ JS_FreeValue(ctx, num);+ return JS_UNDEFINED;+ }+}++/* return -1 if exception or TRUE/FALSE */+static int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom)+{+ JSValue num;+ num = JS_AtomIsNumericIndex1(ctx, atom);+ if (likely(JS_IsUndefined(num)))+ return FALSE;+ if (JS_IsException(num))+ return -1;+ JS_FreeValue(ctx, num);+ return TRUE;+}++void JS_FreeAtom(JSContext *ctx, JSAtom v)+{+ if (!__JS_AtomIsConst(v))+ __JS_FreeAtom(ctx->rt, v);+}++void JS_FreeAtomRT(JSRuntime *rt, JSAtom v)+{+ if (!__JS_AtomIsConst(v))+ __JS_FreeAtom(rt, v);+}++/* return TRUE if 'v' is a symbol with a string description */+static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v)+{+ JSRuntime *rt;+ JSAtomStruct *p;++ rt = ctx->rt;+ if (__JS_AtomIsTaggedInt(v))+ return FALSE;+ p = rt->atom_array[v];+ return (((p->atom_type == JS_ATOM_TYPE_SYMBOL &&+ p->hash == JS_ATOM_HASH_SYMBOL) ||+ p->atom_type == JS_ATOM_TYPE_GLOBAL_SYMBOL) &&+ !(p->len == 0 && p->is_wide_char != 0));+}++static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom)+{+ char buf[ATOM_GET_STR_BUF_SIZE];+ const char *p;+ int i;++ /* XXX: should handle embedded null characters */+ /* XXX: should move encoding code to JS_AtomGetStr */+ p = JS_AtomGetStr(ctx, buf, sizeof(buf), atom);+ for (i = 0; p[i]; i++) {+ int c = (unsigned char)p[i];+ if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||+ (c == '_' || c == '$') || (c >= '0' && c <= '9' && i > 0)))+ break;+ }+ if (i > 0 && p[i] == '\0') {+ printf("%s", p);+ } else {+ putchar('"');+ printf("%.*s", i, p);+ for (; p[i]; i++) {+ int c = (unsigned char)p[i];+ if (c == '\"' || c == '\\') {+ putchar('\\');+ putchar(c);+ } else if (c >= ' ' && c <= 126) {+ putchar(c);+ } else if (c == '\n') {+ putchar('\\');+ putchar('n');+ } else {+ printf("\\u%04x", c);+ }+ }+ putchar('\"');+ }+}++/* free with JS_FreeCString() */+const char *JS_AtomToCString(JSContext *ctx, JSAtom atom)+{+ JSValue str;+ const char *cstr;++ str = JS_AtomToString(ctx, atom);+ if (JS_IsException(str))+ return NULL;+ cstr = JS_ToCString(ctx, str);+ JS_FreeValue(ctx, str);+ return cstr;+}++/* return a string atom containing name concatenated with str1 */+static JSAtom js_atom_concat_str(JSContext *ctx, JSAtom name, const char *str1)+{+ JSValue str;+ JSAtom atom;+ const char *cstr;+ char *cstr2;+ size_t len, len1;+ + str = JS_AtomToString(ctx, name);+ if (JS_IsException(str))+ return JS_ATOM_NULL;+ cstr = JS_ToCStringLen(ctx, &len, str);+ if (!cstr)+ goto fail;+ len1 = strlen(str1);+ cstr2 = js_malloc(ctx, len + len1 + 1);+ if (!cstr2)+ goto fail;+ memcpy(cstr2, cstr, len);+ memcpy(cstr2 + len, str1, len1);+ cstr2[len + len1] = '\0';+ atom = JS_NewAtomLen(ctx, cstr2, len + len1);+ js_free(ctx, cstr2);+ JS_FreeCString(ctx, cstr);+ JS_FreeValue(ctx, str);+ return atom;+ fail:+ JS_FreeCString(ctx, cstr);+ JS_FreeValue(ctx, str);+ return JS_ATOM_NULL;+}++static JSAtom js_atom_concat_num(JSContext *ctx, JSAtom name, uint32_t n)+{+ char buf[16];+ snprintf(buf, sizeof(buf), "%u", n);+ return js_atom_concat_str(ctx, name, buf);+}++static inline BOOL JS_IsEmptyString(JSValueConst v)+{+ return JS_VALUE_GET_TAG(v) == JS_TAG_STRING && JS_VALUE_GET_STRING(v)->len == 0;+}++/* JSClass support */++/* a new class ID is allocated if *pclass_id != 0 */+JSClassID JS_NewClassID(JSClassID *pclass_id)+{+ JSClassID class_id;+ /* XXX: make it thread safe */+ class_id = *pclass_id;+ if (class_id == 0) {+ class_id = js_class_id_alloc++;+ *pclass_id = class_id;+ }+ return class_id;+}++BOOL JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id)+{+ return (class_id < rt->class_count &&+ rt->class_array[class_id].class_id != 0);+}++/* create a new object internal class. Return -1 if error, 0 if+ OK. The finalizer can be NULL if none is needed. */+static int JS_NewClass1(JSRuntime *rt, JSClassID class_id,+ const JSClassDef *class_def, JSAtom name)+{+ int new_size, i;+ JSClass *cl, *new_class_array;+ struct list_head *el;++ if (class_id < rt->class_count &&+ rt->class_array[class_id].class_id != 0)+ return -1;++ if (class_id >= rt->class_count) {+ new_size = max_int(JS_CLASS_INIT_COUNT,+ max_int(class_id + 1, rt->class_count * 3 / 2));++ /* reallocate the context class prototype array, if any */+ list_for_each(el, &rt->context_list) {+ JSContext *ctx = list_entry(el, JSContext, link);+ JSValue *new_tab;+ new_tab = js_realloc_rt(rt, ctx->class_proto,+ sizeof(ctx->class_proto[0]) * new_size);+ if (!new_tab)+ return -1;+ for(i = rt->class_count; i < new_size; i++)+ new_tab[i] = JS_NULL;+ ctx->class_proto = new_tab;+ }+ /* reallocate the class array */+ new_class_array = js_realloc_rt(rt, rt->class_array,+ sizeof(JSClass) * new_size);+ if (!new_class_array)+ return -1;+ memset(new_class_array + rt->class_count, 0,+ (new_size - rt->class_count) * sizeof(JSClass));+ rt->class_array = new_class_array;+ rt->class_count = new_size;+ }+ cl = &rt->class_array[class_id];+ cl->class_id = class_id;+ cl->class_name = JS_DupAtomRT(rt, name);+ cl->finalizer = class_def->finalizer;+ cl->gc_mark = class_def->gc_mark;+ cl->call = class_def->call;+ cl->exotic = class_def->exotic;+ return 0;+}++int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def)+{+ int ret, len;+ JSAtom name;++ len = strlen(class_def->class_name);+ name = __JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);+ if (name == JS_ATOM_NULL) {+ name = __JS_NewAtomInit(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);+ if (name == JS_ATOM_NULL)+ return -1;+ }+ ret = JS_NewClass1(rt, class_id, class_def, name);+ JS_FreeAtomRT(rt, name);+ return ret;+}++static JSValue js_new_string8(JSContext *ctx, const uint8_t *buf, int len)+{+ JSString *str;++ if (len <= 0) {+ return JS_AtomToString(ctx, JS_ATOM_empty_string);+ }+ str = js_alloc_string(ctx, len, 0);+ if (!str)+ return JS_EXCEPTION;+ memcpy(str->u.str8, buf, len);+ str->u.str8[len] = '\0';+ return JS_MKPTR(JS_TAG_STRING, str);+}++static JSValue js_new_string16(JSContext *ctx, const uint16_t *buf, int len)+{+ JSString *str;+ str = js_alloc_string(ctx, len, 1);+ if (!str)+ return JS_EXCEPTION;+ memcpy(str->u.str16, buf, len * 2);+ return JS_MKPTR(JS_TAG_STRING, str);+}++static JSValue js_new_string_char(JSContext *ctx, uint16_t c)+{+ if (c < 0x100) {+ uint8_t ch8 = c;+ return js_new_string8(ctx, &ch8, 1);+ } else {+ uint16_t ch16 = c;+ return js_new_string16(ctx, &ch16, 1);+ }+}++static JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end)+{+ int len = end - start;+ if (start == 0 && end == p->len) {+ return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));+ }+ if (p->is_wide_char && len > 0) {+ JSString *str;+ int i;+ uint16_t c = 0;+ for (i = start; i < end; i++) {+ c |= p->u.str16[i];+ }+ if (c > 0xFF)+ return js_new_string16(ctx, p->u.str16 + start, len);++ str = js_alloc_string(ctx, len, 0);+ if (!str)+ return JS_EXCEPTION;+ for (i = 0; i < len; i++) {+ str->u.str8[i] = p->u.str16[start + i];+ }+ str->u.str8[len] = '\0';+ return JS_MKPTR(JS_TAG_STRING, str);+ } else {+ return js_new_string8(ctx, p->u.str8 + start, len);+ }+}++typedef struct StringBuffer {+ JSContext *ctx;+ JSString *str;+ int len;+ int size;+ int is_wide_char;+ int error_status;+} StringBuffer;++/* It is valid to call string_buffer_end() and all string_buffer functions even+ if string_buffer_init() or another string_buffer function returns an error.+ If the error_status is set, string_buffer_end() returns JS_EXCEPTION.+ */+static int string_buffer_init2(JSContext *ctx, StringBuffer *s, int size,+ int is_wide)+{+ s->ctx = ctx;+ s->size = size;+ s->len = 0;+ s->is_wide_char = is_wide;+ s->error_status = 0;+ s->str = js_alloc_string(ctx, size, is_wide);+ if (unlikely(!s->str)) {+ s->size = 0;+ return s->error_status = -1;+ }+#ifdef DUMP_LEAKS+ /* the StringBuffer may reallocate the JSString, only link it at the end */+ list_del(&s->str->link);+#endif+ return 0;+}++static inline int string_buffer_init(JSContext *ctx, StringBuffer *s, int size)+{+ return string_buffer_init2(ctx, s, size, 0);+}++static void string_buffer_free(StringBuffer *s)+{+ js_free(s->ctx, s->str);+ s->str = NULL;+}++static int string_buffer_set_error(StringBuffer *s)+{+ js_free(s->ctx, s->str);+ s->str = NULL;+ s->size = 0;+ s->len = 0;+ return s->error_status = -1;+}++static no_inline int string_buffer_widen(StringBuffer *s, int size)+{+ JSString *str;+ size_t slack;+ int i;++ if (s->error_status)+ return -1;++ str = js_realloc2(s->ctx, s->str, sizeof(JSString) + (size << 1), &slack);+ if (!str)+ return string_buffer_set_error(s);+ size += slack >> 1;+ for(i = s->len; i-- > 0;) {+ str->u.str16[i] = str->u.str8[i];+ }+ s->is_wide_char = 1;+ s->size = size;+ s->str = str;+ return 0;+}++static no_inline int string_buffer_realloc(StringBuffer *s, int new_len, int c)+{+ JSString *new_str;+ int new_size;+ size_t new_size_bytes, slack;++ if (s->error_status)+ return -1;++ if (new_len > JS_STRING_LEN_MAX) {+ JS_ThrowInternalError(s->ctx, "string too long");+ return string_buffer_set_error(s);+ }+ new_size = min_int(max_int(new_len, s->size * 3 / 2), JS_STRING_LEN_MAX);+ if (!s->is_wide_char && c >= 0x100) {+ return string_buffer_widen(s, new_size);+ }+ new_size_bytes = sizeof(JSString) + (new_size << s->is_wide_char) + 1 - s->is_wide_char;+ new_str = js_realloc2(s->ctx, s->str, new_size_bytes, &slack);+ if (!new_str)+ return string_buffer_set_error(s);+ new_size = min_int(new_size + (slack >> s->is_wide_char), JS_STRING_LEN_MAX);+ s->size = new_size;+ s->str = new_str;+ return 0;+}++static no_inline int string_buffer_putc_slow(StringBuffer *s, uint32_t c)+{+ if (unlikely(s->len >= s->size)) {+ if (string_buffer_realloc(s, s->len + 1, c))+ return -1;+ }+ if (s->is_wide_char) {+ s->str->u.str16[s->len++] = c;+ } else if (c < 0x100) {+ s->str->u.str8[s->len++] = c;+ } else {+ if (string_buffer_widen(s, s->size))+ return -1;+ s->str->u.str16[s->len++] = c;+ }+ return 0;+}++/* 0 <= c <= 0xff */+static int string_buffer_putc8(StringBuffer *s, uint32_t c)+{+ if (unlikely(s->len >= s->size)) {+ if (string_buffer_realloc(s, s->len + 1, c))+ return -1;+ }+ if (s->is_wide_char) {+ s->str->u.str16[s->len++] = c;+ } else {+ s->str->u.str8[s->len++] = c;+ }+ return 0;+}++/* 0 <= c <= 0xffff */+static int string_buffer_putc16(StringBuffer *s, uint32_t c)+{+ if (likely(s->len < s->size)) {+ if (s->is_wide_char) {+ s->str->u.str16[s->len++] = c;+ return 0;+ } else if (c < 0x100) {+ s->str->u.str8[s->len++] = c;+ return 0;+ }+ }+ return string_buffer_putc_slow(s, c);+}++/* 0 <= c <= 0x10ffff */+static int string_buffer_putc(StringBuffer *s, uint32_t c)+{+ if (unlikely(c >= 0x10000)) {+ /* surrogate pair */+ c -= 0x10000;+ if (string_buffer_putc16(s, (c >> 10) + 0xd800))+ return -1;+ c = (c & 0x3ff) + 0xdc00;+ }+ return string_buffer_putc16(s, c);+}++static int string_get(const JSString *p, int idx) {+ return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx];+}++static int string_getc(const JSString *p, int *pidx)+{+ int idx, c, c1;+ idx = *pidx;+ if (p->is_wide_char) {+ c = p->u.str16[idx++];+ if (c >= 0xd800 && c < 0xdc00 && idx < p->len) {+ c1 = p->u.str16[idx];+ if (c1 >= 0xdc00 && c1 < 0xe000) {+ c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;+ idx++;+ }+ }+ } else {+ c = p->u.str8[idx++];+ }+ *pidx = idx;+ return c;+}++static int string_buffer_write8(StringBuffer *s, const uint8_t *p, int len)+{+ int i;++ if (s->len + len > s->size) {+ if (string_buffer_realloc(s, s->len + len, 0))+ return -1;+ }+ if (s->is_wide_char) {+ for (i = 0; i < len; i++) {+ s->str->u.str16[s->len + i] = p[i];+ }+ s->len += len;+ } else {+ memcpy(&s->str->u.str8[s->len], p, len);+ s->len += len;+ }+ return 0;+}++static int string_buffer_write16(StringBuffer *s, const uint16_t *p, int len)+{+ int c = 0, i;++ for (i = 0; i < len; i++) {+ c |= p[i];+ }+ if (s->len + len > s->size) {+ if (string_buffer_realloc(s, s->len + len, c))+ return -1;+ } else if (!s->is_wide_char && c >= 0x100) {+ if (string_buffer_widen(s, s->size))+ return -1;+ }+ if (s->is_wide_char) {+ memcpy(&s->str->u.str16[s->len], p, len << 1);+ s->len += len;+ } else {+ for (i = 0; i < len; i++) {+ s->str->u.str8[s->len + i] = p[i];+ }+ s->len += len;+ }+ return 0;+}++/* appending an ASCII string */+static int string_buffer_puts8(StringBuffer *s, const char *str)+{+ return string_buffer_write8(s, (const uint8_t *)str, strlen(str));+}++static int string_buffer_concat(StringBuffer *s, const JSString *p,+ uint32_t from, uint32_t to)+{+ if (to <= from)+ return 0;+ if (p->is_wide_char)+ return string_buffer_write16(s, p->u.str16 + from, to - from);+ else+ return string_buffer_write8(s, p->u.str8 + from, to - from);+}++static int string_buffer_concat_value(StringBuffer *s, JSValueConst v)+{+ JSString *p;+ JSValue v1;+ int res;++ if (s->error_status) {+ /* prevent exception overload */+ return -1;+ }+ if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {+ v1 = JS_ToString(s->ctx, v);+ if (JS_IsException(v1))+ return string_buffer_set_error(s);+ p = JS_VALUE_GET_STRING(v1);+ res = string_buffer_concat(s, p, 0, p->len);+ JS_FreeValue(s->ctx, v1);+ return res;+ }+ p = JS_VALUE_GET_STRING(v);+ return string_buffer_concat(s, p, 0, p->len);+}++static int string_buffer_concat_value_free(StringBuffer *s, JSValue v)+{+ JSString *p;+ int res;++ if (s->error_status) {+ /* prevent exception overload */+ JS_FreeValue(s->ctx, v);+ return -1;+ }+ if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {+ v = JS_ToStringFree(s->ctx, v);+ if (JS_IsException(v))+ return string_buffer_set_error(s);+ }+ p = JS_VALUE_GET_STRING(v);+ res = string_buffer_concat(s, p, 0, p->len);+ JS_FreeValue(s->ctx, v);+ return res;+}++static int string_buffer_fill(StringBuffer *s, int c, int count)+{+ /* XXX: optimize */+ if (s->len + count > s->size) {+ if (string_buffer_realloc(s, s->len + count, c))+ return -1;+ }+ while (count-- > 0) {+ if (string_buffer_putc16(s, c))+ return -1;+ }+ return 0;+}++static JSValue string_buffer_end(StringBuffer *s)+{+ JSString *str;+ str = s->str;+ if (s->error_status)+ return JS_EXCEPTION;+ if (s->len == 0) {+ js_free(s->ctx, str);+ s->str = NULL;+ return JS_AtomToString(s->ctx, JS_ATOM_empty_string);+ }+ if (s->len < s->size) {+ /* smaller size so js_realloc should not fail, but OK if it does */+ /* XXX: should add some slack to avoid unnecessary calls */+ /* XXX: might need to use malloc+free to ensure smaller size */+ str = js_realloc_rt(s->ctx->rt, str, sizeof(JSString) ++ (s->len << s->is_wide_char) + 1 - s->is_wide_char);+ if (str == NULL)+ str = s->str;+ s->str = str;+ }+ if (!s->is_wide_char)+ str->u.str8[s->len] = 0;+#ifdef DUMP_LEAKS+ list_add_tail(&str->link, &s->ctx->rt->string_list);+#endif+ str->is_wide_char = s->is_wide_char;+ str->len = s->len;+ s->str = NULL;+ return JS_MKPTR(JS_TAG_STRING, str);+}++/* create a string from a UTF-8 buffer */+JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len)+{+ const uint8_t *p, *p_end, *p_start, *p_next;+ uint32_t c;+ StringBuffer b_s, *b = &b_s;+ size_t len1;+ + p_start = (const uint8_t *)buf;+ p_end = p_start + buf_len;+ p = p_start;+ while (p < p_end && *p < 128)+ p++;+ len1 = p - p_start;+ if (len1 > JS_STRING_LEN_MAX)+ return JS_ThrowInternalError(ctx, "string too long");+ if (p == p_end) {+ /* ASCII string */+ return js_new_string8(ctx, (const uint8_t *)buf, buf_len);+ } else {+ if (string_buffer_init(ctx, b, buf_len))+ goto fail;+ string_buffer_write8(b, p_start, len1);+ while (p < p_end) {+ if (*p < 128) {+ string_buffer_putc8(b, *p++);+ } else {+ /* parse utf-8 sequence, return 0xFFFFFFFF for error */+ c = unicode_from_utf8(p, p_end - p, &p_next);+ if (c < 0x10000) {+ p = p_next;+ } else if (c <= 0x10FFFF) {+ p = p_next;+ /* surrogate pair */+ c -= 0x10000;+ string_buffer_putc16(b, (c >> 10) + 0xd800);+ c = (c & 0x3ff) + 0xdc00;+ } else {+ /* invalid char */+ c = 0xfffd;+ /* skip the invalid chars */+ /* XXX: seems incorrect. Why not just use c = *p++; ? */+ while (p < p_end && (*p >= 0x80 && *p < 0xc0))+ p++;+ if (p < p_end) {+ p++;+ while (p < p_end && (*p >= 0x80 && *p < 0xc0))+ p++;+ }+ }+ string_buffer_putc16(b, c);+ }+ }+ }+ return string_buffer_end(b);++ fail:+ string_buffer_free(b);+ return JS_EXCEPTION;+}++static JSValue JS_ConcatString3(JSContext *ctx, const char *str1,+ JSValue str2, const char *str3)+{+ StringBuffer b_s, *b = &b_s;+ int len1, len3;+ JSString *p;++ if (unlikely(JS_VALUE_GET_TAG(str2) != JS_TAG_STRING)) {+ str2 = JS_ToStringFree(ctx, str2);+ if (JS_IsException(str2))+ goto fail;+ }+ p = JS_VALUE_GET_STRING(str2);+ len1 = strlen(str1);+ len3 = strlen(str3);++ if (string_buffer_init2(ctx, b, len1 + p->len + len3, p->is_wide_char))+ goto fail;++ string_buffer_write8(b, (const uint8_t *)str1, len1);+ string_buffer_concat(b, p, 0, p->len);+ string_buffer_write8(b, (const uint8_t *)str3, len3);++ JS_FreeValue(ctx, str2);+ return string_buffer_end(b);++ fail:+ JS_FreeValue(ctx, str2);+ return JS_EXCEPTION;+}++JSValue JS_NewString(JSContext *ctx, const char *str)+{+ return JS_NewStringLen(ctx, str, strlen(str));+}++JSValue JS_NewAtomString(JSContext *ctx, const char *str)+{+ JSAtom atom = JS_NewAtom(ctx, str);+ if (atom == JS_ATOM_NULL)+ return JS_EXCEPTION;+ JSValue val = JS_AtomToString(ctx, atom);+ JS_FreeAtom(ctx, atom);+ return val;+}++/* return (NULL, 0) if exception. */+/* return pointer into a JSString with a live ref_count */+/* cesu8 determines if non-BMP1 codepoints are encoded as 1 or 2 utf-8 sequences */+const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, BOOL cesu8)+{+ JSValue val;+ JSString *str, *str_new;+ int pos, len, c, c1;+ uint8_t *q;++ if (JS_VALUE_GET_TAG(val1) != JS_TAG_STRING) {+ val = JS_ToString(ctx, val1);+ if (JS_IsException(val))+ goto fail;+ } else {+ val = JS_DupValue(ctx, val1);+ }++ str = JS_VALUE_GET_STRING(val);+ len = str->len;+ if (!str->is_wide_char) {+ const uint8_t *src = str->u.str8;+ int count;++ /* count the number of non-ASCII characters */+ /* Scanning the whole string is required for ASCII strings,+ and computing the number of non-ASCII bytes is less expensive+ than testing each byte, hence this method is faster for ASCII+ strings, which is the most common case.+ */+ count = 0;+ for (pos = 0; pos < len; pos++) {+ count += src[pos] >> 7;+ }+ if (count == 0) {+ if (plen)+ *plen = len;+ return (const char *)src;+ }+ str_new = js_alloc_string(ctx, len + count, 0);+ if (!str_new)+ goto fail;+ q = str_new->u.str8;+ for (pos = 0; pos < len; pos++) {+ c = src[pos];+ if (c < 0x80) {+ *q++ = c;+ } else {+ *q++ = (c >> 6) | 0xc0;+ *q++ = (c & 0x3f) | 0x80;+ }+ }+ } else {+ const uint16_t *src = str->u.str16;+ /* Allocate 3 bytes per 16 bit code point. Surrogate pairs may+ produce 4 bytes but use 2 code points.+ */+ str_new = js_alloc_string(ctx, len * 3, 0);+ if (!str_new)+ goto fail;+ q = str_new->u.str8;+ pos = 0;+ while (pos < len) {+ c = src[pos++];+ if (c < 0x80) {+ *q++ = c;+ } else {+ if (c >= 0xd800 && c < 0xdc00) {+ if (pos < len && !cesu8) {+ c1 = src[pos];+ if (c1 >= 0xdc00 && c1 < 0xe000) {+ pos++;+ /* surrogate pair */+ c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;+ } else {+ /* Keep unmatched surrogate code points */+ /* c = 0xfffd; */ /* error */+ }+ } else {+ /* Keep unmatched surrogate code points */+ /* c = 0xfffd; */ /* error */+ }+ }+ q += unicode_to_utf8(q, c);+ }+ }+ }++ *q = '\0';+ str_new->len = q - str_new->u.str8;+ JS_FreeValue(ctx, val);+ if (plen)+ *plen = str_new->len;+ return (const char *)str_new->u.str8;+ fail:+ if (plen)+ *plen = 0;+ return NULL;+}++void JS_FreeCString(JSContext *ctx, const char *ptr)+{+ JSString *p;+ if (!ptr)+ return;+ /* purposely removing constness */+ p = (JSString *)(void *)(ptr - offsetof(JSString, u));+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, p));+}++static int memcmp16_8(const uint16_t *src1, const uint8_t *src2, int len)+{+ int c, i;+ for(i = 0; i < len; i++) {+ c = src1[i] - src2[i];+ if (c != 0)+ return c;+ }+ return 0;+}++static int memcmp16(const uint16_t *src1, const uint16_t *src2, int len)+{+ int c, i;+ for(i = 0; i < len; i++) {+ c = src1[i] - src2[i];+ if (c != 0)+ return c;+ }+ return 0;+}++static int js_string_memcmp(const JSString *p1, const JSString *p2, int len)+{+ int res;++ if (likely(!p1->is_wide_char)) {+ if (likely(!p2->is_wide_char))+ res = memcmp(p1->u.str8, p2->u.str8, len);+ else+ res = -memcmp16_8(p2->u.str16, p1->u.str8, len);+ } else {+ if (!p2->is_wide_char)+ res = memcmp16_8(p1->u.str16, p2->u.str8, len);+ else+ res = memcmp16(p1->u.str16, p2->u.str16, len);+ }+ return res;+}++/* return < 0, 0 or > 0 */+static int js_string_compare(JSContext *ctx,+ const JSString *p1, const JSString *p2)+{+ int res, len;+ len = min_int(p1->len, p2->len);+ res = js_string_memcmp(p1, p2, len);+ if (res == 0) {+ if (p1->len == p2->len)+ res = 0;+ else if (p1->len < p2->len)+ res = -1;+ else+ res = 1;+ }+ return res;+}++static void copy_str16(uint16_t *dst, const JSString *p, int offset, int len)+{+ if (p->is_wide_char) {+ memcpy(dst, p->u.str16 + offset, len * 2);+ } else {+ const uint8_t *src1 = p->u.str8 + offset;+ int i;++ for(i = 0; i < len; i++)+ dst[i] = src1[i];+ }+}++static JSValue JS_ConcatString1(JSContext *ctx,+ const JSString *p1, const JSString *p2)+{+ JSString *p;+ uint32_t len;+ int is_wide_char;++ len = p1->len + p2->len;+ if (len > JS_STRING_LEN_MAX)+ return JS_ThrowInternalError(ctx, "string too long");+ is_wide_char = p1->is_wide_char | p2->is_wide_char;+ p = js_alloc_string(ctx, len, is_wide_char);+ if (!p)+ return JS_EXCEPTION;+ if (!is_wide_char) {+ memcpy(p->u.str8, p1->u.str8, p1->len);+ memcpy(p->u.str8 + p1->len, p2->u.str8, p2->len);+ p->u.str8[len] = '\0';+ } else {+ copy_str16(p->u.str16, p1, 0, p1->len);+ copy_str16(p->u.str16 + p1->len, p2, 0, p2->len);+ }+ return JS_MKPTR(JS_TAG_STRING, p);+}++/* op1 and op2 are converted to strings. For convience, op1 or op2 =+ JS_EXCEPTION are accepted and return JS_EXCEPTION. */+static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2)+{+ JSValue ret;+ JSString *p1, *p2;++ if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_STRING)) {+ op1 = JS_ToStringFree(ctx, op1);+ if (JS_IsException(op1)) {+ JS_FreeValue(ctx, op2);+ return JS_EXCEPTION;+ }+ }+ if (unlikely(JS_VALUE_GET_TAG(op2) != JS_TAG_STRING)) {+ op2 = JS_ToStringFree(ctx, op2);+ if (JS_IsException(op2)) {+ JS_FreeValue(ctx, op1);+ return JS_EXCEPTION;+ }+ }+ p1 = JS_VALUE_GET_STRING(op1);+ p2 = JS_VALUE_GET_STRING(op2);++ /* XXX: could also check if p1 is empty */+ if (p2->len == 0) {+ goto ret_op1;+ }+ if (p1->header.ref_count == 1 && p1->is_wide_char == p2->is_wide_char+ && js_malloc_usable_size(ctx, p1) >= sizeof(*p1) + ((p1->len + p2->len) << p2->is_wide_char) + 1 - p1->is_wide_char) {+ /* Concatenate in place in available space at the end of p1 */+ if (p1->is_wide_char) {+ memcpy(p1->u.str16 + p1->len, p2->u.str16, p2->len << 1);+ p1->len += p2->len;+ } else {+ memcpy(p1->u.str8 + p1->len, p2->u.str8, p2->len);+ p1->len += p2->len;+ p1->u.str8[p1->len] = '\0';+ }+ ret_op1:+ JS_FreeValue(ctx, op2);+ return op1;+ }+ ret = JS_ConcatString1(ctx, p1, p2);+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ return ret;+}++/* Shape support */++static inline size_t get_shape_size(size_t hash_size, size_t prop_size)+{+ return hash_size * sizeof(uint32_t) + sizeof(JSShape) ++ prop_size * sizeof(JSShapeProperty);+}++static inline JSShape *get_shape_from_alloc(void *sh_alloc, size_t hash_size)+{+ return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size);+}++static inline uint32_t *prop_hash_end(JSShape *sh)+{+ return (uint32_t *)sh;+}++static inline void *get_alloc_from_shape(JSShape *sh)+{+ return prop_hash_end(sh) - ((intptr_t)sh->prop_hash_mask + 1);+}++static inline JSShapeProperty *get_shape_prop(JSShape *sh)+{+ return sh->prop;+}++static int init_shape_hash(JSRuntime *rt)+{+ rt->shape_hash_bits = 4; /* 16 shapes */+ rt->shape_hash_size = 1 << rt->shape_hash_bits;+ rt->shape_hash_count = 0;+ rt->shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) *+ rt->shape_hash_size);+ if (!rt->shape_hash)+ return -1;+ return 0;+}++/* same magic hash multiplier as the Linux kernel */+static uint32_t shape_hash(uint32_t h, uint32_t val)+{+ return (h + val) * 0x9e370001;+}++/* truncate the shape hash to 'hash_bits' bits */+static uint32_t get_shape_hash(uint32_t h, int hash_bits)+{+ return h >> (32 - hash_bits);+}++static uint32_t shape_initial_hash(JSObject *proto)+{+ uint32_t h;+ h = shape_hash(1, (uintptr_t)proto);+ if (sizeof(proto) > 4)+ h = shape_hash(h, (uint64_t)(uintptr_t)proto >> 32);+ return h;+}++static int resize_shape_hash(JSRuntime *rt, int new_shape_hash_bits)+{+ int new_shape_hash_size, i;+ uint32_t h;+ JSShape **new_shape_hash, *sh, *sh_next;++ new_shape_hash_size = 1 << new_shape_hash_bits;+ new_shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) *+ new_shape_hash_size);+ if (!new_shape_hash)+ return -1;+ for(i = 0; i < rt->shape_hash_size; i++) {+ for(sh = rt->shape_hash[i]; sh != NULL; sh = sh_next) {+ sh_next = sh->shape_hash_next;+ h = get_shape_hash(sh->hash, new_shape_hash_bits);+ sh->shape_hash_next = new_shape_hash[h];+ new_shape_hash[h] = sh;+ }+ }+ js_free_rt(rt, rt->shape_hash);+ rt->shape_hash_bits = new_shape_hash_bits;+ rt->shape_hash_size = new_shape_hash_size;+ rt->shape_hash = new_shape_hash;+ return 0;+}++static void js_shape_hash_link(JSRuntime *rt, JSShape *sh)+{+ uint32_t h;+ h = get_shape_hash(sh->hash, rt->shape_hash_bits);+ sh->shape_hash_next = rt->shape_hash[h];+ rt->shape_hash[h] = sh;+ rt->shape_hash_count++;+}++static void js_shape_hash_unlink(JSRuntime *rt, JSShape *sh)+{+ uint32_t h;+ JSShape **psh;++ h = get_shape_hash(sh->hash, rt->shape_hash_bits);+ psh = &rt->shape_hash[h];+ while (*psh != sh)+ psh = &(*psh)->shape_hash_next;+ *psh = sh->shape_hash_next;+ rt->shape_hash_count--;+}++/* create a new empty shape with prototype 'proto' */+static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto,+ int hash_size, int prop_size)+{+ JSRuntime *rt = ctx->rt;+ void *sh_alloc;+ JSShape *sh;++ /* resize the shape hash table if necessary */+ if (2 * (rt->shape_hash_count + 1) > rt->shape_hash_size) {+ resize_shape_hash(rt, rt->shape_hash_bits + 1);+ }++ sh_alloc = js_malloc(ctx, get_shape_size(hash_size, prop_size));+ if (!sh_alloc)+ return NULL;+ sh = get_shape_from_alloc(sh_alloc, hash_size);+ sh->header.ref_count = 1;+ add_gc_object(rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);+ if (proto)+ JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, proto));+ sh->proto = proto;+ memset(prop_hash_end(sh) - hash_size, 0, sizeof(prop_hash_end(sh)[0]) *+ hash_size);+ sh->prop_hash_mask = hash_size - 1;+ sh->prop_size = prop_size;+ sh->prop_count = 0;+ sh->deleted_prop_count = 0;+ + /* insert in the hash table */+ sh->hash = shape_initial_hash(proto);+ sh->is_hashed = TRUE;+ sh->has_small_array_index = FALSE;+ js_shape_hash_link(ctx->rt, sh);+ return sh;+}++static JSShape *js_new_shape(JSContext *ctx, JSObject *proto)+{+ return js_new_shape2(ctx, proto, JS_PROP_INITIAL_HASH_SIZE,+ JS_PROP_INITIAL_SIZE);+}++/* The shape is cloned. The new shape is not inserted in the shape+ hash table */+static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1)+{+ JSShape *sh;+ void *sh_alloc, *sh_alloc1;+ size_t size;+ JSShapeProperty *pr;+ uint32_t i, hash_size;++ hash_size = sh1->prop_hash_mask + 1;+ size = get_shape_size(hash_size, sh1->prop_size);+ sh_alloc = js_malloc(ctx, size);+ if (!sh_alloc)+ return NULL;+ sh_alloc1 = get_alloc_from_shape(sh1);+ memcpy(sh_alloc, sh_alloc1, size);+ sh = get_shape_from_alloc(sh_alloc, hash_size);+ sh->header.ref_count = 1;+ add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);+ sh->is_hashed = FALSE;+ if (sh->proto) {+ JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto));+ }+ for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {+ JS_DupAtom(ctx, pr->atom);+ }+ return sh;+}++static JSShape *js_dup_shape(JSShape *sh)+{+ sh->header.ref_count++;+ return sh;+}++static void js_free_shape0(JSRuntime *rt, JSShape *sh)+{+ uint32_t i;+ JSShapeProperty *pr;++ assert(sh->header.ref_count == 0);+ if (sh->is_hashed)+ js_shape_hash_unlink(rt, sh);+ if (sh->proto != NULL) {+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, sh->proto));+ }+ pr = get_shape_prop(sh);+ for(i = 0; i < sh->prop_count; i++) {+ JS_FreeAtomRT(rt, pr->atom);+ pr++;+ }+ remove_gc_object(&sh->header);+ js_free_rt(rt, get_alloc_from_shape(sh));+}++static void js_free_shape(JSRuntime *rt, JSShape *sh)+{+ if (unlikely(--sh->header.ref_count <= 0)) {+ js_free_shape0(rt, sh);+ }+}++static void js_free_shape_null(JSRuntime *rt, JSShape *sh)+{+ if (sh)+ js_free_shape(rt, sh);+}++/* make space to hold at least 'count' properties */+static no_inline int resize_properties(JSContext *ctx, JSShape **psh,+ JSObject *p, uint32_t count)+{+ JSShape *sh;+ uint32_t new_size, new_hash_size, new_hash_mask, i;+ JSShapeProperty *pr;+ void *sh_alloc;+ intptr_t h;++ sh = *psh;+ new_size = max_int(count, sh->prop_size * 3 / 2);+ /* Reallocate prop array first to avoid crash or size inconsistency+ in case of memory allocation failure */+ if (p) {+ JSProperty *new_prop;+ new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size);+ if (unlikely(!new_prop))+ return -1;+ p->prop = new_prop;+ }+ new_hash_size = sh->prop_hash_mask + 1;+ while (new_hash_size < new_size)+ new_hash_size = 2 * new_hash_size;+ if (new_hash_size != (sh->prop_hash_mask + 1)) {+ JSShape *old_sh;+ /* resize the hash table and the properties */+ old_sh = sh;+ sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));+ if (!sh_alloc)+ return -1;+ sh = get_shape_from_alloc(sh_alloc, new_hash_size);+ list_del(&old_sh->header.link);+ /* copy all the fields and the properties */+ memcpy(sh, old_sh,+ sizeof(JSShape) + sizeof(sh->prop[0]) * old_sh->prop_count);+ list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);+ new_hash_mask = new_hash_size - 1;+ sh->prop_hash_mask = new_hash_mask;+ memset(prop_hash_end(sh) - new_hash_size, 0,+ sizeof(prop_hash_end(sh)[0]) * new_hash_size);+ for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) {+ if (pr->atom != JS_ATOM_NULL) {+ h = ((uintptr_t)pr->atom & new_hash_mask);+ pr->hash_next = prop_hash_end(sh)[-h - 1];+ prop_hash_end(sh)[-h - 1] = i + 1;+ }+ }+ js_free(ctx, get_alloc_from_shape(old_sh));+ } else {+ /* only resize the properties */+ list_del(&sh->header.link);+ sh_alloc = js_realloc(ctx, get_alloc_from_shape(sh),+ get_shape_size(new_hash_size, new_size));+ if (unlikely(!sh_alloc)) {+ /* insert again in the GC list */+ list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);+ return -1;+ }+ sh = get_shape_from_alloc(sh_alloc, new_hash_size);+ list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);+ }+ *psh = sh;+ sh->prop_size = new_size;+ return 0;+}++/* remove the deleted properties. */+static int compact_properties(JSContext *ctx, JSObject *p)+{+ JSShape *sh, *old_sh;+ void *sh_alloc;+ intptr_t h;+ uint32_t new_hash_size, i, j, new_hash_mask, new_size;+ JSShapeProperty *old_pr, *pr;+ JSProperty *prop, *new_prop;+ + sh = p->shape;+ assert(!sh->is_hashed);++ new_size = max_int(JS_PROP_INITIAL_SIZE,+ sh->prop_count - sh->deleted_prop_count);+ assert(new_size <= sh->prop_size);++ new_hash_size = sh->prop_hash_mask + 1;+ while ((new_hash_size / 2) >= new_size)+ new_hash_size = new_hash_size / 2;+ new_hash_mask = new_hash_size - 1;++ /* resize the hash table and the properties */+ old_sh = sh;+ sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));+ if (!sh_alloc)+ return -1;+ sh = get_shape_from_alloc(sh_alloc, new_hash_size);+ list_del(&old_sh->header.link);+ memcpy(sh, old_sh, sizeof(JSShape));+ list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);+ + memset(prop_hash_end(sh) - new_hash_size, 0,+ sizeof(prop_hash_end(sh)[0]) * new_hash_size);++ j = 0;+ old_pr = old_sh->prop;+ pr = sh->prop;+ prop = p->prop;+ for(i = 0; i < sh->prop_count; i++) {+ if (old_pr->atom != JS_ATOM_NULL) {+ pr->atom = old_pr->atom;+ pr->flags = old_pr->flags;+ h = ((uintptr_t)old_pr->atom & new_hash_mask);+ pr->hash_next = prop_hash_end(sh)[-h - 1];+ prop_hash_end(sh)[-h - 1] = j + 1;+ prop[j] = prop[i];+ j++;+ pr++;+ }+ old_pr++;+ }+ assert(j == (sh->prop_count - sh->deleted_prop_count));+ sh->prop_hash_mask = new_hash_mask;+ sh->prop_size = new_size;+ sh->deleted_prop_count = 0;+ sh->prop_count = j;++ p->shape = sh;+ js_free(ctx, get_alloc_from_shape(old_sh));+ + /* reduce the size of the object properties */+ new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size);+ if (new_prop)+ p->prop = new_prop;+ return 0;+}++static int add_shape_property(JSContext *ctx, JSShape **psh,+ JSObject *p, JSAtom atom, int prop_flags)+{+ JSRuntime *rt = ctx->rt;+ JSShape *sh = *psh;+ JSShapeProperty *pr, *prop;+ uint32_t hash_mask, new_shape_hash = 0;+ intptr_t h;++ /* update the shape hash */+ if (sh->is_hashed) {+ js_shape_hash_unlink(rt, sh);+ new_shape_hash = shape_hash(shape_hash(sh->hash, atom), prop_flags);+ }++ if (unlikely(sh->prop_count >= sh->prop_size)) {+ if (resize_properties(ctx, psh, p, sh->prop_count + 1)) {+ /* in case of error, reinsert in the hash table.+ sh is still valid if resize_properties() failed */+ if (sh->is_hashed)+ js_shape_hash_link(rt, sh);+ return -1;+ }+ sh = *psh;+ }+ if (sh->is_hashed) {+ sh->hash = new_shape_hash;+ js_shape_hash_link(rt, sh);+ }+ /* Initialize the new shape property.+ The object property at p->prop[sh->prop_count] is uninitialized */+ prop = get_shape_prop(sh);+ pr = &prop[sh->prop_count++];+ pr->atom = JS_DupAtom(ctx, atom);+ pr->flags = prop_flags;+ sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom);+ /* add in hash table */+ hash_mask = sh->prop_hash_mask;+ h = atom & hash_mask;+ pr->hash_next = prop_hash_end(sh)[-h - 1];+ prop_hash_end(sh)[-h - 1] = sh->prop_count;+ return 0;+}++/* find a hashed empty shape matching the prototype. Return NULL if+ not found */+static JSShape *find_hashed_shape_proto(JSRuntime *rt, JSObject *proto)+{+ JSShape *sh1;+ uint32_t h, h1;++ h = shape_initial_hash(proto);+ h1 = get_shape_hash(h, rt->shape_hash_bits);+ for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) {+ if (sh1->hash == h &&+ sh1->proto == proto &&+ sh1->prop_count == 0) {+ return sh1;+ }+ }+ return NULL;+}++/* find a hashed shape matching sh + (prop, prop_flags). Return NULL if+ not found */+static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh,+ JSAtom atom, int prop_flags)+{+ JSShape *sh1;+ uint32_t h, h1, i, n;++ h = sh->hash;+ h = shape_hash(h, atom);+ h = shape_hash(h, prop_flags);+ h1 = get_shape_hash(h, rt->shape_hash_bits);+ for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) {+ /* we test the hash first so that the rest is done only if the+ shapes really match */+ if (sh1->hash == h &&+ sh1->proto == sh->proto &&+ sh1->prop_count == ((n = sh->prop_count) + 1)) {+ for(i = 0; i < n; i++) {+ if (unlikely(sh1->prop[i].atom != sh->prop[i].atom) ||+ unlikely(sh1->prop[i].flags != sh->prop[i].flags))+ goto next;+ }+ if (unlikely(sh1->prop[n].atom != atom) ||+ unlikely(sh1->prop[n].flags != prop_flags))+ goto next;+ return sh1;+ }+ next: ;+ }+ return NULL;+}++static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh)+{+ char atom_buf[ATOM_GET_STR_BUF_SIZE];+ int j;++ /* XXX: should output readable class prototype */+ printf("%5d %3d%c %14p %5d %5d", i,+ sh->header.ref_count, " *"[sh->is_hashed],+ (void *)sh->proto, sh->prop_size, sh->prop_count);+ for(j = 0; j < sh->prop_count; j++) {+ printf(" %s", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf),+ sh->prop[j].atom));+ }+ printf("\n");+}++static __maybe_unused void JS_DumpShapes(JSRuntime *rt)+{+ int i;+ JSShape *sh;+ struct list_head *el;+ JSObject *p;+ JSGCObjectHeader *gp;+ + printf("JSShapes: {\n");+ printf("%5s %4s %14s %5s %5s %s\n", "SLOT", "REFS", "PROTO", "SIZE", "COUNT", "PROPS");+ for(i = 0; i < rt->shape_hash_size; i++) {+ for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) {+ JS_DumpShape(rt, i, sh);+ assert(sh->is_hashed);+ }+ }+ /* dump non-hashed shapes */+ list_for_each(el, &rt->gc_obj_list) {+ gp = list_entry(el, JSGCObjectHeader, link);+ if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {+ p = (JSObject *)gp;+ if (!p->shape->is_hashed) {+ JS_DumpShape(rt, -1, p->shape);+ }+ }+ }+ printf("}\n");+}++static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID class_id)+{+ JSObject *p;++ js_trigger_gc(ctx->rt, sizeof(JSObject));+ p = js_malloc(ctx, sizeof(JSObject));+ if (unlikely(!p))+ goto fail;+ p->class_id = class_id;+ p->extensible = TRUE;+ p->free_mark = 0;+ p->is_exotic = 0;+ p->fast_array = 0;+ p->is_constructor = 0;+ p->is_uncatchable_error = 0;+ p->tmp_mark = 0;+ p->is_HTMLDDA = 0;+ p->first_weak_ref = NULL;+ p->u.opaque = NULL;+ p->shape = sh;+ p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size);+ if (unlikely(!p->prop)) {+ js_free(ctx, p);+ fail:+ js_free_shape(ctx->rt, sh);+ return JS_EXCEPTION;+ }++ switch(class_id) {+ case JS_CLASS_OBJECT:+ break;+ case JS_CLASS_ARRAY:+ {+ JSProperty *pr;+ p->is_exotic = 1;+ p->fast_array = 1;+ p->u.array.u.values = NULL;+ p->u.array.count = 0;+ p->u.array.u1.size = 0;+ /* the length property is always the first one */+ if (likely(sh == ctx->array_shape)) {+ pr = &p->prop[0];+ } else {+ /* only used for the first array */+ /* cannot fail */+ pr = add_property(ctx, p, JS_ATOM_length,+ JS_PROP_WRITABLE | JS_PROP_LENGTH);+ }+ pr->u.value = JS_NewInt32(ctx, 0);+ }+ break;+ case JS_CLASS_C_FUNCTION:+ p->prop[0].u.value = JS_UNDEFINED;+ break;+ case JS_CLASS_ARGUMENTS:+ case JS_CLASS_UINT8C_ARRAY:+ case JS_CLASS_INT8_ARRAY:+ case JS_CLASS_UINT8_ARRAY:+ case JS_CLASS_INT16_ARRAY:+ case JS_CLASS_UINT16_ARRAY:+ case JS_CLASS_INT32_ARRAY:+ case JS_CLASS_UINT32_ARRAY:+#ifdef CONFIG_BIGNUM+ case JS_CLASS_BIG_INT64_ARRAY:+ case JS_CLASS_BIG_UINT64_ARRAY:+#endif+ case JS_CLASS_FLOAT32_ARRAY:+ case JS_CLASS_FLOAT64_ARRAY:+ p->is_exotic = 1;+ p->fast_array = 1;+ p->u.array.u.ptr = NULL;+ p->u.array.count = 0;+ break;+ case JS_CLASS_DATAVIEW:+ p->u.array.u.ptr = NULL;+ p->u.array.count = 0;+ break;+ case JS_CLASS_NUMBER:+ case JS_CLASS_STRING:+ case JS_CLASS_BOOLEAN:+ case JS_CLASS_SYMBOL:+ case JS_CLASS_DATE:+#ifdef CONFIG_BIGNUM+ case JS_CLASS_BIG_INT:+ case JS_CLASS_BIG_FLOAT:+ case JS_CLASS_BIG_DECIMAL:+#endif+ p->u.object_data = JS_UNDEFINED;+ goto set_exotic;+ case JS_CLASS_REGEXP:+ p->u.regexp.pattern = NULL;+ p->u.regexp.bytecode = NULL;+ goto set_exotic;+ default:+ set_exotic:+ if (ctx->rt->class_array[class_id].exotic) {+ p->is_exotic = 1;+ }+ break;+ }+ p->header.ref_count = 1;+ add_gc_object(ctx->rt, &p->header, JS_GC_OBJ_TYPE_JS_OBJECT);+ return JS_MKPTR(JS_TAG_OBJECT, p);+}++static JSObject *get_proto_obj(JSValueConst proto_val)+{+ if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT)+ return NULL;+ else+ return JS_VALUE_GET_OBJ(proto_val);+}++/* WARNING: proto must be an object or JS_NULL */+JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto_val,+ JSClassID class_id)+{+ JSShape *sh;+ JSObject *proto;++ proto = get_proto_obj(proto_val);+ sh = find_hashed_shape_proto(ctx->rt, proto);+ if (likely(sh)) {+ sh = js_dup_shape(sh);+ } else {+ sh = js_new_shape(ctx, proto);+ if (!sh)+ return JS_EXCEPTION;+ }+ return JS_NewObjectFromShape(ctx, sh, class_id);+}++#if 0+static JSValue JS_GetObjectData(JSContext *ctx, JSValueConst obj)+{+ JSObject *p;++ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {+ p = JS_VALUE_GET_OBJ(obj);+ switch(p->class_id) {+ case JS_CLASS_NUMBER:+ case JS_CLASS_STRING:+ case JS_CLASS_BOOLEAN:+ case JS_CLASS_SYMBOL:+ case JS_CLASS_DATE:+#ifdef CONFIG_BIGNUM+ case JS_CLASS_BIG_INT:+ case JS_CLASS_BIG_FLOAT:+ case JS_CLASS_BIG_DECIMAL:+#endif+ return JS_DupValue(ctx, p->u.object_data);+ }+ }+ return JS_UNDEFINED;+}+#endif++static int JS_SetObjectData(JSContext *ctx, JSValueConst obj, JSValue val)+{+ JSObject *p;++ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {+ p = JS_VALUE_GET_OBJ(obj);+ switch(p->class_id) {+ case JS_CLASS_NUMBER:+ case JS_CLASS_STRING:+ case JS_CLASS_BOOLEAN:+ case JS_CLASS_SYMBOL:+ case JS_CLASS_DATE:+#ifdef CONFIG_BIGNUM+ case JS_CLASS_BIG_INT:+ case JS_CLASS_BIG_FLOAT:+ case JS_CLASS_BIG_DECIMAL:+#endif+ JS_FreeValue(ctx, p->u.object_data);+ p->u.object_data = val;+ return 0;+ }+ }+ JS_FreeValue(ctx, val);+ if (!JS_IsException(obj))+ JS_ThrowTypeError(ctx, "invalid object type");+ return -1;+}++JSValue JS_NewObjectClass(JSContext *ctx, int class_id)+{+ return JS_NewObjectProtoClass(ctx, ctx->class_proto[class_id], class_id);+}++JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto)+{+ return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT);+}++JSValue JS_NewArray(JSContext *ctx)+{+ return JS_NewObjectFromShape(ctx, js_dup_shape(ctx->array_shape),+ JS_CLASS_ARRAY);+}++JSValue JS_NewObject(JSContext *ctx)+{+ /* inline JS_NewObjectClass(ctx, JS_CLASS_OBJECT); */+ return JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_OBJECT);+}++static void js_function_set_properties(JSContext *ctx, JSValueConst func_obj,+ JSAtom name, int len)+{+ /* ES6 feature non compatible with ES5.1: length is configurable */+ JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, JS_NewInt32(ctx, len),+ JS_PROP_CONFIGURABLE);+ JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name,+ JS_AtomToString(ctx, name), JS_PROP_CONFIGURABLE);+}++static BOOL js_class_has_bytecode(JSClassID class_id)+{+ return (class_id == JS_CLASS_BYTECODE_FUNCTION ||+ class_id == JS_CLASS_GENERATOR_FUNCTION ||+ class_id == JS_CLASS_ASYNC_FUNCTION ||+ class_id == JS_CLASS_ASYNC_GENERATOR_FUNCTION);+}++/* return NULL without exception if not a function or no bytecode */+static JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val)+{+ JSObject *p;+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)+ return NULL;+ p = JS_VALUE_GET_OBJ(val);+ if (!js_class_has_bytecode(p->class_id))+ return NULL;+ return p->u.func.function_bytecode;+}++static void js_method_set_home_object(JSContext *ctx, JSValueConst func_obj,+ JSValueConst home_obj)+{+ JSObject *p, *p1;+ JSFunctionBytecode *b;++ if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)+ return;+ p = JS_VALUE_GET_OBJ(func_obj);+ if (!js_class_has_bytecode(p->class_id))+ return;+ b = p->u.func.function_bytecode;+ if (b->need_home_object) {+ p1 = p->u.func.home_object;+ if (p1) {+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));+ }+ if (JS_VALUE_GET_TAG(home_obj) == JS_TAG_OBJECT)+ p1 = JS_VALUE_GET_OBJ(JS_DupValue(ctx, home_obj));+ else+ p1 = NULL;+ p->u.func.home_object = p1;+ }+}++static JSValue js_get_function_name(JSContext *ctx, JSAtom name)+{+ JSValue name_str;++ name_str = JS_AtomToString(ctx, name);+ if (JS_AtomSymbolHasDescription(ctx, name)) {+ name_str = JS_ConcatString3(ctx, "[", name_str, "]");+ }+ return name_str;+}++/* Modify the name of a method according to the atom and+ 'flags'. 'flags' is a bitmask of JS_PROP_HAS_GET and+ JS_PROP_HAS_SET. Also set the home object of the method.+ Return < 0 if exception. */+static int js_method_set_properties(JSContext *ctx, JSValueConst func_obj,+ JSAtom name, int flags, JSValueConst home_obj)+{+ JSValue name_str;++ name_str = js_get_function_name(ctx, name);+ if (flags & JS_PROP_HAS_GET) {+ name_str = JS_ConcatString3(ctx, "get ", name_str, "");+ } else if (flags & JS_PROP_HAS_SET) {+ name_str = JS_ConcatString3(ctx, "set ", name_str, "");+ }+ if (JS_IsException(name_str))+ return -1;+ if (JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name_str,+ JS_PROP_CONFIGURABLE) < 0)+ return -1;+ js_method_set_home_object(ctx, func_obj, home_obj);+ return 0;+}++/* Note: at least 'length' arguments will be readable in 'argv' */+static JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func,+ const char *name,+ int length, JSCFunctionEnum cproto, int magic,+ JSValueConst proto_val)+{+ JSValue func_obj;+ JSObject *p;+ JSAtom name_atom;+ + func_obj = JS_NewObjectProtoClass(ctx, proto_val, JS_CLASS_C_FUNCTION);+ if (JS_IsException(func_obj))+ return func_obj;+ p = JS_VALUE_GET_OBJ(func_obj);+ p->u.cfunc.realm = JS_DupContext(ctx);+ p->u.cfunc.c_function.generic = func;+ p->u.cfunc.length = length;+ p->u.cfunc.cproto = cproto;+ p->u.cfunc.magic = magic;+ p->is_constructor = (cproto == JS_CFUNC_constructor ||+ cproto == JS_CFUNC_constructor_magic ||+ cproto == JS_CFUNC_constructor_or_func ||+ cproto == JS_CFUNC_constructor_or_func_magic);+ if (!name)+ name = "";+ name_atom = JS_NewAtom(ctx, name);+ js_function_set_properties(ctx, func_obj, name_atom, length);+ JS_FreeAtom(ctx, name_atom);+ return func_obj;+}++/* Note: at least 'length' arguments will be readable in 'argv' */+JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func,+ const char *name,+ int length, JSCFunctionEnum cproto, int magic)+{+ return JS_NewCFunction3(ctx, func, name, length, cproto, magic,+ ctx->function_proto);+}++typedef struct JSCFunctionDataRecord {+ JSCFunctionData *func;+ uint8_t length;+ uint8_t data_len;+ uint16_t magic;+ JSValue data[0];+} JSCFunctionDataRecord;++static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val)+{+ JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA);+ int i;++ if (s) {+ for(i = 0; i < s->data_len; i++) {+ JS_FreeValueRT(rt, s->data[i]);+ }+ js_free_rt(rt, s);+ }+}++static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func)+{+ JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA);+ int i;++ if (s) {+ for(i = 0; i < s->data_len; i++) {+ JS_MarkValue(rt, s->data[i], mark_func);+ }+ }+}++static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj,+ JSValueConst this_val,+ int argc, JSValueConst *argv, int flags)+{+ JSCFunctionDataRecord *s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA);+ JSValueConst *arg_buf;+ int i;++ /* XXX: could add the function on the stack for debug */+ if (unlikely(argc < s->length)) {+ arg_buf = alloca(sizeof(arg_buf[0]) * s->length);+ for(i = 0; i < argc; i++)+ arg_buf[i] = argv[i];+ for(i = argc; i < s->length; i++)+ arg_buf[i] = JS_UNDEFINED;+ } else {+ arg_buf = argv;+ }++ return s->func(ctx, this_val, argc, arg_buf, s->magic, s->data);+}++JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func,+ int length, int magic, int data_len,+ JSValueConst *data)+{+ JSCFunctionDataRecord *s;+ JSValue func_obj;+ int i;++ func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,+ JS_CLASS_C_FUNCTION_DATA);+ if (JS_IsException(func_obj))+ return func_obj;+ s = js_malloc(ctx, sizeof(*s) + data_len * sizeof(JSValue));+ if (!s) {+ JS_FreeValue(ctx, func_obj);+ return JS_EXCEPTION;+ }+ s->func = func;+ s->length = length;+ s->data_len = data_len;+ s->magic = magic;+ for(i = 0; i < data_len; i++)+ s->data[i] = JS_DupValue(ctx, data[i]);+ JS_SetOpaque(func_obj, s);+ js_function_set_properties(ctx, func_obj,+ JS_ATOM_empty_string, length);+ return func_obj;+}++static JSContext *js_autoinit_get_realm(JSProperty *pr)+{+ return (JSContext *)(pr->u.init.realm_and_id & ~3);+}++static JSAutoInitIDEnum js_autoinit_get_id(JSProperty *pr)+{+ return pr->u.init.realm_and_id & 3;+}++static void js_autoinit_free(JSRuntime *rt, JSProperty *pr)+{+ JS_FreeContext(js_autoinit_get_realm(pr));+}++static void js_autoinit_mark(JSRuntime *rt, JSProperty *pr,+ JS_MarkFunc *mark_func)+{+ mark_func(rt, &js_autoinit_get_realm(pr)->header);+}++static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags)+{+ if (unlikely(prop_flags & JS_PROP_TMASK)) {+ if ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET) {+ if (pr->u.getset.getter)+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));+ if (pr->u.getset.setter)+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));+ } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_VARREF) {+ free_var_ref(rt, pr->u.var_ref);+ } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {+ js_autoinit_free(rt, pr);+ }+ } else {+ JS_FreeValueRT(rt, pr->u.value);+ }+}++static force_inline JSShapeProperty *find_own_property1(JSObject *p,+ JSAtom atom)+{+ JSShape *sh;+ JSShapeProperty *pr, *prop;+ intptr_t h;+ sh = p->shape;+ h = (uintptr_t)atom & sh->prop_hash_mask;+ h = prop_hash_end(sh)[-h - 1];+ prop = get_shape_prop(sh);+ while (h) {+ pr = &prop[h - 1];+ if (likely(pr->atom == atom)) {+ return pr;+ }+ h = pr->hash_next;+ }+ return NULL;+}++static force_inline JSShapeProperty *find_own_property(JSProperty **ppr,+ JSObject *p,+ JSAtom atom)+{+ JSShape *sh;+ JSShapeProperty *pr, *prop;+ intptr_t h;+ sh = p->shape;+ h = (uintptr_t)atom & sh->prop_hash_mask;+ h = prop_hash_end(sh)[-h - 1];+ prop = get_shape_prop(sh);+ while (h) {+ pr = &prop[h - 1];+ if (likely(pr->atom == atom)) {+ *ppr = &p->prop[h - 1];+ /* the compiler should be able to assume that pr != NULL here */+ return pr;+ }+ h = pr->hash_next;+ }+ *ppr = NULL;+ return NULL;+}++/* indicate that the object may be part of a function prototype cycle */+static void set_cycle_flag(JSContext *ctx, JSValueConst obj)+{+}++static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref)+{+ if (var_ref) {+ assert(var_ref->header.ref_count > 0);+ if (--var_ref->header.ref_count == 0) {+ if (var_ref->is_detached) {+ JS_FreeValueRT(rt, var_ref->value);+ remove_gc_object(&var_ref->header);+ } else {+ list_del(&var_ref->header.link); /* still on the stack */+ }+ js_free_rt(rt, var_ref);+ }+ }+}++static void js_array_finalizer(JSRuntime *rt, JSValue val)+{+ JSObject *p = JS_VALUE_GET_OBJ(val);+ int i;++ for(i = 0; i < p->u.array.count; i++) {+ JS_FreeValueRT(rt, p->u.array.u.values[i]);+ }+ js_free_rt(rt, p->u.array.u.values);+}++static void js_array_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func)+{+ JSObject *p = JS_VALUE_GET_OBJ(val);+ int i;++ for(i = 0; i < p->u.array.count; i++) {+ JS_MarkValue(rt, p->u.array.u.values[i], mark_func);+ }+}++static void js_object_data_finalizer(JSRuntime *rt, JSValue val)+{+ JSObject *p = JS_VALUE_GET_OBJ(val);+ JS_FreeValueRT(rt, p->u.object_data);+ p->u.object_data = JS_UNDEFINED;+}++static void js_object_data_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func)+{+ JSObject *p = JS_VALUE_GET_OBJ(val);+ JS_MarkValue(rt, p->u.object_data, mark_func);+}++static void js_c_function_finalizer(JSRuntime *rt, JSValue val)+{+ JSObject *p = JS_VALUE_GET_OBJ(val);++ if (p->u.cfunc.realm)+ JS_FreeContext(p->u.cfunc.realm);+}++static void js_c_function_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func)+{+ JSObject *p = JS_VALUE_GET_OBJ(val);++ if (p->u.cfunc.realm)+ mark_func(rt, &p->u.cfunc.realm->header);+}++static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val)+{+ JSObject *p1, *p = JS_VALUE_GET_OBJ(val);+ JSFunctionBytecode *b;+ JSVarRef **var_refs;+ int i;++ p1 = p->u.func.home_object;+ if (p1) {+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, p1));+ }+ b = p->u.func.function_bytecode;+ if (b) {+ var_refs = p->u.func.var_refs;+ if (var_refs) {+ for(i = 0; i < b->closure_var_count; i++)+ free_var_ref(rt, var_refs[i]);+ js_free_rt(rt, var_refs);+ }+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b));+ }+}++static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func)+{+ JSObject *p = JS_VALUE_GET_OBJ(val);+ JSVarRef **var_refs = p->u.func.var_refs;+ JSFunctionBytecode *b = p->u.func.function_bytecode;+ int i;++ if (p->u.func.home_object) {+ JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object),+ mark_func);+ }+ if (b) {+ if (var_refs) {+ for(i = 0; i < b->closure_var_count; i++) {+ JSVarRef *var_ref = var_refs[i];+ if (var_ref && var_ref->is_detached) {+ mark_func(rt, &var_ref->header);+ }+ }+ }+ /* must mark the function bytecode because template objects may be+ part of a cycle */+ JS_MarkValue(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b), mark_func);+ }+}++static void js_bound_function_finalizer(JSRuntime *rt, JSValue val)+{+ JSObject *p = JS_VALUE_GET_OBJ(val);+ JSBoundFunction *bf = p->u.bound_function;+ int i;++ JS_FreeValueRT(rt, bf->func_obj);+ JS_FreeValueRT(rt, bf->this_val);+ for(i = 0; i < bf->argc; i++) {+ JS_FreeValueRT(rt, bf->argv[i]);+ }+ js_free_rt(rt, bf);+}++static void js_bound_function_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func)+{+ JSObject *p = JS_VALUE_GET_OBJ(val);+ JSBoundFunction *bf = p->u.bound_function;+ int i;++ JS_MarkValue(rt, bf->func_obj, mark_func);+ JS_MarkValue(rt, bf->this_val, mark_func);+ for(i = 0; i < bf->argc; i++)+ JS_MarkValue(rt, bf->argv[i], mark_func);+}++static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val)+{+ JSObject *p = JS_VALUE_GET_OBJ(val);+ JSForInIterator *it = p->u.for_in_iterator;+ JS_FreeValueRT(rt, it->obj);+ js_free_rt(rt, it);+}++static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func)+{+ JSObject *p = JS_VALUE_GET_OBJ(val);+ JSForInIterator *it = p->u.for_in_iterator;+ JS_MarkValue(rt, it->obj, mark_func);+}++static void free_object(JSRuntime *rt, JSObject *p)+{+ int i;+ JSClassFinalizer *finalizer;+ JSShape *sh;+ JSShapeProperty *pr;++ p->free_mark = 1; /* used to tell the object is invalid when+ freeing cycles */+ /* free all the fields */+ sh = p->shape;+ pr = get_shape_prop(sh);+ for(i = 0; i < sh->prop_count; i++) {+ free_property(rt, &p->prop[i], pr->flags);+ pr++;+ }+ js_free_rt(rt, p->prop);+ /* as an optimization we destroy the shape immediately without+ putting it in gc_zero_ref_count_list */+ js_free_shape(rt, sh);++ /* fail safe */+ p->shape = NULL;+ p->prop = NULL;++ if (unlikely(p->first_weak_ref)) {+ reset_weak_ref(rt, p);+ }++ finalizer = rt->class_array[p->class_id].finalizer;+ if (finalizer)+ (*finalizer)(rt, JS_MKPTR(JS_TAG_OBJECT, p));++ /* fail safe */+ p->class_id = 0;+ p->u.opaque = NULL;+ p->u.func.var_refs = NULL;+ p->u.func.home_object = NULL;++ remove_gc_object(&p->header);+ if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && p->header.ref_count != 0) {+ list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list);+ } else {+ js_free_rt(rt, p);+ }+}++static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp)+{+ switch(gp->gc_obj_type) {+ case JS_GC_OBJ_TYPE_JS_OBJECT:+ free_object(rt, (JSObject *)gp);+ break;+ case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:+ free_function_bytecode(rt, (JSFunctionBytecode *)gp);+ break;+ default:+ abort();+ }+}++static void free_zero_refcount(JSRuntime *rt)+{+ struct list_head *el;+ JSGCObjectHeader *p;+ + rt->gc_phase = JS_GC_PHASE_DECREF;+ for(;;) {+ el = rt->gc_zero_ref_count_list.next;+ if (el == &rt->gc_zero_ref_count_list)+ break;+ p = list_entry(el, JSGCObjectHeader, link);+ assert(p->ref_count == 0);+ free_gc_object(rt, p);+ }+ rt->gc_phase = JS_GC_PHASE_NONE;+}++/* called with the ref_count of 'v' reaches zero. */+void __JS_FreeValueRT(JSRuntime *rt, JSValue v)+{+ uint32_t tag = JS_VALUE_GET_TAG(v);++#ifdef DUMP_FREE+ {+ printf("Freeing ");+ if (tag == JS_TAG_OBJECT) {+ JS_DumpObject(rt, JS_VALUE_GET_OBJ(v));+ } else {+ JS_DumpValueShort(rt, v);+ printf("\n");+ }+ }+#endif++ switch(tag) {+ case JS_TAG_STRING:+ {+ JSString *p = JS_VALUE_GET_STRING(v);+ if (p->atom_type) {+ JS_FreeAtomStruct(rt, p);+ } else {+#ifdef DUMP_LEAKS+ list_del(&p->link);+#endif+ js_free_rt(rt, p);+ }+ }+ break;+ case JS_TAG_OBJECT:+ case JS_TAG_FUNCTION_BYTECODE:+ {+ JSGCObjectHeader *p = JS_VALUE_GET_PTR(v);+ if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) {+ list_del(&p->link);+ list_add(&p->link, &rt->gc_zero_ref_count_list);+ if (rt->gc_phase == JS_GC_PHASE_NONE) {+ free_zero_refcount(rt);+ }+ }+ }+ break;+ case JS_TAG_MODULE:+ abort(); /* never freed here */+ break;+#ifdef CONFIG_BIGNUM+ case JS_TAG_BIG_INT:+ case JS_TAG_BIG_FLOAT:+ {+ JSBigFloat *bf = JS_VALUE_GET_PTR(v);+ bf_delete(&bf->num);+ js_free_rt(rt, bf);+ }+ break;+ case JS_TAG_BIG_DECIMAL:+ {+ JSBigDecimal *bf = JS_VALUE_GET_PTR(v);+ bfdec_delete(&bf->num);+ js_free_rt(rt, bf);+ }+ break;+#endif+ case JS_TAG_SYMBOL:+ {+ JSAtomStruct *p = JS_VALUE_GET_PTR(v);+ JS_FreeAtomStruct(rt, p);+ }+ break;+ default:+ printf("__JS_FreeValue: unknown tag=%d\n", tag);+ abort();+ }+}++void __JS_FreeValue(JSContext *ctx, JSValue v)+{+ __JS_FreeValueRT(ctx->rt, v);+}++/* garbage collection */++static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,+ JSGCObjectTypeEnum type)+{+ h->mark = 0;+ h->gc_obj_type = type;+ list_add_tail(&h->link, &rt->gc_obj_list);+}++static void remove_gc_object(JSGCObjectHeader *h)+{+ list_del(&h->link);+}++void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)+{+ if (JS_VALUE_HAS_REF_COUNT(val)) {+ switch(JS_VALUE_GET_TAG(val)) {+ case JS_TAG_OBJECT:+ case JS_TAG_FUNCTION_BYTECODE:+ mark_func(rt, JS_VALUE_GET_PTR(val));+ break;+ default:+ break;+ }+ }+}++static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp,+ JS_MarkFunc *mark_func)+{+ switch(gp->gc_obj_type) {+ case JS_GC_OBJ_TYPE_JS_OBJECT:+ {+ JSObject *p = (JSObject *)gp;+ JSShapeProperty *prs;+ JSShape *sh;+ int i;+ sh = p->shape;+ mark_func(rt, &sh->header);+ /* mark all the fields */+ prs = get_shape_prop(sh);+ for(i = 0; i < sh->prop_count; i++) {+ JSProperty *pr = &p->prop[i];+ if (prs->atom != JS_ATOM_NULL) {+ if (prs->flags & JS_PROP_TMASK) {+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {+ if (pr->u.getset.getter)+ mark_func(rt, &pr->u.getset.getter->header);+ if (pr->u.getset.setter)+ mark_func(rt, &pr->u.getset.setter->header);+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {+ if (pr->u.var_ref->is_detached) {+ /* Note: the tag does not matter+ provided it is a GC object */+ mark_func(rt, &pr->u.var_ref->header);+ }+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {+ js_autoinit_mark(rt, pr, mark_func);+ }+ } else {+ JS_MarkValue(rt, pr->u.value, mark_func);+ }+ }+ prs++;+ }++ if (p->class_id != JS_CLASS_OBJECT) {+ JSClassGCMark *gc_mark;+ gc_mark = rt->class_array[p->class_id].gc_mark;+ if (gc_mark)+ gc_mark(rt, JS_MKPTR(JS_TAG_OBJECT, p), mark_func);+ }+ }+ break;+ case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:+ /* the template objects can be part of a cycle */+ {+ JSFunctionBytecode *b = (JSFunctionBytecode *)gp;+ int i;+ for(i = 0; i < b->cpool_count; i++) {+ JS_MarkValue(rt, b->cpool[i], mark_func);+ }+ if (b->realm)+ mark_func(rt, &b->realm->header);+ }+ break;+ case JS_GC_OBJ_TYPE_VAR_REF:+ {+ JSVarRef *var_ref = (JSVarRef *)gp;+ /* only detached variable referenced are taken into account */+ assert(var_ref->is_detached);+ JS_MarkValue(rt, *var_ref->pvalue, mark_func);+ }+ break;+ case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:+ {+ JSAsyncFunctionData *s = (JSAsyncFunctionData *)gp;+ if (s->is_active)+ async_func_mark(rt, &s->func_state, mark_func);+ JS_MarkValue(rt, s->resolving_funcs[0], mark_func);+ JS_MarkValue(rt, s->resolving_funcs[1], mark_func);+ }+ break;+ case JS_GC_OBJ_TYPE_SHAPE:+ {+ JSShape *sh = (JSShape *)gp;+ if (sh->proto != NULL) {+ mark_func(rt, &sh->proto->header);+ }+ }+ break;+ case JS_GC_OBJ_TYPE_JS_CONTEXT:+ {+ JSContext *ctx = (JSContext *)gp;+ JS_MarkContext(rt, ctx, mark_func);+ }+ break;+ default:+ abort();+ }+}++static void gc_decref_child(JSRuntime *rt, JSGCObjectHeader *p)+{+ assert(p->ref_count > 0);+ p->ref_count--;+ if (p->ref_count == 0 && p->mark == 1) {+ list_del(&p->link);+ list_add_tail(&p->link, &rt->tmp_obj_list);+ }+}++static void gc_decref(JSRuntime *rt)+{+ struct list_head *el, *el1;+ JSGCObjectHeader *p;+ + init_list_head(&rt->tmp_obj_list);++ /* decrement the refcount of all the children of all the GC+ objects and move the GC objects with zero refcount to+ tmp_obj_list */+ list_for_each_safe(el, el1, &rt->gc_obj_list) {+ p = list_entry(el, JSGCObjectHeader, link);+ assert(p->mark == 0);+ mark_children(rt, p, gc_decref_child);+ p->mark = 1;+ if (p->ref_count == 0) {+ list_del(&p->link);+ list_add_tail(&p->link, &rt->tmp_obj_list);+ }+ }+}++static void gc_scan_incref_child(JSRuntime *rt, JSGCObjectHeader *p)+{+ p->ref_count++;+ if (p->ref_count == 1) {+ /* ref_count was 0: remove from tmp_obj_list and add at the+ end of gc_obj_list */+ list_del(&p->link);+ list_add_tail(&p->link, &rt->gc_obj_list);+ p->mark = 0; /* reset the mark for the next GC call */+ }+}++static void gc_scan_incref_child2(JSRuntime *rt, JSGCObjectHeader *p)+{+ p->ref_count++;+}++static void gc_scan(JSRuntime *rt)+{+ struct list_head *el;+ JSGCObjectHeader *p;++ /* keep the objects with a refcount > 0 and their children. */+ list_for_each(el, &rt->gc_obj_list) {+ p = list_entry(el, JSGCObjectHeader, link);+ assert(p->ref_count > 0);+ p->mark = 0; /* reset the mark for the next GC call */+ mark_children(rt, p, gc_scan_incref_child);+ }+ + /* restore the refcount of the objects to be deleted. */+ list_for_each(el, &rt->tmp_obj_list) {+ p = list_entry(el, JSGCObjectHeader, link);+ mark_children(rt, p, gc_scan_incref_child2);+ }+}++static void gc_free_cycles(JSRuntime *rt)+{+ struct list_head *el, *el1;+ JSGCObjectHeader *p;+#ifdef DUMP_GC_FREE+ BOOL header_done = FALSE;+#endif++ rt->gc_phase = JS_GC_PHASE_REMOVE_CYCLES;++ for(;;) {+ el = rt->tmp_obj_list.next;+ if (el == &rt->tmp_obj_list)+ break;+ p = list_entry(el, JSGCObjectHeader, link);+ /* Only need to free the GC object associated with JS+ values. The rest will be automatically removed because they+ must be referenced by them. */+ switch(p->gc_obj_type) {+ case JS_GC_OBJ_TYPE_JS_OBJECT:+ case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:+#ifdef DUMP_GC_FREE+ if (!header_done) {+ printf("Freeing cycles:\n");+ JS_DumpObjectHeader(rt);+ header_done = TRUE;+ }+ JS_DumpGCObject(rt, p);+#endif+ free_gc_object(rt, p);+ break;+ default:+ list_del(&p->link);+ list_add_tail(&p->link, &rt->gc_zero_ref_count_list);+ break;+ }+ }+ rt->gc_phase = JS_GC_PHASE_NONE;+ + list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) {+ p = list_entry(el, JSGCObjectHeader, link);+ assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT ||+ p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);+ js_free_rt(rt, p);+ }++ init_list_head(&rt->gc_zero_ref_count_list);+}++void JS_RunGC(JSRuntime *rt)+{+ /* decrement the reference of the children of each object. mark =+ 1 after this pass. */+ gc_decref(rt);++ /* keep the GC objects with a non zero refcount and their childs */+ gc_scan(rt);++ /* free the GC objects in a cycle */+ gc_free_cycles(rt);+}++/* Return false if not an object or if the object has already been+ freed (zombie objects are visible in finalizers when freeing+ cycles). */+BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj)+{+ JSObject *p;+ if (!JS_IsObject(obj))+ return FALSE;+ p = JS_VALUE_GET_OBJ(obj);+ return !p->free_mark;+}++/* Compute memory used by various object types */+/* XXX: poor man's approach to handling multiply referenced objects */+typedef struct JSMemoryUsage_helper {+ double memory_used_count;+ double str_count;+ double str_size;+ int64_t js_func_count;+ double js_func_size;+ int64_t js_func_code_size;+ int64_t js_func_pc2line_count;+ int64_t js_func_pc2line_size;+} JSMemoryUsage_helper;++static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp);++static void compute_jsstring_size(JSString *str, JSMemoryUsage_helper *hp)+{+ if (!str->atom_type) { /* atoms are handled separately */+ double s_ref_count = str->header.ref_count;+ hp->str_count += 1 / s_ref_count;+ hp->str_size += ((sizeof(*str) + (str->len << str->is_wide_char) ++ 1 - str->is_wide_char) / s_ref_count);+ }+}++static void compute_bytecode_size(JSFunctionBytecode *b, JSMemoryUsage_helper *hp)+{+ int memory_used_count, js_func_size, i;++ memory_used_count = 0;+ js_func_size = offsetof(JSFunctionBytecode, debug);+ if (b->vardefs) {+ js_func_size += (b->arg_count + b->var_count) * sizeof(*b->vardefs);+ }+ if (b->cpool) {+ js_func_size += b->cpool_count * sizeof(*b->cpool);+ for (i = 0; i < b->cpool_count; i++) {+ JSValueConst val = b->cpool[i];+ compute_value_size(val, hp);+ }+ }+ if (b->closure_var) {+ js_func_size += b->closure_var_count * sizeof(*b->closure_var);+ }+ if (!b->read_only_bytecode && b->byte_code_buf) {+ hp->js_func_code_size += b->byte_code_len;+ }+ if (b->has_debug) {+ js_func_size += sizeof(*b) - offsetof(JSFunctionBytecode, debug);+ if (b->debug.source) {+ memory_used_count++;+ js_func_size += b->debug.source_len + 1;+ }+ if (b->debug.pc2line_len) {+ memory_used_count++;+ hp->js_func_pc2line_count += 1;+ hp->js_func_pc2line_size += b->debug.pc2line_len;+ }+ }+ hp->js_func_size += js_func_size;+ hp->js_func_count += 1;+ hp->memory_used_count += memory_used_count;+}++static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp)+{+ switch(JS_VALUE_GET_TAG(val)) {+ case JS_TAG_STRING:+ compute_jsstring_size(JS_VALUE_GET_STRING(val), hp);+ break;+#ifdef CONFIG_BIGNUM+ case JS_TAG_BIG_INT:+ case JS_TAG_BIG_FLOAT:+ case JS_TAG_BIG_DECIMAL:+ /* should track JSBigFloat usage */+ break;+#endif+ }+}++void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s)+{+ struct list_head *el, *el1;+ int i;+ JSMemoryUsage_helper mem = { 0 }, *hp = &mem;++ memset(s, 0, sizeof(*s));+ s->malloc_count = rt->malloc_state.malloc_count;+ s->malloc_size = rt->malloc_state.malloc_size;+ s->malloc_limit = rt->malloc_state.malloc_limit;++ s->memory_used_count = 2; /* rt + rt->class_array */+ s->memory_used_size = sizeof(JSRuntime) + sizeof(JSValue) * rt->class_count;++ list_for_each(el, &rt->context_list) {+ JSContext *ctx = list_entry(el, JSContext, link);+ JSShape *sh = ctx->array_shape;+ s->memory_used_count += 2; /* ctx + ctx->class_proto */+ s->memory_used_size += sizeof(JSContext) ++ sizeof(JSValue) * rt->class_count;+ s->binary_object_count += ctx->binary_object_count;+ s->binary_object_size += ctx->binary_object_size;++ /* the hashed shapes are counted separately */+ if (sh && !sh->is_hashed) {+ int hash_size = sh->prop_hash_mask + 1;+ s->shape_count++;+ s->shape_size += get_shape_size(hash_size, sh->prop_size);+ }+ list_for_each(el1, &ctx->loaded_modules) {+ JSModuleDef *m = list_entry(el1, JSModuleDef, link);+ s->memory_used_count += 1;+ s->memory_used_size += sizeof(*m);+ if (m->req_module_entries) {+ s->memory_used_count += 1;+ s->memory_used_size += m->req_module_entries_count * sizeof(*m->req_module_entries);+ }+ if (m->export_entries) {+ s->memory_used_count += 1;+ s->memory_used_size += m->export_entries_count * sizeof(*m->export_entries);+ for (i = 0; i < m->export_entries_count; i++) {+ JSExportEntry *me = &m->export_entries[i];+ if (me->export_type == JS_EXPORT_TYPE_LOCAL && me->u.local.var_ref) {+ /* potential multiple count */+ s->memory_used_count += 1;+ compute_value_size(me->u.local.var_ref->value, hp);+ }+ }+ }+ if (m->star_export_entries) {+ s->memory_used_count += 1;+ s->memory_used_size += m->star_export_entries_count * sizeof(*m->star_export_entries);+ }+ if (m->import_entries) {+ s->memory_used_count += 1;+ s->memory_used_size += m->import_entries_count * sizeof(*m->import_entries);+ }+ compute_value_size(m->module_ns, hp);+ compute_value_size(m->func_obj, hp);+ }+ }++ list_for_each(el, &rt->gc_obj_list) {+ JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link);+ JSObject *p;+ JSShape *sh;+ JSShapeProperty *prs;++ /* XXX: could count the other GC object types too */+ if (gp->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) {+ compute_bytecode_size((JSFunctionBytecode *)gp, hp);+ continue;+ } else if (gp->gc_obj_type != JS_GC_OBJ_TYPE_JS_OBJECT) {+ continue;+ }+ p = (JSObject *)gp;+ sh = p->shape;+ s->obj_count++;+ if (p->prop) {+ s->memory_used_count++;+ s->prop_size += sh->prop_size * sizeof(*p->prop);+ s->prop_count += sh->prop_count;+ prs = get_shape_prop(sh);+ for(i = 0; i < sh->prop_count; i++) {+ JSProperty *pr = &p->prop[i];+ if (prs->atom != JS_ATOM_NULL && !(prs->flags & JS_PROP_TMASK)) {+ compute_value_size(pr->u.value, hp);+ }+ prs++;+ }+ }+ /* the hashed shapes are counted separately */+ if (!sh->is_hashed) {+ int hash_size = sh->prop_hash_mask + 1;+ s->shape_count++;+ s->shape_size += get_shape_size(hash_size, sh->prop_size);+ }++ switch(p->class_id) {+ case JS_CLASS_ARRAY: /* u.array | length */+ case JS_CLASS_ARGUMENTS: /* u.array | length */+ s->array_count++;+ if (p->fast_array) {+ s->fast_array_count++;+ if (p->u.array.u.values) {+ s->memory_used_count++;+ s->memory_used_size += p->u.array.count *+ sizeof(*p->u.array.u.values);+ s->fast_array_elements += p->u.array.count;+ for (i = 0; i < p->u.array.count; i++) {+ compute_value_size(p->u.array.u.values[i], hp);+ }+ }+ }+ break;+ case JS_CLASS_NUMBER: /* u.object_data */+ case JS_CLASS_STRING: /* u.object_data */+ case JS_CLASS_BOOLEAN: /* u.object_data */+ case JS_CLASS_SYMBOL: /* u.object_data */+ case JS_CLASS_DATE: /* u.object_data */+#ifdef CONFIG_BIGNUM+ case JS_CLASS_BIG_INT: /* u.object_data */+ case JS_CLASS_BIG_FLOAT: /* u.object_data */+ case JS_CLASS_BIG_DECIMAL: /* u.object_data */+#endif+ compute_value_size(p->u.object_data, hp);+ break;+ case JS_CLASS_C_FUNCTION: /* u.cfunc */+ s->c_func_count++;+ break;+ case JS_CLASS_BYTECODE_FUNCTION: /* u.func */+ {+ JSFunctionBytecode *b = p->u.func.function_bytecode;+ JSVarRef **var_refs = p->u.func.var_refs;+ /* home_object: object will be accounted for in list scan */+ if (var_refs) {+ s->memory_used_count++;+ s->js_func_size += b->closure_var_count * sizeof(*var_refs);+ for (i = 0; i < b->closure_var_count; i++) {+ if (var_refs[i]) {+ double ref_count = var_refs[i]->header.ref_count;+ s->memory_used_count += 1 / ref_count;+ s->js_func_size += sizeof(*var_refs[i]) / ref_count;+ /* handle non object closed values */+ if (var_refs[i]->pvalue == &var_refs[i]->value) {+ /* potential multiple count */+ compute_value_size(var_refs[i]->value, hp);+ }+ }+ }+ }+ }+ break;+ case JS_CLASS_BOUND_FUNCTION: /* u.bound_function */+ {+ JSBoundFunction *bf = p->u.bound_function;+ /* func_obj and this_val are objects */+ for (i = 0; i < bf->argc; i++) {+ compute_value_size(bf->argv[i], hp);+ }+ s->memory_used_count += 1;+ s->memory_used_size += sizeof(*bf) + bf->argc * sizeof(*bf->argv);+ }+ break;+ case JS_CLASS_C_FUNCTION_DATA: /* u.c_function_data_record */+ {+ JSCFunctionDataRecord *fd = p->u.c_function_data_record;+ if (fd) {+ for (i = 0; i < fd->data_len; i++) {+ compute_value_size(fd->data[i], hp);+ }+ s->memory_used_count += 1;+ s->memory_used_size += sizeof(*fd) + fd->data_len * sizeof(*fd->data);+ }+ }+ break;+ case JS_CLASS_REGEXP: /* u.regexp */+ compute_jsstring_size(p->u.regexp.pattern, hp);+ compute_jsstring_size(p->u.regexp.bytecode, hp);+ break;++ case JS_CLASS_FOR_IN_ITERATOR: /* u.for_in_iterator */+ {+ JSForInIterator *it = p->u.for_in_iterator;+ if (it) {+ compute_value_size(it->obj, hp);+ s->memory_used_count += 1;+ s->memory_used_size += sizeof(*it);+ }+ }+ break;+ case JS_CLASS_ARRAY_BUFFER: /* u.array_buffer */+ case JS_CLASS_SHARED_ARRAY_BUFFER: /* u.array_buffer */+ {+ JSArrayBuffer *abuf = p->u.array_buffer;+ if (abuf) {+ s->memory_used_count += 1;+ s->memory_used_size += sizeof(*abuf);+ if (abuf->data) {+ s->memory_used_count += 1;+ s->memory_used_size += abuf->byte_length;+ }+ }+ }+ break;+ case JS_CLASS_GENERATOR: /* u.generator_data */+ case JS_CLASS_UINT8C_ARRAY: /* u.typed_array / u.array */+ case JS_CLASS_INT8_ARRAY: /* u.typed_array / u.array */+ case JS_CLASS_UINT8_ARRAY: /* u.typed_array / u.array */+ case JS_CLASS_INT16_ARRAY: /* u.typed_array / u.array */+ case JS_CLASS_UINT16_ARRAY: /* u.typed_array / u.array */+ case JS_CLASS_INT32_ARRAY: /* u.typed_array / u.array */+ case JS_CLASS_UINT32_ARRAY: /* u.typed_array / u.array */+#ifdef CONFIG_BIGNUM+ case JS_CLASS_BIG_INT64_ARRAY: /* u.typed_array / u.array */+ case JS_CLASS_BIG_UINT64_ARRAY: /* u.typed_array / u.array */+#endif+ case JS_CLASS_FLOAT32_ARRAY: /* u.typed_array / u.array */+ case JS_CLASS_FLOAT64_ARRAY: /* u.typed_array / u.array */+ case JS_CLASS_DATAVIEW: /* u.typed_array */+#ifdef CONFIG_BIGNUM+ case JS_CLASS_FLOAT_ENV: /* u.float_env */+#endif+ case JS_CLASS_MAP: /* u.map_state */+ case JS_CLASS_SET: /* u.map_state */+ case JS_CLASS_WEAKMAP: /* u.map_state */+ case JS_CLASS_WEAKSET: /* u.map_state */+ case JS_CLASS_MAP_ITERATOR: /* u.map_iterator_data */+ case JS_CLASS_SET_ITERATOR: /* u.map_iterator_data */+ case JS_CLASS_ARRAY_ITERATOR: /* u.array_iterator_data */+ case JS_CLASS_STRING_ITERATOR: /* u.array_iterator_data */+ case JS_CLASS_PROXY: /* u.proxy_data */+ case JS_CLASS_PROMISE: /* u.promise_data */+ case JS_CLASS_PROMISE_RESOLVE_FUNCTION: /* u.promise_function_data */+ case JS_CLASS_PROMISE_REJECT_FUNCTION: /* u.promise_function_data */+ case JS_CLASS_ASYNC_FUNCTION_RESOLVE: /* u.async_function_data */+ case JS_CLASS_ASYNC_FUNCTION_REJECT: /* u.async_function_data */+ case JS_CLASS_ASYNC_FROM_SYNC_ITERATOR: /* u.async_from_sync_iterator_data */+ case JS_CLASS_ASYNC_GENERATOR: /* u.async_generator_data */+ /* TODO */+ default:+ /* XXX: class definition should have an opaque block size */+ if (p->u.opaque) {+ s->memory_used_count += 1;+ }+ break;+ }+ }+ s->obj_size += s->obj_count * sizeof(JSObject);++ /* hashed shapes */+ s->memory_used_count++; /* rt->shape_hash */+ s->memory_used_size += sizeof(rt->shape_hash[0]) * rt->shape_hash_size;+ for(i = 0; i < rt->shape_hash_size; i++) {+ JSShape *sh;+ for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) {+ int hash_size = sh->prop_hash_mask + 1;+ s->shape_count++;+ s->shape_size += get_shape_size(hash_size, sh->prop_size);+ }+ }++ /* atoms */+ s->memory_used_count += 2; /* rt->atom_array, rt->atom_hash */+ s->atom_count = rt->atom_count;+ s->atom_size = sizeof(rt->atom_array[0]) * rt->atom_size ++ sizeof(rt->atom_hash[0]) * rt->atom_hash_size;+ for(i = 0; i < rt->atom_size; i++) {+ JSAtomStruct *p = rt->atom_array[i];+ if (!atom_is_free(p)) {+ s->atom_size += (sizeof(*p) + (p->len << p->is_wide_char) ++ 1 - p->is_wide_char);+ }+ }+ s->str_count = round(mem.str_count);+ s->str_size = round(mem.str_size);+ s->js_func_count = mem.js_func_count;+ s->js_func_size = round(mem.js_func_size);+ s->js_func_code_size = mem.js_func_code_size;+ s->js_func_pc2line_count = mem.js_func_pc2line_count;+ s->js_func_pc2line_size = mem.js_func_pc2line_size;+ s->memory_used_count += round(mem.memory_used_count) ++ s->atom_count + s->str_count ++ s->obj_count + s->shape_count ++ s->js_func_count + s->js_func_pc2line_count;+ s->memory_used_size += s->atom_size + s->str_size ++ s->obj_size + s->prop_size + s->shape_size ++ s->js_func_size + s->js_func_code_size + s->js_func_pc2line_size;+}++void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt)+{+ fprintf(fp, "QuickJS memory usage -- "+#ifdef CONFIG_BIGNUM+ "BigNum "+#endif+ CONFIG_VERSION " version, %d-bit, malloc limit: %"PRId64"\n\n",+ (int)sizeof(void *) * 8, (int64_t)(ssize_t)s->malloc_limit);+#if 1+ if (rt) {+ static const struct {+ const char *name;+ size_t size;+ } object_types[] = {+ { "JSRuntime", sizeof(JSRuntime) },+ { "JSContext", sizeof(JSContext) },+ { "JSObject", sizeof(JSObject) },+ { "JSString", sizeof(JSString) },+ { "JSFunctionBytecode", sizeof(JSFunctionBytecode) },+ };+ int i, usage_size_ok = 0;+ for(i = 0; i < countof(object_types); i++) {+ unsigned int size = object_types[i].size;+ void *p = js_malloc_rt(rt, size);+ if (p) {+ unsigned int size1 = js_malloc_usable_size_rt(rt, p);+ if (size1 >= size) {+ usage_size_ok = 1;+ fprintf(fp, " %3u + %-2u %s\n",+ size, size1 - size, object_types[i].name);+ }+ js_free_rt(rt, p);+ }+ }+ if (!usage_size_ok) {+ fprintf(fp, " malloc_usable_size unavailable\n");+ }+ {+ int obj_classes[JS_CLASS_INIT_COUNT + 1] = { 0 };+ int class_id;+ struct list_head *el;+ list_for_each(el, &rt->gc_obj_list) {+ JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link);+ JSObject *p;+ if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {+ p = (JSObject *)gp;+ obj_classes[min_uint32(p->class_id, JS_CLASS_INIT_COUNT)]++;+ }+ }+ fprintf(fp, "\n" "JSObject classes\n");+ if (obj_classes[0])+ fprintf(fp, " %5d %2.0d %s\n", obj_classes[0], 0, "none");+ for (class_id = 1; class_id < JS_CLASS_INIT_COUNT; class_id++) {+ if (obj_classes[class_id]) {+ char buf[ATOM_GET_STR_BUF_SIZE];+ fprintf(fp, " %5d %2.0d %s\n", obj_classes[class_id], class_id,+ JS_AtomGetStrRT(rt, buf, sizeof(buf), js_std_class_def[class_id - 1].class_name));+ }+ }+ if (obj_classes[JS_CLASS_INIT_COUNT])+ fprintf(fp, " %5d %2.0d %s\n", obj_classes[JS_CLASS_INIT_COUNT], 0, "other");+ }+ fprintf(fp, "\n");+ }+#endif+ fprintf(fp, "%-20s %8s %8s\n", "NAME", "COUNT", "SIZE");++ if (s->malloc_count) {+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per block)\n",+ "memory allocated", s->malloc_count, s->malloc_size,+ (double)s->malloc_size / s->malloc_count);+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%d overhead, %0.1f average slack)\n",+ "memory used", s->memory_used_count, s->memory_used_size,+ MALLOC_OVERHEAD, ((double)(s->malloc_size - s->memory_used_size) /+ s->memory_used_count));+ }+ if (s->atom_count) {+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per atom)\n",+ "atoms", s->atom_count, s->atom_size,+ (double)s->atom_size / s->atom_count);+ }+ if (s->str_count) {+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per string)\n",+ "strings", s->str_count, s->str_size,+ (double)s->str_size / s->str_count);+ }+ if (s->obj_count) {+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n",+ "objects", s->obj_count, s->obj_size,+ (double)s->obj_size / s->obj_count);+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n",+ " properties", s->prop_count, s->prop_size,+ (double)s->prop_count / s->obj_count);+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per shape)\n",+ " shapes", s->shape_count, s->shape_size,+ (double)s->shape_size / s->shape_count);+ }+ if (s->js_func_count) {+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n",+ "bytecode functions", s->js_func_count, s->js_func_size);+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n",+ " bytecode", s->js_func_count, s->js_func_code_size,+ (double)s->js_func_code_size / s->js_func_count);+ if (s->js_func_pc2line_count) {+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n",+ " pc2line", s->js_func_pc2line_count,+ s->js_func_pc2line_size,+ (double)s->js_func_pc2line_size / s->js_func_pc2line_count);+ }+ }+ if (s->c_func_count) {+ fprintf(fp, "%-20s %8"PRId64"\n", "C functions", s->c_func_count);+ }+ if (s->array_count) {+ fprintf(fp, "%-20s %8"PRId64"\n", "arrays", s->array_count);+ if (s->fast_array_count) {+ fprintf(fp, "%-20s %8"PRId64"\n", " fast arrays", s->fast_array_count);+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per fast array)\n",+ " elements", s->fast_array_elements,+ s->fast_array_elements * (int)sizeof(JSValue),+ (double)s->fast_array_elements / s->fast_array_count);+ }+ }+ if (s->binary_object_count) {+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n",+ "binary objects", s->binary_object_count, s->binary_object_size);+ }+}++JSValue JS_GetGlobalObject(JSContext *ctx)+{+ return JS_DupValue(ctx, ctx->global_obj);+}++/* WARNING: obj is freed */+JSValue JS_Throw(JSContext *ctx, JSValue obj)+{+ JSRuntime *rt = ctx->rt;+ JS_FreeValue(ctx, rt->current_exception);+ rt->current_exception = obj;+ return JS_EXCEPTION;+}++/* return the pending exception (cannot be called twice). */+JSValue JS_GetException(JSContext *ctx)+{+ JSValue val;+ JSRuntime *rt = ctx->rt;+ val = rt->current_exception;+ rt->current_exception = JS_NULL;+ return val;+}++static void dbuf_put_leb128(DynBuf *s, uint32_t v)+{+ uint32_t a;+ for(;;) {+ a = v & 0x7f;+ v >>= 7;+ if (v != 0) {+ dbuf_putc(s, a | 0x80);+ } else {+ dbuf_putc(s, a);+ break;+ }+ }+}++static void dbuf_put_sleb128(DynBuf *s, int32_t v1)+{+ uint32_t v = v1;+ dbuf_put_leb128(s, (2 * v) ^ -(v >> 31));+}++static int get_leb128(uint32_t *pval, const uint8_t *buf,+ const uint8_t *buf_end)+{+ const uint8_t *ptr = buf;+ uint32_t v, a, i;+ v = 0;+ for(i = 0; i < 5; i++) {+ if (unlikely(ptr >= buf_end))+ break;+ a = *ptr++;+ v |= (a & 0x7f) << (i * 7);+ if (!(a & 0x80)) {+ *pval = v;+ return ptr - buf;+ }+ }+ *pval = 0;+ return -1;+}++static int get_sleb128(int32_t *pval, const uint8_t *buf,+ const uint8_t *buf_end)+{+ int ret;+ uint32_t val;+ ret = get_leb128(&val, buf, buf_end);+ if (ret < 0) {+ *pval = 0;+ return -1;+ }+ *pval = (val >> 1) ^ -(val & 1);+ return ret;+}++static int find_line_num(JSContext *ctx, JSFunctionBytecode *b,+ uint32_t pc_value)+{+ const uint8_t *p_end, *p;+ int new_line_num, line_num, pc, v, ret;+ unsigned int op;++ if (!b->has_debug || !b->debug.pc2line_buf) {+ /* function was stripped */+ return -1;+ }++ p = b->debug.pc2line_buf;+ p_end = p + b->debug.pc2line_len;+ pc = 0;+ line_num = b->debug.line_num;+ while (p < p_end) {+ op = *p++;+ if (op == 0) {+ uint32_t val;+ ret = get_leb128(&val, p, p_end);+ if (ret < 0)+ goto fail;+ pc += val;+ p += ret;+ ret = get_sleb128(&v, p, p_end);+ if (ret < 0) {+ fail:+ /* should never happen */+ return b->debug.line_num;+ }+ p += ret;+ new_line_num = line_num + v;+ } else {+ op -= PC2LINE_OP_FIRST;+ pc += (op / PC2LINE_RANGE);+ new_line_num = line_num + (op % PC2LINE_RANGE) + PC2LINE_BASE;+ }+ if (pc_value < pc)+ return line_num;+ line_num = new_line_num;+ }+ return line_num;+}++/* in order to avoid executing arbitrary code during the stack trace+ generation, we only look at simple 'name' properties containing a+ string. */+static const char *get_func_name(JSContext *ctx, JSValueConst func)+{+ JSProperty *pr;+ JSShapeProperty *prs;+ JSValueConst val;+ + if (JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)+ return NULL;+ prs = find_own_property(&pr, JS_VALUE_GET_OBJ(func), JS_ATOM_name);+ if (!prs)+ return NULL;+ if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL)+ return NULL;+ val = pr->u.value;+ if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)+ return NULL;+ return JS_ToCString(ctx, val);+}++#define JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL (1 << 0)+/* only taken into account if filename is provided */+#define JS_BACKTRACE_FLAG_SINGLE_LEVEL (1 << 1)++/* if filename != NULL, an additional level is added with the filename+ and line number information (used for parse error). */+static void build_backtrace(JSContext *ctx, JSValueConst error_obj,+ const char *filename, int line_num,+ int backtrace_flags)+{+ JSStackFrame *sf;+ JSValue str;+ DynBuf dbuf;+ const char *func_name_str;+ const char *str1;+ JSObject *p;+ BOOL backtrace_barrier;+ + js_dbuf_init(ctx, &dbuf);+ if (filename) {+ dbuf_printf(&dbuf, " at %s", filename);+ if (line_num != -1)+ dbuf_printf(&dbuf, ":%d", line_num);+ dbuf_putc(&dbuf, '\n');+ str = JS_NewString(ctx, filename);+ JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_fileName, str,+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+ JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_lineNumber, JS_NewInt32(ctx, line_num),+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+ if (backtrace_flags & JS_BACKTRACE_FLAG_SINGLE_LEVEL)+ goto done;+ }+ for(sf = ctx->rt->current_stack_frame; sf != NULL; sf = sf->prev_frame) {+ if (backtrace_flags & JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL) {+ backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL;+ continue;+ }+ func_name_str = get_func_name(ctx, sf->cur_func);+ if (!func_name_str || func_name_str[0] == '\0')+ str1 = "<anonymous>";+ else+ str1 = func_name_str;+ dbuf_printf(&dbuf, " at %s", str1);+ JS_FreeCString(ctx, func_name_str);++ p = JS_VALUE_GET_OBJ(sf->cur_func);+ backtrace_barrier = FALSE;+ if (js_class_has_bytecode(p->class_id)) {+ JSFunctionBytecode *b;+ const char *atom_str;+ int line_num1;++ b = p->u.func.function_bytecode;+ backtrace_barrier = b->backtrace_barrier;+ if (b->has_debug) {+ line_num1 = find_line_num(ctx, b,+ sf->cur_pc - b->byte_code_buf - 1);+ atom_str = JS_AtomToCString(ctx, b->debug.filename);+ dbuf_printf(&dbuf, " (%s",+ atom_str ? atom_str : "<null>");+ JS_FreeCString(ctx, atom_str);+ if (line_num1 != -1)+ dbuf_printf(&dbuf, ":%d", line_num1);+ dbuf_putc(&dbuf, ')');+ }+ } else {+ dbuf_printf(&dbuf, " (native)");+ }+ dbuf_putc(&dbuf, '\n');+ /* stop backtrace if JS_EVAL_FLAG_BACKTRACE_BARRIER was used */+ if (backtrace_barrier)+ break;+ }+ done:+ dbuf_putc(&dbuf, '\0');+ if (dbuf_error(&dbuf))+ str = JS_NULL;+ else+ str = JS_NewString(ctx, (char *)dbuf.buf);+ dbuf_free(&dbuf);+ JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, str,+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+}++/* Note: it is important that no exception is returned by this function */+static BOOL is_backtrace_needed(JSContext *ctx, JSValueConst obj)+{+ JSObject *p;+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)+ return FALSE;+ p = JS_VALUE_GET_OBJ(obj);+ if (p->class_id != JS_CLASS_ERROR)+ return FALSE;+ if (find_own_property1(p, JS_ATOM_stack))+ return FALSE;+ return TRUE;+}++JSValue JS_NewError(JSContext *ctx)+{+ return JS_NewObjectClass(ctx, JS_CLASS_ERROR);+}++static JSValue JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num,+ const char *fmt, va_list ap, BOOL add_backtrace)+{+ char buf[256];+ JSValue obj, ret;++ vsnprintf(buf, sizeof(buf), fmt, ap);+ obj = JS_NewObjectProtoClass(ctx, ctx->native_error_proto[error_num],+ JS_CLASS_ERROR);+ if (unlikely(JS_IsException(obj))) {+ /* out of memory: throw JS_NULL to avoid recursing */+ obj = JS_NULL;+ } else {+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_message,+ JS_NewString(ctx, buf),+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+ }+ if (add_backtrace) {+ build_backtrace(ctx, obj, NULL, 0, 0);+ }+ ret = JS_Throw(ctx, obj);+ return ret;+}++static JSValue JS_ThrowError(JSContext *ctx, JSErrorEnum error_num,+ const char *fmt, va_list ap)+{+ JSRuntime *rt = ctx->rt;+ JSStackFrame *sf;+ BOOL add_backtrace;++ /* the backtrace is added later if called from a bytecode function */+ sf = rt->current_stack_frame;+ add_backtrace = !rt->in_out_of_memory &&+ (!sf || (JS_GetFunctionBytecode(sf->cur_func) == NULL));+ return JS_ThrowError2(ctx, error_num, fmt, ap, add_backtrace);+}++JSValue __attribute__((format(printf, 2, 3))) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...)+{+ JSValue val;+ va_list ap;++ va_start(ap, fmt);+ val = JS_ThrowError(ctx, JS_SYNTAX_ERROR, fmt, ap);+ va_end(ap);+ return val;+}++JSValue __attribute__((format(printf, 2, 3))) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...)+{+ JSValue val;+ va_list ap;++ va_start(ap, fmt);+ val = JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap);+ va_end(ap);+ return val;+}++static int __attribute__((format(printf, 3, 4))) JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, const char *fmt, ...)+{+ va_list ap;++ if ((flags & JS_PROP_THROW) ||+ ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {+ va_start(ap, fmt);+ JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap);+ va_end(ap);+ return -1;+ } else {+ return FALSE;+ }+}++/* never use it directly */+static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowTypeErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...)+{+ char buf[ATOM_GET_STR_BUF_SIZE];+ return JS_ThrowTypeError(ctx, fmt,+ JS_AtomGetStr(ctx, buf, sizeof(buf), atom));+}++/* never use it directly */+static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowSyntaxErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...)+{+ char buf[ATOM_GET_STR_BUF_SIZE];+ return JS_ThrowSyntaxError(ctx, fmt,+ JS_AtomGetStr(ctx, buf, sizeof(buf), atom));+}++/* %s is replaced by 'atom'. The macro is used so that gcc can check+ the format string. */+#define JS_ThrowTypeErrorAtom(ctx, fmt, atom) __JS_ThrowTypeErrorAtom(ctx, atom, fmt, "")+#define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) __JS_ThrowSyntaxErrorAtom(ctx, atom, fmt, "")++static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom)+{+ if ((flags & JS_PROP_THROW) ||+ ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {+ JS_ThrowTypeErrorAtom(ctx, "'%s' is read-only", atom);+ return -1;+ } else {+ return FALSE;+ }+}++JSValue __attribute__((format(printf, 2, 3))) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...)+{+ JSValue val;+ va_list ap;++ va_start(ap, fmt);+ val = JS_ThrowError(ctx, JS_REFERENCE_ERROR, fmt, ap);+ va_end(ap);+ return val;+}++JSValue __attribute__((format(printf, 2, 3))) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...)+{+ JSValue val;+ va_list ap;++ va_start(ap, fmt);+ val = JS_ThrowError(ctx, JS_RANGE_ERROR, fmt, ap);+ va_end(ap);+ return val;+}++JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...)+{+ JSValue val;+ va_list ap;++ va_start(ap, fmt);+ val = JS_ThrowError(ctx, JS_INTERNAL_ERROR, fmt, ap);+ va_end(ap);+ return val;+}++JSValue JS_ThrowOutOfMemory(JSContext *ctx)+{+ JSRuntime *rt = ctx->rt;+ if (!rt->in_out_of_memory) {+ rt->in_out_of_memory = TRUE;+ JS_ThrowInternalError(ctx, "out of memory");+ rt->in_out_of_memory = FALSE;+ }+ return JS_EXCEPTION;+}++static JSValue JS_ThrowStackOverflow(JSContext *ctx)+{+ return JS_ThrowInternalError(ctx, "stack overflow");+}++static JSValue JS_ThrowTypeErrorNotAnObject(JSContext *ctx)+{+ return JS_ThrowTypeError(ctx, "not an object");+}++static JSValue JS_ThrowTypeErrorNotASymbol(JSContext *ctx)+{+ return JS_ThrowTypeError(ctx, "not a symbol");+}++static JSValue JS_ThrowReferenceErrorNotDefined(JSContext *ctx, JSAtom name)+{+ char buf[ATOM_GET_STR_BUF_SIZE];+ return JS_ThrowReferenceError(ctx, "'%s' is not defined",+ JS_AtomGetStr(ctx, buf, sizeof(buf), name));+}++static JSValue JS_ThrowReferenceErrorUninitialized(JSContext *ctx, JSAtom name)+{+ char buf[ATOM_GET_STR_BUF_SIZE];+ return JS_ThrowReferenceError(ctx, "%s is not initialized",+ name == JS_ATOM_NULL ? "lexical variable" :+ JS_AtomGetStr(ctx, buf, sizeof(buf), name));+}++static JSValue JS_ThrowReferenceErrorUninitialized2(JSContext *ctx,+ JSFunctionBytecode *b,+ int idx, BOOL is_ref)+{+ JSAtom atom = JS_ATOM_NULL;+ if (is_ref) {+ atom = b->closure_var[idx].var_name;+ } else {+ /* not present if the function is stripped and contains no eval() */+ if (b->vardefs)+ atom = b->vardefs[b->arg_count + idx].var_name;+ }+ return JS_ThrowReferenceErrorUninitialized(ctx, atom);+}++static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id)+{+ JSRuntime *rt = ctx->rt;+ JSAtom name;+ name = rt->class_array[class_id].class_name;+ return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name);+}++static no_inline __exception int __js_poll_interrupts(JSContext *ctx)+{+ JSRuntime *rt = ctx->rt;+ ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT;+ if (rt->interrupt_handler) {+ if (rt->interrupt_handler(rt, rt->interrupt_opaque)) {+ /* XXX: should set a specific flag to avoid catching */+ JS_ThrowInternalError(ctx, "interrupted");+ JS_SetUncatchableError(ctx, ctx->rt->current_exception, TRUE);+ return -1;+ }+ }+ return 0;+}++static inline __exception int js_poll_interrupts(JSContext *ctx)+{+ if (unlikely(--ctx->interrupt_counter <= 0)) {+ return __js_poll_interrupts(ctx);+ } else {+ return 0;+ }+}++/* return -1 (exception) or TRUE/FALSE */+static int JS_SetPrototypeInternal(JSContext *ctx, JSValueConst obj,+ JSValueConst proto_val,+ BOOL throw_flag)+{+ JSObject *proto, *p, *p1;+ JSShape *sh;++ if (throw_flag) {+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_NULL ||+ JS_VALUE_GET_TAG(obj) == JS_TAG_UNDEFINED)+ goto not_obj;+ } else {+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)+ goto not_obj;+ }+ p = JS_VALUE_GET_OBJ(obj);+ if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) {+ if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_NULL) {+ not_obj:+ JS_ThrowTypeErrorNotAnObject(ctx);+ return -1;+ }+ proto = NULL;+ } else {+ proto = JS_VALUE_GET_OBJ(proto_val);+ }++ if (throw_flag && JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)+ return TRUE;++ if (unlikely(p->class_id == JS_CLASS_PROXY))+ return js_proxy_setPrototypeOf(ctx, obj, proto_val, throw_flag);+ sh = p->shape;+ if (sh->proto == proto)+ return TRUE;+ if (!p->extensible) {+ if (throw_flag) {+ JS_ThrowTypeError(ctx, "object is not extensible");+ return -1;+ } else {+ return FALSE;+ }+ }+ if (proto) {+ /* check if there is a cycle */+ p1 = proto;+ do {+ if (p1 == p) {+ if (throw_flag) {+ JS_ThrowTypeError(ctx, "circular prototype chain");+ return -1;+ } else {+ return FALSE;+ }+ }+ /* Note: for Proxy objects, proto is NULL */+ p1 = p1->shape->proto;+ } while (p1 != NULL);+ JS_DupValue(ctx, proto_val);+ }++ if (js_shape_prepare_update(ctx, p, NULL))+ return -1;+ sh = p->shape;+ if (sh->proto)+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto));+ sh->proto = proto;+ return TRUE;+}++/* return -1 (exception) or TRUE/FALSE */+int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val)+{+ return JS_SetPrototypeInternal(ctx, obj, proto_val, TRUE);+}++/* Only works for primitive types, otherwise return JS_NULL. */+static JSValueConst JS_GetPrototypePrimitive(JSContext *ctx, JSValueConst val)+{+ switch(JS_VALUE_GET_NORM_TAG(val)) {+#ifdef CONFIG_BIGNUM+ case JS_TAG_BIG_INT:+ val = ctx->class_proto[JS_CLASS_BIG_INT];+ break;+ case JS_TAG_BIG_FLOAT:+ val = ctx->class_proto[JS_CLASS_BIG_FLOAT];+ break;+ case JS_TAG_BIG_DECIMAL:+ val = ctx->class_proto[JS_CLASS_BIG_DECIMAL];+ break;+#endif+ case JS_TAG_INT:+ case JS_TAG_FLOAT64:+ val = ctx->class_proto[JS_CLASS_NUMBER];+ break;+ case JS_TAG_BOOL:+ val = ctx->class_proto[JS_CLASS_BOOLEAN];+ break;+ case JS_TAG_STRING:+ val = ctx->class_proto[JS_CLASS_STRING];+ break;+ case JS_TAG_SYMBOL:+ val = ctx->class_proto[JS_CLASS_SYMBOL];+ break;+ case JS_TAG_OBJECT:+ case JS_TAG_NULL:+ case JS_TAG_UNDEFINED:+ default:+ val = JS_NULL;+ break;+ }+ return val;+}++/* Return an Object, JS_NULL or JS_EXCEPTION in case of Proxy object. */+JSValue JS_GetPrototype(JSContext *ctx, JSValueConst obj)+{+ JSValue val;+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {+ JSObject *p;+ p = JS_VALUE_GET_OBJ(obj);+ if (unlikely(p->class_id == JS_CLASS_PROXY)) {+ val = js_proxy_getPrototypeOf(ctx, obj);+ } else {+ p = p->shape->proto;+ if (!p)+ val = JS_NULL;+ else+ val = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));+ }+ } else {+ val = JS_DupValue(ctx, JS_GetPrototypePrimitive(ctx, obj));+ }+ return val;+}++static JSValue JS_GetPrototypeFree(JSContext *ctx, JSValue obj)+{+ JSValue obj1;+ obj1 = JS_GetPrototype(ctx, obj);+ JS_FreeValue(ctx, obj);+ return obj1;+}++/* return TRUE, FALSE or (-1) in case of exception */+static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val,+ JSValueConst obj)+{+ JSValue obj_proto;+ JSObject *proto;+ const JSObject *p, *proto1;+ BOOL ret;++ if (!JS_IsFunction(ctx, obj))+ return FALSE;+ p = JS_VALUE_GET_OBJ(obj);+ if (p->class_id == JS_CLASS_BOUND_FUNCTION) {+ JSBoundFunction *s = p->u.bound_function;+ return JS_IsInstanceOf(ctx, val, s->func_obj);+ }++ /* Only explicitly boxed values are instances of constructors */+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)+ return FALSE;+ obj_proto = JS_GetProperty(ctx, obj, JS_ATOM_prototype);+ if (JS_VALUE_GET_TAG(obj_proto) != JS_TAG_OBJECT) {+ if (!JS_IsException(obj_proto))+ JS_ThrowTypeError(ctx, "operand 'prototype' property is not an object");+ ret = -1;+ goto done;+ }+ proto = JS_VALUE_GET_OBJ(obj_proto);+ p = JS_VALUE_GET_OBJ(val);+ for(;;) {+ proto1 = p->shape->proto;+ if (!proto1) {+ /* slow case if proxy in the prototype chain */+ if (unlikely(p->class_id == JS_CLASS_PROXY)) {+ JSValue obj1;+ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, (JSObject *)p));+ for(;;) {+ obj1 = JS_GetPrototypeFree(ctx, obj1);+ if (JS_IsException(obj1)) {+ ret = -1;+ break;+ }+ if (JS_IsNull(obj1)) {+ ret = FALSE;+ break;+ }+ if (proto == JS_VALUE_GET_OBJ(obj1)) {+ JS_FreeValue(ctx, obj1);+ ret = TRUE;+ break;+ }+ /* must check for timeout to avoid infinite loop */+ if (js_poll_interrupts(ctx)) {+ JS_FreeValue(ctx, obj1);+ ret = -1;+ break;+ }+ }+ } else {+ ret = FALSE;+ }+ break;+ }+ p = proto1;+ if (proto == p) {+ ret = TRUE;+ break;+ }+ }+done:+ JS_FreeValue(ctx, obj_proto);+ return ret;+}++/* return TRUE, FALSE or (-1) in case of exception */+int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj)+{+ JSValue method;++ if (!JS_IsObject(obj))+ goto fail;+ method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_hasInstance);+ if (JS_IsException(method))+ return -1;+ if (!JS_IsNull(method) && !JS_IsUndefined(method)) {+ JSValue ret;+ ret = JS_CallFree(ctx, method, obj, 1, &val);+ return JS_ToBoolFree(ctx, ret);+ }++ /* legacy case */+ if (!JS_IsFunction(ctx, obj)) {+ fail:+ JS_ThrowTypeError(ctx, "invalid 'instanceof' right operand");+ return -1;+ }+ return JS_OrdinaryIsInstanceOf(ctx, val, obj);+}++/* return the value associated to the autoinit property or an exception */+typedef JSValue JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque);++static JSAutoInitFunc *js_autoinit_func_table[] = {+ js_instantiate_prototype, /* JS_AUTOINIT_ID_PROTOTYPE */+ js_module_ns_autoinit, /* JS_AUTOINIT_ID_MODULE_NS */+ JS_InstantiateFunctionListItem2, /* JS_AUTOINIT_ID_PROP */+};++/* warning: 'prs' is reallocated after it */+static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop,+ JSProperty *pr, JSShapeProperty *prs)+{+ JSValue val;+ JSContext *realm;+ JSAutoInitFunc *func;++ if (js_shape_prepare_update(ctx, p, &prs))+ return -1;++ realm = js_autoinit_get_realm(pr);+ func = js_autoinit_func_table[js_autoinit_get_id(pr)];+ /* 'func' shall not modify the object properties 'pr' */+ val = func(realm, p, prop, pr->u.init.opaque);+ js_autoinit_free(ctx->rt, pr);+ prs->flags &= ~JS_PROP_TMASK;+ pr->u.value = JS_UNDEFINED;+ if (JS_IsException(val))+ return -1;+ pr->u.value = val;+ return 0;+}++JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,+ JSAtom prop, JSValueConst this_obj,+ BOOL throw_ref_error)+{+ JSObject *p;+ JSProperty *pr;+ JSShapeProperty *prs;+ uint32_t tag;++ tag = JS_VALUE_GET_TAG(obj);+ if (unlikely(tag != JS_TAG_OBJECT)) {+ switch(tag) {+ case JS_TAG_NULL:+ return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", prop);+ case JS_TAG_UNDEFINED:+ return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of undefined", prop);+ case JS_TAG_EXCEPTION:+ return JS_EXCEPTION;+ case JS_TAG_STRING:+ {+ JSString *p1 = JS_VALUE_GET_STRING(obj);+ if (__JS_AtomIsTaggedInt(prop)) {+ uint32_t idx, ch;+ idx = __JS_AtomToUInt32(prop);+ if (idx < p1->len) {+ if (p1->is_wide_char)+ ch = p1->u.str16[idx];+ else+ ch = p1->u.str8[idx];+ return js_new_string_char(ctx, ch);+ }+ } else if (prop == JS_ATOM_length) {+ return JS_NewInt32(ctx, p1->len);+ }+ }+ break;+ default:+ break;+ }+ /* cannot raise an exception */+ p = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj));+ if (!p)+ return JS_UNDEFINED;+ } else {+ p = JS_VALUE_GET_OBJ(obj);+ }++ for(;;) {+ prs = find_own_property(&pr, p, prop);+ if (prs) {+ /* found */+ if (unlikely(prs->flags & JS_PROP_TMASK)) {+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {+ if (unlikely(!pr->u.getset.getter)) {+ return JS_UNDEFINED;+ } else {+ JSValue func = JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter);+ /* Note: the field could be removed in the getter */+ func = JS_DupValue(ctx, func);+ return JS_CallFree(ctx, func, this_obj, 0, NULL);+ }+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {+ JSValue val = *pr->u.var_ref->pvalue;+ if (unlikely(JS_IsUninitialized(val)))+ return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);+ return JS_DupValue(ctx, val);+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {+ /* Instantiate property and retry */+ if (JS_AutoInitProperty(ctx, p, prop, pr, prs))+ return JS_EXCEPTION;+ continue;+ }+ } else {+ return JS_DupValue(ctx, pr->u.value);+ }+ }+ if (unlikely(p->is_exotic)) {+ /* exotic behaviors */+ if (p->fast_array) {+ if (__JS_AtomIsTaggedInt(prop)) {+ uint32_t idx = __JS_AtomToUInt32(prop);+ if (idx < p->u.array.count) {+ /* we avoid duplicating the code */+ return JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx);+ } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {+ return JS_UNDEFINED;+ }+ } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {+ int ret;+ ret = JS_AtomIsNumericIndex(ctx, prop);+ if (ret != 0) {+ if (ret < 0)+ return JS_EXCEPTION;+ return JS_UNDEFINED;+ }+ }+ } else {+ const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;+ if (em) {+ if (em->get_property) {+ JSValue obj1, retval;+ /* XXX: should pass throw_ref_error */+ /* Note: if 'p' is a prototype, it can be+ freed in the called function */+ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));+ retval = em->get_property(ctx, obj1, prop, this_obj);+ JS_FreeValue(ctx, obj1);+ return retval;+ }+ if (em->get_own_property) {+ JSPropertyDescriptor desc;+ int ret;+ JSValue obj1;++ /* Note: if 'p' is a prototype, it can be+ freed in the called function */+ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));+ ret = em->get_own_property(ctx, &desc, obj1, prop);+ JS_FreeValue(ctx, obj1);+ if (ret < 0)+ return JS_EXCEPTION;+ if (ret) {+ if (desc.flags & JS_PROP_GETSET) {+ JS_FreeValue(ctx, desc.setter);+ return JS_CallFree(ctx, desc.getter, this_obj, 0, NULL);+ } else {+ return desc.value;+ }+ }+ }+ }+ }+ }+ p = p->shape->proto;+ if (!p)+ break;+ }+ if (unlikely(throw_ref_error)) {+ return JS_ThrowReferenceErrorNotDefined(ctx, prop);+ } else {+ return JS_UNDEFINED;+ }+}++static JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *ctx, JSAtom atom)+{+ return JS_ThrowTypeErrorAtom(ctx, "private class field '%s' does not exist",+ atom);+}++/* Private fields can be added even on non extensible objects or+ Proxies */+static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj,+ JSValueConst name, JSValue val)+{+ JSObject *p;+ JSShapeProperty *prs;+ JSProperty *pr;+ JSAtom prop;++ if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {+ JS_ThrowTypeErrorNotAnObject(ctx);+ goto fail;+ }+ /* safety check */+ if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) {+ JS_ThrowTypeErrorNotASymbol(ctx);+ goto fail;+ }+ prop = js_symbol_to_atom(ctx, (JSValue)name);+ p = JS_VALUE_GET_OBJ(obj);+ prs = find_own_property(&pr, p, prop);+ if (prs) {+ JS_ThrowTypeErrorAtom(ctx, "private class field '%s' already exists",+ prop);+ goto fail;+ }+ pr = add_property(ctx, p, prop, JS_PROP_C_W_E);+ if (unlikely(!pr)) {+ fail:+ JS_FreeValue(ctx, val);+ return -1;+ }+ pr->u.value = val;+ return 0;+}++static JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj,+ JSValueConst name)+{+ JSObject *p;+ JSShapeProperty *prs;+ JSProperty *pr;+ JSAtom prop;++ if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))+ return JS_ThrowTypeErrorNotAnObject(ctx);+ /* safety check */+ if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL))+ return JS_ThrowTypeErrorNotASymbol(ctx);+ prop = js_symbol_to_atom(ctx, (JSValue)name);+ p = JS_VALUE_GET_OBJ(obj);+ prs = find_own_property(&pr, p, prop);+ if (!prs) {+ JS_ThrowTypeErrorPrivateNotFound(ctx, prop);+ return JS_EXCEPTION;+ }+ return JS_DupValue(ctx, pr->u.value);+}++static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj,+ JSValueConst name, JSValue val)+{+ JSObject *p;+ JSShapeProperty *prs;+ JSProperty *pr;+ JSAtom prop;++ if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {+ JS_ThrowTypeErrorNotAnObject(ctx);+ goto fail;+ }+ /* safety check */+ if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) {+ JS_ThrowTypeErrorNotASymbol(ctx);+ goto fail;+ }+ prop = js_symbol_to_atom(ctx, (JSValue)name);+ p = JS_VALUE_GET_OBJ(obj);+ prs = find_own_property(&pr, p, prop);+ if (!prs) {+ JS_ThrowTypeErrorPrivateNotFound(ctx, prop);+ fail:+ JS_FreeValue(ctx, val);+ return -1;+ }+ set_value(ctx, &pr->u.value, val);+ return 0;+}++static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj)+{+ JSObject *p, *p1;+ JSShapeProperty *prs;+ JSProperty *pr;+ JSValue brand;+ JSAtom brand_atom;+ + if (unlikely(JS_VALUE_GET_TAG(home_obj) != JS_TAG_OBJECT)) {+ JS_ThrowTypeErrorNotAnObject(ctx);+ return -1;+ }+ p = JS_VALUE_GET_OBJ(home_obj);+ prs = find_own_property(&pr, p, JS_ATOM_Private_brand);+ if (!prs) {+ brand = JS_NewSymbolFromAtom(ctx, JS_ATOM_brand, JS_ATOM_TYPE_PRIVATE);+ if (JS_IsException(brand))+ return -1;+ /* if the brand is not present, add it */+ pr = add_property(ctx, p, JS_ATOM_Private_brand, JS_PROP_C_W_E);+ if (!pr) {+ JS_FreeValue(ctx, brand);+ return -1;+ }+ pr->u.value = JS_DupValue(ctx, brand);+ } else {+ brand = JS_DupValue(ctx, pr->u.value);+ }+ brand_atom = js_symbol_to_atom(ctx, brand);+ + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {+ JS_ThrowTypeErrorNotAnObject(ctx);+ JS_FreeAtom(ctx, brand_atom);+ return -1;+ }+ p1 = JS_VALUE_GET_OBJ(obj);+ pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E);+ JS_FreeAtom(ctx, brand_atom);+ if (!pr)+ return -1;+ pr->u.value = JS_UNDEFINED;+ return 0;+}++static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func)+{+ JSObject *p, *p1, *home_obj;+ JSShapeProperty *prs;+ JSProperty *pr;+ JSValueConst brand;+ + /* get the home object of 'func' */+ if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)) {+ not_obj:+ JS_ThrowTypeErrorNotAnObject(ctx);+ return -1;+ }+ p1 = JS_VALUE_GET_OBJ(func);+ if (!js_class_has_bytecode(p1->class_id))+ goto not_obj;+ home_obj = p1->u.func.home_object;+ if (!home_obj)+ goto not_obj;+ prs = find_own_property(&pr, home_obj, JS_ATOM_Private_brand);+ if (!prs) {+ JS_ThrowTypeError(ctx, "expecting <brand> private field");+ return -1;+ }+ brand = pr->u.value;+ /* safety check */+ if (unlikely(JS_VALUE_GET_TAG(brand) != JS_TAG_SYMBOL))+ goto not_obj;+ + /* get the brand array of 'obj' */+ if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))+ goto not_obj;+ p = JS_VALUE_GET_OBJ(obj);+ prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, (JSValue)brand));+ if (!prs) {+ JS_ThrowTypeError(ctx, "invalid brand on object");+ return -1;+ }+ return 0;+}++static uint32_t js_string_obj_get_length(JSContext *ctx,+ JSValueConst obj)+{+ JSObject *p;+ JSString *p1;+ uint32_t len = 0;++ /* This is a class exotic method: obj class_id is JS_CLASS_STRING */+ p = JS_VALUE_GET_OBJ(obj);+ if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) {+ p1 = JS_VALUE_GET_STRING(p->u.object_data);+ len = p1->len;+ }+ return len;+}++static int num_keys_cmp(const void *p1, const void *p2, void *opaque)+{+ JSContext *ctx = opaque;+ JSAtom atom1 = ((const JSPropertyEnum *)p1)->atom;+ JSAtom atom2 = ((const JSPropertyEnum *)p2)->atom;+ uint32_t v1, v2;+ BOOL atom1_is_integer, atom2_is_integer;++ atom1_is_integer = JS_AtomIsArrayIndex(ctx, &v1, atom1);+ atom2_is_integer = JS_AtomIsArrayIndex(ctx, &v2, atom2);+ assert(atom1_is_integer && atom2_is_integer);+ if (v1 < v2)+ return -1;+ else if (v1 == v2)+ return 0;+ else+ return 1;+}++static void js_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len)+{+ uint32_t i;+ if (tab) {+ for(i = 0; i < len; i++)+ JS_FreeAtom(ctx, tab[i].atom);+ js_free(ctx, tab);+ }+}++/* return < 0 in case if exception, 0 if OK. ptab and its atoms must+ be freed by the user. */+static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx,+ JSPropertyEnum **ptab,+ uint32_t *plen,+ JSObject *p, int flags)+{+ int i, j;+ JSShape *sh;+ JSShapeProperty *prs;+ JSPropertyEnum *tab_atom, *tab_exotic;+ JSAtom atom;+ uint32_t num_keys_count, str_keys_count, sym_keys_count, atom_count;+ uint32_t num_index, str_index, sym_index, exotic_count, exotic_keys_count;+ BOOL is_enumerable, num_sorted;+ uint32_t num_key;+ JSAtomKindEnum kind;+ + /* clear pointer for consistency in case of failure */+ *ptab = NULL;+ *plen = 0;++ /* compute the number of returned properties */+ num_keys_count = 0;+ str_keys_count = 0;+ sym_keys_count = 0;+ exotic_keys_count = 0;+ exotic_count = 0;+ tab_exotic = NULL;+ sh = p->shape;+ for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {+ atom = prs->atom;+ if (atom != JS_ATOM_NULL) {+ is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0);+ kind = JS_AtomGetKind(ctx, atom);+ if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&+ ((flags >> kind) & 1) != 0) {+ /* need to raise an exception in case of the module+ name space (implicit GetOwnProperty) */+ if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) &&+ (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY))) {+ JSVarRef *var_ref = p->prop[i].u.var_ref;+ if (unlikely(JS_IsUninitialized(*var_ref->pvalue))) {+ JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);+ return -1;+ }+ }+ if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) {+ num_keys_count++;+ } else if (kind == JS_ATOM_KIND_STRING) {+ str_keys_count++;+ } else {+ sym_keys_count++;+ }+ }+ }+ }++ if (p->is_exotic) {+ if (p->fast_array) {+ if (flags & JS_GPN_STRING_MASK) {+ num_keys_count += p->u.array.count;+ }+ } else if (p->class_id == JS_CLASS_STRING) {+ if (flags & JS_GPN_STRING_MASK) {+ num_keys_count += js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p));+ }+ } else {+ const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;+ if (em && em->get_own_property_names) {+ if (em->get_own_property_names(ctx, &tab_exotic, &exotic_count,+ JS_MKPTR(JS_TAG_OBJECT, p)))+ return -1;+ for(i = 0; i < exotic_count; i++) {+ atom = tab_exotic[i].atom;+ kind = JS_AtomGetKind(ctx, atom);+ if (((flags >> kind) & 1) != 0) {+ is_enumerable = FALSE;+ if (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY)) {+ JSPropertyDescriptor desc;+ int res;+ /* set the "is_enumerable" field if necessary */+ res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);+ if (res < 0) {+ js_free_prop_enum(ctx, tab_exotic, exotic_count);+ return -1;+ }+ if (res) {+ is_enumerable =+ ((desc.flags & JS_PROP_ENUMERABLE) != 0);+ js_free_desc(ctx, &desc);+ }+ tab_exotic[i].is_enumerable = is_enumerable;+ }+ if (!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) {+ exotic_keys_count++;+ }+ }+ }+ }+ }+ }++ /* fill them */++ atom_count = num_keys_count + str_keys_count + sym_keys_count + exotic_keys_count;+ /* avoid allocating 0 bytes */+ tab_atom = js_malloc(ctx, sizeof(tab_atom[0]) * max_int(atom_count, 1));+ if (!tab_atom) {+ js_free_prop_enum(ctx, tab_exotic, exotic_count);+ return -1;+ }++ num_index = 0;+ str_index = num_keys_count;+ sym_index = str_index + str_keys_count;++ num_sorted = TRUE;+ sh = p->shape;+ for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {+ atom = prs->atom;+ if (atom != JS_ATOM_NULL) {+ is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0);+ kind = JS_AtomGetKind(ctx, atom);+ if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&+ ((flags >> kind) & 1) != 0) {+ if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) {+ j = num_index++;+ num_sorted = FALSE;+ } else if (kind == JS_ATOM_KIND_STRING) {+ j = str_index++;+ } else {+ j = sym_index++;+ }+ tab_atom[j].atom = JS_DupAtom(ctx, atom);+ tab_atom[j].is_enumerable = is_enumerable;+ }+ }+ }++ if (p->is_exotic) {+ int len;+ if (p->fast_array) {+ if (flags & JS_GPN_STRING_MASK) {+ len = p->u.array.count;+ goto add_array_keys;+ }+ } else if (p->class_id == JS_CLASS_STRING) {+ if (flags & JS_GPN_STRING_MASK) {+ len = js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p));+ add_array_keys:+ for(i = 0; i < len; i++) {+ tab_atom[num_index].atom = __JS_AtomFromUInt32(i);+ if (tab_atom[num_index].atom == JS_ATOM_NULL) {+ js_free_prop_enum(ctx, tab_atom, num_index);+ return -1;+ }+ tab_atom[num_index].is_enumerable = TRUE;+ num_index++;+ }+ }+ } else {+ /* Note: exotic keys are not reordered and comes after the object own properties. */+ for(i = 0; i < exotic_count; i++) {+ atom = tab_exotic[i].atom;+ is_enumerable = tab_exotic[i].is_enumerable;+ kind = JS_AtomGetKind(ctx, atom);+ if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&+ ((flags >> kind) & 1) != 0) {+ tab_atom[sym_index].atom = atom;+ tab_atom[sym_index].is_enumerable = is_enumerable;+ sym_index++;+ } else {+ JS_FreeAtom(ctx, atom);+ }+ }+ js_free(ctx, tab_exotic);+ }+ }++ assert(num_index == num_keys_count);+ assert(str_index == num_keys_count + str_keys_count);+ assert(sym_index == atom_count);++ if (num_keys_count != 0 && !num_sorted) {+ rqsort(tab_atom, num_keys_count, sizeof(tab_atom[0]), num_keys_cmp,+ ctx);+ }+ *ptab = tab_atom;+ *plen = atom_count;+ return 0;+}++int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab,+ uint32_t *plen, JSValueConst obj, int flags)+{+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {+ JS_ThrowTypeErrorNotAnObject(ctx);+ return -1;+ }+ return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen,+ JS_VALUE_GET_OBJ(obj), flags);+}++/* Return -1 if exception,+ FALSE if the property does not exist, TRUE if it exists. If TRUE is+ returned, the property descriptor 'desc' is filled present. */+static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,+ JSObject *p, JSAtom prop)+{+ JSShapeProperty *prs;+ JSProperty *pr;++retry:+ prs = find_own_property(&pr, p, prop);+ if (prs) {+ if (desc) {+ desc->flags = prs->flags & JS_PROP_C_W_E;+ desc->getter = JS_UNDEFINED;+ desc->setter = JS_UNDEFINED;+ desc->value = JS_UNDEFINED;+ if (unlikely(prs->flags & JS_PROP_TMASK)) {+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {+ desc->flags |= JS_PROP_GETSET;+ if (pr->u.getset.getter)+ desc->getter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));+ if (pr->u.getset.setter)+ desc->setter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {+ JSValue val = *pr->u.var_ref->pvalue;+ if (unlikely(JS_IsUninitialized(val))) {+ JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);+ return -1;+ }+ desc->value = JS_DupValue(ctx, val);+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {+ /* Instantiate property and retry */+ if (JS_AutoInitProperty(ctx, p, prop, pr, prs))+ return -1;+ goto retry;+ }+ } else {+ desc->value = JS_DupValue(ctx, pr->u.value);+ }+ } else {+ /* for consistency, send the exception even if desc is NULL */+ if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF)) {+ if (unlikely(JS_IsUninitialized(*pr->u.var_ref->pvalue))) {+ JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);+ return -1;+ }+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {+ /* nothing to do: delay instantiation until actual value and/or attributes are read */+ }+ }+ return TRUE;+ }+ if (p->is_exotic) {+ if (p->fast_array) {+ /* specific case for fast arrays */+ if (__JS_AtomIsTaggedInt(prop)) {+ uint32_t idx;+ idx = __JS_AtomToUInt32(prop);+ if (idx < p->u.array.count) {+ if (desc) {+ desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE |+ JS_PROP_CONFIGURABLE;+ desc->getter = JS_UNDEFINED;+ desc->setter = JS_UNDEFINED;+ desc->value = JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx);+ }+ return TRUE;+ }+ }+ } else {+ const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;+ if (em && em->get_own_property) {+ return em->get_own_property(ctx, desc,+ JS_MKPTR(JS_TAG_OBJECT, p), prop);+ }+ }+ }+ return FALSE;+}++int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc,+ JSValueConst obj, JSAtom prop)+{+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {+ JS_ThrowTypeErrorNotAnObject(ctx);+ return -1;+ }+ return JS_GetOwnPropertyInternal(ctx, desc, JS_VALUE_GET_OBJ(obj), prop);+}++/* return -1 if exception (Proxy object only) or TRUE/FALSE */+int JS_IsExtensible(JSContext *ctx, JSValueConst obj)+{+ JSObject *p;++ if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))+ return FALSE;+ p = JS_VALUE_GET_OBJ(obj);+ if (unlikely(p->class_id == JS_CLASS_PROXY))+ return js_proxy_isExtensible(ctx, obj);+ else+ return p->extensible;+}++/* return -1 if exception (Proxy object only) or TRUE/FALSE */+int JS_PreventExtensions(JSContext *ctx, JSValueConst obj)+{+ JSObject *p;++ if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))+ return FALSE;+ p = JS_VALUE_GET_OBJ(obj);+ if (unlikely(p->class_id == JS_CLASS_PROXY))+ return js_proxy_preventExtensions(ctx, obj);+ p->extensible = FALSE;+ return TRUE;+}++/* return -1 if exception otherwise TRUE or FALSE */+int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop)+{+ JSObject *p;+ int ret;+ JSValue obj1;++ if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))+ return FALSE;+ p = JS_VALUE_GET_OBJ(obj);+ for(;;) {+ if (p->is_exotic) {+ const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;+ if (em && em->has_property) {+ /* has_property can free the prototype */+ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));+ ret = em->has_property(ctx, obj1, prop);+ JS_FreeValue(ctx, obj1);+ return ret;+ }+ }+ /* JS_GetOwnPropertyInternal can free the prototype */+ JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));+ ret = JS_GetOwnPropertyInternal(ctx, NULL, p, prop);+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));+ if (ret != 0)+ return ret;+ if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {+ ret = JS_AtomIsNumericIndex(ctx, prop);+ if (ret != 0) {+ if (ret < 0)+ return -1;+ return FALSE;+ }+ }+ p = p->shape->proto;+ if (!p)+ break;+ }+ return FALSE;+}++/* val must be a symbol */+static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val)+{+ JSAtomStruct *p = JS_VALUE_GET_PTR(val);+ return js_get_atom_index(ctx->rt, p);+}++/* return JS_ATOM_NULL in case of exception */+JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val)+{+ JSAtom atom;+ uint32_t tag;+ tag = JS_VALUE_GET_TAG(val);+ if (tag == JS_TAG_INT &&+ (uint32_t)JS_VALUE_GET_INT(val) <= JS_ATOM_MAX_INT) {+ /* fast path for integer values */+ atom = __JS_AtomFromUInt32(JS_VALUE_GET_INT(val));+ } else if (tag == JS_TAG_SYMBOL) {+ JSAtomStruct *p = JS_VALUE_GET_PTR(val);+ atom = JS_DupAtom(ctx, js_get_atom_index(ctx->rt, p));+ } else {+ JSValue str;+ str = JS_ToPropertyKey(ctx, val);+ if (JS_IsException(str))+ return JS_ATOM_NULL;+ if (JS_VALUE_GET_TAG(str) == JS_TAG_SYMBOL) {+ atom = js_symbol_to_atom(ctx, str);+ } else {+ atom = JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(str));+ }+ }+ return atom;+}++static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj,+ JSValue prop)+{+ JSAtom atom;+ JSValue ret;++ if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT &&+ JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) {+ JSObject *p;+ uint32_t idx, len;+ /* fast path for array access */+ p = JS_VALUE_GET_OBJ(this_obj);+ idx = JS_VALUE_GET_INT(prop);+ len = (uint32_t)p->u.array.count;+ if (unlikely(idx >= len))+ goto slow_path;+ switch(p->class_id) {+ case JS_CLASS_ARRAY:+ case JS_CLASS_ARGUMENTS:+ return JS_DupValue(ctx, p->u.array.u.values[idx]);+ case JS_CLASS_INT8_ARRAY:+ return JS_NewInt32(ctx, p->u.array.u.int8_ptr[idx]);+ case JS_CLASS_UINT8C_ARRAY:+ case JS_CLASS_UINT8_ARRAY:+ return JS_NewInt32(ctx, p->u.array.u.uint8_ptr[idx]);+ case JS_CLASS_INT16_ARRAY:+ return JS_NewInt32(ctx, p->u.array.u.int16_ptr[idx]);+ case JS_CLASS_UINT16_ARRAY:+ return JS_NewInt32(ctx, p->u.array.u.uint16_ptr[idx]);+ case JS_CLASS_INT32_ARRAY:+ return JS_NewInt32(ctx, p->u.array.u.int32_ptr[idx]);+ case JS_CLASS_UINT32_ARRAY:+ return JS_NewUint32(ctx, p->u.array.u.uint32_ptr[idx]);+#ifdef CONFIG_BIGNUM+ case JS_CLASS_BIG_INT64_ARRAY:+ return JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]);+ case JS_CLASS_BIG_UINT64_ARRAY:+ return JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]);+#endif+ case JS_CLASS_FLOAT32_ARRAY:+ return __JS_NewFloat64(ctx, p->u.array.u.float_ptr[idx]);+ case JS_CLASS_FLOAT64_ARRAY:+ return __JS_NewFloat64(ctx, p->u.array.u.double_ptr[idx]);+ default:+ goto slow_path;+ }+ } else {+ slow_path:+ atom = JS_ValueToAtom(ctx, prop);+ JS_FreeValue(ctx, prop);+ if (unlikely(atom == JS_ATOM_NULL))+ return JS_EXCEPTION;+ ret = JS_GetProperty(ctx, this_obj, atom);+ JS_FreeAtom(ctx, atom);+ return ret;+ }+}++JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj,+ uint32_t idx)+{+ return JS_GetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx));+}++/* Check if an object has a generalized numeric property. Return value:+ -1 for exception,+ TRUE if property exists, stored into *pval,+ FALSE if proprty does not exist.+ */+static int JS_TryGetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, JSValue *pval)+{+ JSValue val = JS_UNDEFINED;+ JSAtom prop;+ int present;++ if (likely((uint64_t)idx <= JS_ATOM_MAX_INT)) {+ /* fast path */+ present = JS_HasProperty(ctx, obj, __JS_AtomFromUInt32(idx));+ if (present > 0) {+ val = JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx));+ if (unlikely(JS_IsException(val)))+ present = -1;+ }+ } else {+ prop = JS_NewAtomInt64(ctx, idx);+ present = -1;+ if (likely(prop != JS_ATOM_NULL)) {+ present = JS_HasProperty(ctx, obj, prop);+ if (present > 0) {+ val = JS_GetProperty(ctx, obj, prop);+ if (unlikely(JS_IsException(val)))+ present = -1;+ }+ JS_FreeAtom(ctx, prop);+ }+ }+ *pval = val;+ return present;+}++static JSValue JS_GetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx)+{+ JSAtom prop;+ JSValue val;++ if ((uint64_t)idx <= INT32_MAX) {+ /* fast path for fast arrays */+ return JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx));+ }+ prop = JS_NewAtomInt64(ctx, idx);+ if (prop == JS_ATOM_NULL)+ return JS_EXCEPTION;++ val = JS_GetProperty(ctx, obj, prop);+ JS_FreeAtom(ctx, prop);+ return val;+}++JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj,+ const char *prop)+{+ JSAtom atom;+ JSValue ret;+ atom = JS_NewAtom(ctx, prop);+ ret = JS_GetProperty(ctx, this_obj, atom);+ JS_FreeAtom(ctx, atom);+ return ret;+}++/* Note: the property value is not initialized. Return NULL if memory+ error. */+static JSProperty *add_property(JSContext *ctx,+ JSObject *p, JSAtom prop, int prop_flags)+{+ JSShape *sh, *new_sh;++ sh = p->shape;+ if (sh->is_hashed) {+ /* try to find an existing shape */+ new_sh = find_hashed_shape_prop(ctx->rt, sh, prop, prop_flags);+ if (new_sh) {+ /* matching shape found: use it */+ /* the property array may need to be resized */+ if (new_sh->prop_size != sh->prop_size) {+ JSProperty *new_prop;+ new_prop = js_realloc(ctx, p->prop, sizeof(p->prop[0]) *+ new_sh->prop_size);+ if (!new_prop)+ return NULL;+ p->prop = new_prop;+ }+ p->shape = js_dup_shape(new_sh);+ js_free_shape(ctx->rt, sh);+ return &p->prop[new_sh->prop_count - 1];+ } else if (sh->header.ref_count != 1) {+ /* if the shape is shared, clone it */+ new_sh = js_clone_shape(ctx, sh);+ if (!new_sh)+ return NULL;+ /* hash the cloned shape */+ new_sh->is_hashed = TRUE;+ js_shape_hash_link(ctx->rt, new_sh);+ js_free_shape(ctx->rt, p->shape);+ p->shape = new_sh;+ }+ }+ assert(p->shape->header.ref_count == 1);+ if (add_shape_property(ctx, &p->shape, p, prop, prop_flags))+ return NULL;+ return &p->prop[p->shape->prop_count - 1];+}++/* can be called on Array or Arguments objects. return < 0 if+ memory alloc error. */+static no_inline __exception int convert_fast_array_to_array(JSContext *ctx,+ JSObject *p)+{+ JSProperty *pr;+ JSShape *sh;+ JSValue *tab;+ uint32_t i, len, new_count;++ if (js_shape_prepare_update(ctx, p, NULL))+ return -1;+ len = p->u.array.count;+ /* resize the properties once to simplify the error handling */+ sh = p->shape;+ new_count = sh->prop_count + len;+ if (new_count > sh->prop_size) {+ if (resize_properties(ctx, &p->shape, p, new_count))+ return -1;+ }++ tab = p->u.array.u.values;+ for(i = 0; i < len; i++) {+ /* add_property cannot fail here but+ __JS_AtomFromUInt32(i) fails for i > INT32_MAX */+ pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E);+ pr->u.value = *tab++;+ }+ js_free(ctx, p->u.array.u.values);+ p->u.array.count = 0;+ p->u.array.u.values = NULL; /* fail safe */+ p->u.array.u1.size = 0;+ p->fast_array = 0;+ return 0;+}++static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom)+{+ JSShape *sh;+ JSShapeProperty *pr, *lpr, *prop;+ JSProperty *pr1;+ uint32_t lpr_idx;+ intptr_t h, h1;++ redo:+ sh = p->shape;+ h1 = atom & sh->prop_hash_mask;+ h = prop_hash_end(sh)[-h1 - 1];+ prop = get_shape_prop(sh);+ lpr = NULL;+ lpr_idx = 0; /* prevent warning */+ while (h != 0) {+ pr = &prop[h - 1];+ if (likely(pr->atom == atom)) {+ /* found ! */+ if (!(pr->flags & JS_PROP_CONFIGURABLE))+ return FALSE;+ /* realloc the shape if needed */+ if (lpr)+ lpr_idx = lpr - get_shape_prop(sh);+ if (js_shape_prepare_update(ctx, p, &pr))+ return -1;+ sh = p->shape;+ /* remove property */+ if (lpr) {+ lpr = get_shape_prop(sh) + lpr_idx;+ lpr->hash_next = pr->hash_next;+ } else {+ prop_hash_end(sh)[-h1 - 1] = pr->hash_next;+ }+ sh->deleted_prop_count++;+ /* free the entry */+ pr1 = &p->prop[h - 1];+ free_property(ctx->rt, pr1, pr->flags);+ JS_FreeAtom(ctx, pr->atom);+ /* put default values */+ pr->flags = 0;+ pr->atom = JS_ATOM_NULL;+ pr1->u.value = JS_UNDEFINED;++ /* compact the properties if too many deleted properties */+ if (sh->deleted_prop_count >= 8 &&+ sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) {+ compact_properties(ctx, p);+ }+ return TRUE;+ }+ lpr = pr;+ h = pr->hash_next;+ }++ if (p->is_exotic) {+ if (p->fast_array) {+ uint32_t idx;+ if (JS_AtomIsArrayIndex(ctx, &idx, atom) &&+ idx < p->u.array.count) {+ if (p->class_id == JS_CLASS_ARRAY ||+ p->class_id == JS_CLASS_ARGUMENTS) {+ /* Special case deleting the last element of a fast Array */+ if (idx == p->u.array.count - 1) {+ JS_FreeValue(ctx, p->u.array.u.values[idx]);+ p->u.array.count = idx;+ return TRUE;+ }+ if (convert_fast_array_to_array(ctx, p))+ return -1;+ goto redo;+ } else {+ return FALSE;+ }+ }+ } else {+ const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;+ if (em && em->delete_property) {+ return em->delete_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), atom);+ }+ }+ }+ /* not found */+ return TRUE;+}++static int call_setter(JSContext *ctx, JSObject *setter,+ JSValueConst this_obj, JSValue val, int flags)+{+ JSValue ret, func;+ if (likely(setter)) {+ func = JS_MKPTR(JS_TAG_OBJECT, setter);+ /* Note: the field could be removed in the setter */+ func = JS_DupValue(ctx, func);+ ret = JS_CallFree(ctx, func, this_obj, 1, (JSValueConst *)&val);+ JS_FreeValue(ctx, val);+ if (JS_IsException(ret))+ return -1;+ JS_FreeValue(ctx, ret);+ return TRUE;+ } else {+ JS_FreeValue(ctx, val);+ if ((flags & JS_PROP_THROW) ||+ ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {+ JS_ThrowTypeError(ctx, "no setter for property");+ return -1;+ }+ return FALSE;+ }+}++/* set the array length and remove the array elements if necessary. */+static int set_array_length(JSContext *ctx, JSObject *p, JSValue val,+ int flags)+{+ uint32_t len, idx, cur_len;+ int i, ret;++ /* Note: this call can reallocate the properties of 'p' */+ ret = JS_ToArrayLengthFree(ctx, &len, val, FALSE);+ if (ret)+ return -1;+ /* JS_ToArrayLengthFree() must be done before the read-only test */+ if (unlikely(!(p->shape->prop[0].flags & JS_PROP_WRITABLE)))+ return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);++ if (likely(p->fast_array)) {+ uint32_t old_len = p->u.array.count;+ if (len < old_len) {+ for(i = len; i < old_len; i++) {+ JS_FreeValue(ctx, p->u.array.u.values[i]);+ }+ p->u.array.count = len;+ }+ p->prop[0].u.value = JS_NewUint32(ctx, len);+ } else {+ /* Note: length is always a uint32 because the object is an+ array */+ JS_ToUint32(ctx, &cur_len, p->prop[0].u.value);+ if (len < cur_len) {+ uint32_t d;+ JSShape *sh;+ JSShapeProperty *pr;++ d = cur_len - len;+ sh = p->shape;+ if (d <= sh->prop_count) {+ JSAtom atom;++ /* faster to iterate */+ while (cur_len > len) {+ atom = JS_NewAtomUInt32(ctx, cur_len - 1);+ ret = delete_property(ctx, p, atom);+ JS_FreeAtom(ctx, atom);+ if (unlikely(!ret)) {+ /* unlikely case: property is not+ configurable */+ break;+ }+ cur_len--;+ }+ } else {+ /* faster to iterate thru all the properties. Need two+ passes in case one of the property is not+ configurable */+ cur_len = len;+ for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count;+ i++, pr++) {+ if (pr->atom != JS_ATOM_NULL &&+ JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) {+ if (idx >= cur_len &&+ !(pr->flags & JS_PROP_CONFIGURABLE)) {+ cur_len = idx + 1;+ }+ }+ }++ for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count;+ i++, pr++) {+ if (pr->atom != JS_ATOM_NULL &&+ JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) {+ if (idx >= cur_len) {+ /* remove the property */+ delete_property(ctx, p, pr->atom);+ /* WARNING: the shape may have been modified */+ sh = p->shape;+ pr = get_shape_prop(sh) + i;+ }+ }+ }+ }+ } else {+ cur_len = len;+ }+ set_value(ctx, &p->prop[0].u.value, JS_NewUint32(ctx, cur_len));+ if (unlikely(cur_len > len)) {+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "not configurable");+ }+ }+ return TRUE;+}++/* Preconditions: 'p' must be of class JS_CLASS_ARRAY, p->fast_array =+ TRUE and p->extensible = TRUE */+static int add_fast_array_element(JSContext *ctx, JSObject *p,+ JSValue val, int flags)+{+ uint32_t new_len, array_len;+ /* extend the array by one */+ /* XXX: convert to slow array if new_len > 2^31-1 elements */+ new_len = p->u.array.count + 1;+ /* update the length if necessary. We assume that if the length is+ not an integer, then if it >= 2^31. */+ if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT)) {+ array_len = JS_VALUE_GET_INT(p->prop[0].u.value);+ if (new_len > array_len) {+ if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) {+ JS_FreeValue(ctx, val);+ return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);+ }+ p->prop[0].u.value = JS_NewInt32(ctx, new_len);+ }+ }+ if (unlikely(new_len > p->u.array.u1.size)) {+ uint32_t new_size;+ size_t slack;+ JSValue *new_array_prop;+ /* XXX: potential arithmetic overflow */+ new_size = max_int(new_len, p->u.array.u1.size * 3 / 2);+ new_array_prop = js_realloc2(ctx, p->u.array.u.values, sizeof(JSValue) * new_size, &slack);+ if (!new_array_prop) {+ JS_FreeValue(ctx, val);+ return -1;+ }+ new_size += slack / sizeof(*new_array_prop);+ p->u.array.u.values = new_array_prop;+ p->u.array.u1.size = new_size;+ }+ p->u.array.u.values[new_len - 1] = val;+ p->u.array.count = new_len;+ return TRUE;+}++static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc)+{+ JS_FreeValue(ctx, desc->getter);+ JS_FreeValue(ctx, desc->setter);+ JS_FreeValue(ctx, desc->value);+}++/* generic (and slower) version of JS_SetProperty() for Reflect.set() */+static int JS_SetPropertyGeneric(JSContext *ctx,+ JSObject *p, JSAtom prop,+ JSValue val, JSValueConst this_obj,+ int flags)+{+ int ret;+ JSPropertyDescriptor desc;++ while (p != NULL) {+ if (p->is_exotic) {+ const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;+ if (em && em->set_property) {+ JSValue obj1;+ /* set_property can free the prototype */+ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));+ ret = em->set_property(ctx, obj1, prop,+ val, this_obj, flags);+ JS_FreeValue(ctx, obj1);+ JS_FreeValue(ctx, val);+ return ret;+ }+ }++ ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);+ if (ret < 0)+ return ret;+ if (ret) {+ if (desc.flags & JS_PROP_GETSET) {+ JSObject *setter;+ if (JS_IsUndefined(desc.setter))+ setter = NULL;+ else+ setter = JS_VALUE_GET_OBJ(desc.setter);+ ret = call_setter(ctx, setter, this_obj, val, flags);+ JS_FreeValue(ctx, desc.getter);+ JS_FreeValue(ctx, desc.setter);+ return ret;+ } else {+ JS_FreeValue(ctx, desc.value);+ if (!(desc.flags & JS_PROP_WRITABLE)) {+ goto read_only_error;+ }+ }+ break;+ }+ p = p->shape->proto;+ }++ if (!JS_IsObject(this_obj))+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "receiver is not an object");++ p = JS_VALUE_GET_OBJ(this_obj);++ /* modify the property in this_obj if it already exists */+ ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);+ if (ret < 0)+ return ret;+ if (ret) {+ if (desc.flags & JS_PROP_GETSET) {+ JS_FreeValue(ctx, desc.getter);+ JS_FreeValue(ctx, desc.setter);+ JS_FreeValue(ctx, val);+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "setter is forbidden");+ } else {+ JS_FreeValue(ctx, desc.value);+ if (!(desc.flags & JS_PROP_WRITABLE) ||+ p->class_id == JS_CLASS_MODULE_NS) {+ read_only_error:+ JS_FreeValue(ctx, val);+ return JS_ThrowTypeErrorReadOnly(ctx, flags, prop);+ }+ }+ ret = JS_DefineProperty(ctx, this_obj, prop, val,+ JS_UNDEFINED, JS_UNDEFINED,+ JS_PROP_HAS_VALUE);+ JS_FreeValue(ctx, val);+ return ret;+ }++ ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED,+ flags |+ JS_PROP_HAS_VALUE |+ JS_PROP_HAS_ENUMERABLE |+ JS_PROP_HAS_WRITABLE |+ JS_PROP_HAS_CONFIGURABLE |+ JS_PROP_C_W_E);+ JS_FreeValue(ctx, val);+ return ret;+}++/* return -1 in case of exception or TRUE or FALSE. Warning: 'val' is+ freed by the function. 'flags' is a bitmask of JS_PROP_NO_ADD,+ JS_PROP_THROW or JS_PROP_THROW_STRICT. If JS_PROP_NO_ADD is set,+ the new property is not added and an error is raised. */+int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj,+ JSAtom prop, JSValue val, int flags)+{+ JSObject *p, *p1;+ JSShapeProperty *prs;+ JSProperty *pr;+ uint32_t tag;+ JSPropertyDescriptor desc;+ int ret;+#if 0+ printf("JS_SetPropertyInternal: "); print_atom(ctx, prop); printf("\n");+#endif+ tag = JS_VALUE_GET_TAG(this_obj);+ if (unlikely(tag != JS_TAG_OBJECT)) {+ switch(tag) {+ case JS_TAG_NULL:+ JS_FreeValue(ctx, val);+ JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of null", prop);+ return -1;+ case JS_TAG_UNDEFINED:+ JS_FreeValue(ctx, val);+ JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of undefined", prop);+ return -1;+ default:+ /* even on a primitive type we can have setters on the prototype */+ p = NULL;+ p1 = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, this_obj));+ goto prototype_lookup;+ }+ }+ p = JS_VALUE_GET_OBJ(this_obj);+retry:+ prs = find_own_property(&pr, p, prop);+ if (prs) {+ if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE |+ JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) {+ /* fast case */+ set_value(ctx, &pr->u.value, val);+ return TRUE;+ } else if (prs->flags & JS_PROP_LENGTH) {+ assert(p->class_id == JS_CLASS_ARRAY);+ assert(prop == JS_ATOM_length);+ return set_array_length(ctx, p, val, flags);+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {+ return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags);+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {+ /* JS_PROP_WRITABLE is always true for variable+ references, but they are write protected in module name+ spaces. */+ if (p->class_id == JS_CLASS_MODULE_NS)+ goto read_only_prop;+ set_value(ctx, pr->u.var_ref->pvalue, val);+ return TRUE;+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {+ /* Instantiate property and retry (potentially useless) */+ if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) {+ JS_FreeValue(ctx, val);+ return -1;+ }+ goto retry;+ } else {+ goto read_only_prop;+ }+ }++ p1 = p;+ for(;;) {+ if (p1->is_exotic) {+ if (p1->fast_array) {+ if (__JS_AtomIsTaggedInt(prop)) {+ uint32_t idx = __JS_AtomToUInt32(prop);+ if (idx < p1->u.array.count) {+ if (unlikely(p == p1))+ return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, flags);+ else+ break;+ } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY &&+ p1->class_id <= JS_CLASS_FLOAT64_ARRAY) {+ goto typed_array_oob;+ }+ } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY &&+ p1->class_id <= JS_CLASS_FLOAT64_ARRAY) {+ ret = JS_AtomIsNumericIndex(ctx, prop);+ if (ret != 0) {+ if (ret < 0) {+ JS_FreeValue(ctx, val);+ return -1;+ }+ typed_array_oob:+ val = JS_ToNumberFree(ctx, val);+ JS_FreeValue(ctx, val);+ if (JS_IsException(val))+ return -1;+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index");+ }+ }+ } else {+ const JSClassExoticMethods *em = ctx->rt->class_array[p1->class_id].exotic;+ if (em) {+ JSValue obj1;+ if (em->set_property) {+ /* set_property can free the prototype */+ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));+ ret = em->set_property(ctx, obj1, prop,+ val, this_obj, flags);+ JS_FreeValue(ctx, obj1);+ JS_FreeValue(ctx, val);+ return ret;+ }+ if (em->get_own_property) {+ /* get_own_property can free the prototype */+ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));+ ret = em->get_own_property(ctx, &desc,+ obj1, prop);+ JS_FreeValue(ctx, obj1);+ if (ret < 0) {+ JS_FreeValue(ctx, val);+ return ret;+ }+ if (ret) {+ if (desc.flags & JS_PROP_GETSET) {+ JSObject *setter;+ if (JS_IsUndefined(desc.setter))+ setter = NULL;+ else+ setter = JS_VALUE_GET_OBJ(desc.setter);+ ret = call_setter(ctx, setter, this_obj, val, flags);+ JS_FreeValue(ctx, desc.getter);+ JS_FreeValue(ctx, desc.setter);+ return ret;+ } else {+ JS_FreeValue(ctx, desc.value);+ if (!(desc.flags & JS_PROP_WRITABLE))+ goto read_only_prop;+ if (likely(p == p1)) {+ ret = JS_DefineProperty(ctx, this_obj, prop, val,+ JS_UNDEFINED, JS_UNDEFINED,+ JS_PROP_HAS_VALUE);+ JS_FreeValue(ctx, val);+ return ret;+ } else {+ break;+ }+ }+ }+ }+ }+ }+ }+ p1 = p1->shape->proto;+ prototype_lookup:+ if (!p1)+ break;++ retry2:+ prs = find_own_property(&pr, p1, prop);+ if (prs) {+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {+ return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags);+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {+ /* Instantiate property and retry (potentially useless) */+ if (JS_AutoInitProperty(ctx, p1, prop, pr, prs))+ return -1;+ goto retry2;+ } else if (!(prs->flags & JS_PROP_WRITABLE)) {+ read_only_prop:+ JS_FreeValue(ctx, val);+ return JS_ThrowTypeErrorReadOnly(ctx, flags, prop);+ }+ }+ }++ if (unlikely(flags & JS_PROP_NO_ADD)) {+ JS_FreeValue(ctx, val);+ JS_ThrowReferenceErrorNotDefined(ctx, prop);+ return -1;+ }++ if (unlikely(!p)) {+ JS_FreeValue(ctx, val);+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "not an object");+ }++ if (unlikely(!p->extensible)) {+ JS_FreeValue(ctx, val);+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible");+ }++ if (p->is_exotic) {+ if (p->class_id == JS_CLASS_ARRAY && p->fast_array &&+ __JS_AtomIsTaggedInt(prop)) {+ uint32_t idx = __JS_AtomToUInt32(prop);+ if (idx == p->u.array.count) {+ /* fast case */+ return add_fast_array_element(ctx, p, val, flags);+ } else {+ goto generic_create_prop;+ }+ } else {+ generic_create_prop:+ ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED,+ flags |+ JS_PROP_HAS_VALUE |+ JS_PROP_HAS_ENUMERABLE |+ JS_PROP_HAS_WRITABLE |+ JS_PROP_HAS_CONFIGURABLE |+ JS_PROP_C_W_E);+ JS_FreeValue(ctx, val);+ return ret;+ }+ }++ pr = add_property(ctx, p, prop, JS_PROP_C_W_E);+ if (unlikely(!pr)) {+ JS_FreeValue(ctx, val);+ return -1;+ }+ pr->u.value = val;+ return TRUE;+}++/* flags can be JS_PROP_THROW or JS_PROP_THROW_STRICT */+static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,+ JSValue prop, JSValue val, int flags)+{+ if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT &&+ JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) {+ JSObject *p;+ uint32_t idx;+ double d;+ int32_t v;++ /* fast path for array access */+ p = JS_VALUE_GET_OBJ(this_obj);+ idx = JS_VALUE_GET_INT(prop);+ switch(p->class_id) {+ case JS_CLASS_ARRAY:+ if (unlikely(idx >= (uint32_t)p->u.array.count)) {+ JSObject *p1;+ JSShape *sh1;++ /* fast path to add an element to the array */+ if (idx != (uint32_t)p->u.array.count ||+ !p->fast_array || !p->extensible)+ goto slow_path;+ /* check if prototype chain has a numeric property */+ p1 = p->shape->proto;+ while (p1 != NULL) {+ sh1 = p1->shape;+ if (p1->class_id == JS_CLASS_ARRAY) {+ if (unlikely(!p1->fast_array))+ goto slow_path;+ } else if (p1->class_id == JS_CLASS_OBJECT) {+ if (unlikely(sh1->has_small_array_index))+ goto slow_path;+ } else {+ goto slow_path;+ }+ p1 = sh1->proto;+ }+ /* add element */+ return add_fast_array_element(ctx, p, val, flags);+ }+ set_value(ctx, &p->u.array.u.values[idx], val);+ break;+ case JS_CLASS_ARGUMENTS:+ if (unlikely(idx >= (uint32_t)p->u.array.count))+ goto slow_path;+ set_value(ctx, &p->u.array.u.values[idx], val);+ break;+ case JS_CLASS_UINT8C_ARRAY:+ if (JS_ToUint8ClampFree(ctx, &v, val))+ return -1;+ /* Note: the conversion can detach the typed array, so the+ array bound check must be done after */+ if (unlikely(idx >= (uint32_t)p->u.array.count))+ goto ta_out_of_bound;+ p->u.array.u.uint8_ptr[idx] = v;+ break;+ case JS_CLASS_INT8_ARRAY:+ case JS_CLASS_UINT8_ARRAY:+ if (JS_ToInt32Free(ctx, &v, val))+ return -1;+ if (unlikely(idx >= (uint32_t)p->u.array.count))+ goto ta_out_of_bound;+ p->u.array.u.uint8_ptr[idx] = v;+ break;+ case JS_CLASS_INT16_ARRAY:+ case JS_CLASS_UINT16_ARRAY:+ if (JS_ToInt32Free(ctx, &v, val))+ return -1;+ if (unlikely(idx >= (uint32_t)p->u.array.count))+ goto ta_out_of_bound;+ p->u.array.u.uint16_ptr[idx] = v;+ break;+ case JS_CLASS_INT32_ARRAY:+ case JS_CLASS_UINT32_ARRAY:+ if (JS_ToInt32Free(ctx, &v, val))+ return -1;+ if (unlikely(idx >= (uint32_t)p->u.array.count))+ goto ta_out_of_bound;+ p->u.array.u.uint32_ptr[idx] = v;+ break;+#ifdef CONFIG_BIGNUM+ case JS_CLASS_BIG_INT64_ARRAY:+ case JS_CLASS_BIG_UINT64_ARRAY:+ /* XXX: need specific conversion function */+ {+ int64_t v;+ if (JS_ToBigInt64Free(ctx, &v, val))+ return -1;+ if (unlikely(idx >= (uint32_t)p->u.array.count))+ goto ta_out_of_bound;+ p->u.array.u.uint64_ptr[idx] = v;+ }+ break;+#endif+ case JS_CLASS_FLOAT32_ARRAY:+ if (JS_ToFloat64Free(ctx, &d, val))+ return -1;+ if (unlikely(idx >= (uint32_t)p->u.array.count))+ goto ta_out_of_bound;+ p->u.array.u.float_ptr[idx] = d;+ break;+ case JS_CLASS_FLOAT64_ARRAY:+ if (JS_ToFloat64Free(ctx, &d, val))+ return -1;+ if (unlikely(idx >= (uint32_t)p->u.array.count)) {+ ta_out_of_bound:+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index");+ }+ p->u.array.u.double_ptr[idx] = d;+ break;+ default:+ goto slow_path;+ }+ return TRUE;+ } else {+ JSAtom atom;+ int ret;+ slow_path:+ atom = JS_ValueToAtom(ctx, prop);+ JS_FreeValue(ctx, prop);+ if (unlikely(atom == JS_ATOM_NULL)) {+ JS_FreeValue(ctx, val);+ return -1;+ }+ ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, flags);+ JS_FreeAtom(ctx, atom);+ return ret;+ }+}++int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj,+ uint32_t idx, JSValue val)+{+ return JS_SetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx), val,+ JS_PROP_THROW);+}++int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj,+ int64_t idx, JSValue val)+{+ JSAtom prop;+ int res;++ if ((uint64_t)idx <= INT32_MAX) {+ /* fast path for fast arrays */+ return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val,+ JS_PROP_THROW);+ }+ prop = JS_NewAtomInt64(ctx, idx);+ if (prop == JS_ATOM_NULL) {+ JS_FreeValue(ctx, val);+ return -1;+ }+ res = JS_SetProperty(ctx, this_obj, prop, val);+ JS_FreeAtom(ctx, prop);+ return res;+}++int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj,+ const char *prop, JSValue val)+{+ JSAtom atom;+ int ret;+ atom = JS_NewAtom(ctx, prop);+ ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, JS_PROP_THROW);+ JS_FreeAtom(ctx, atom);+ return ret;+}++/* compute the property flags. For each flag: (JS_PROP_HAS_x forces+ it, otherwise def_flags is used)+ Note: makes assumption about the bit pattern of the flags+*/+static int get_prop_flags(int flags, int def_flags)+{+ int mask;+ mask = (flags >> JS_PROP_HAS_SHIFT) & JS_PROP_C_W_E;+ return (flags & mask) | (def_flags & ~mask);+}++static int JS_CreateProperty(JSContext *ctx, JSObject *p,+ JSAtom prop, JSValueConst val,+ JSValueConst getter, JSValueConst setter,+ int flags)+{+ JSProperty *pr;+ int ret, prop_flags;++ /* add a new property or modify an existing exotic one */+ if (p->is_exotic) {+ if (p->class_id == JS_CLASS_ARRAY) {+ uint32_t idx, len;++ if (p->fast_array) {+ if (__JS_AtomIsTaggedInt(prop)) {+ idx = __JS_AtomToUInt32(prop);+ if (idx == p->u.array.count) {+ if (!p->extensible)+ goto not_extensible;+ if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET))+ goto convert_to_array;+ prop_flags = get_prop_flags(flags, 0);+ if (prop_flags != JS_PROP_C_W_E)+ goto convert_to_array;+ return add_fast_array_element(ctx, p,+ JS_DupValue(ctx, val), flags);+ } else {+ goto convert_to_array;+ }+ } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) {+ /* convert the fast array to normal array */+ convert_to_array:+ if (convert_fast_array_to_array(ctx, p))+ return -1;+ goto generic_array;+ }+ } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) {+ JSProperty *plen;+ JSShapeProperty *pslen;+ generic_array:+ /* update the length field */+ plen = &p->prop[0];+ JS_ToUint32(ctx, &len, plen->u.value);+ if ((idx + 1) > len) {+ pslen = get_shape_prop(p->shape);+ if (unlikely(!(pslen->flags & JS_PROP_WRITABLE)))+ return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);+ /* XXX: should update the length after defining+ the property */+ len = idx + 1;+ set_value(ctx, &plen->u.value, JS_NewUint32(ctx, len));+ }+ }+ } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {+ ret = JS_AtomIsNumericIndex(ctx, prop);+ if (ret != 0) {+ if (ret < 0)+ return -1;+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "cannot create numeric index in typed array");+ }+ } else if (!(flags & JS_PROP_NO_EXOTIC)) {+ const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;+ if (em) {+ if (em->define_own_property) {+ return em->define_own_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p),+ prop, val, getter, setter, flags);+ }+ ret = JS_IsExtensible(ctx, JS_MKPTR(JS_TAG_OBJECT, p));+ if (ret < 0)+ return -1;+ if (!ret)+ goto not_extensible;+ }+ }+ }++ if (!p->extensible) {+ not_extensible:+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible");+ }++ if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {+ prop_flags = (flags & (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) |+ JS_PROP_GETSET;+ } else {+ prop_flags = flags & JS_PROP_C_W_E;+ }+ pr = add_property(ctx, p, prop, prop_flags);+ if (unlikely(!pr))+ return -1;+ if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {+ pr->u.getset.getter = NULL;+ if ((flags & JS_PROP_HAS_GET) && JS_IsFunction(ctx, getter)) {+ pr->u.getset.getter =+ JS_VALUE_GET_OBJ(JS_DupValue(ctx, getter));+ }+ pr->u.getset.setter = NULL;+ if ((flags & JS_PROP_HAS_SET) && JS_IsFunction(ctx, setter)) {+ pr->u.getset.setter =+ JS_VALUE_GET_OBJ(JS_DupValue(ctx, setter));+ }+ } else {+ if (flags & JS_PROP_HAS_VALUE) {+ pr->u.value = JS_DupValue(ctx, val);+ } else {+ pr->u.value = JS_UNDEFINED;+ }+ }+ return TRUE;+}++/* return FALSE if not OK */+static BOOL check_define_prop_flags(int prop_flags, int flags)+{+ BOOL has_accessor, is_getset;++ if (!(prop_flags & JS_PROP_CONFIGURABLE)) {+ if ((flags & (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) ==+ (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) {+ return FALSE;+ }+ if ((flags & JS_PROP_HAS_ENUMERABLE) &&+ (flags & JS_PROP_ENUMERABLE) != (prop_flags & JS_PROP_ENUMERABLE))+ return FALSE;+ }+ if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE |+ JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {+ if (!(prop_flags & JS_PROP_CONFIGURABLE)) {+ has_accessor = ((flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) != 0);+ is_getset = ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET);+ if (has_accessor != is_getset)+ return FALSE;+ if (!has_accessor && !is_getset && !(prop_flags & JS_PROP_WRITABLE)) {+ /* not writable: cannot set the writable bit */+ if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) ==+ (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE))+ return FALSE;+ }+ }+ }+ return TRUE;+}++/* ensure that the shape can be safely modified */+static int js_shape_prepare_update(JSContext *ctx, JSObject *p,+ JSShapeProperty **pprs)+{+ JSShape *sh;+ uint32_t idx = 0; /* prevent warning */++ sh = p->shape;+ if (sh->is_hashed) {+ if (sh->header.ref_count != 1) {+ if (pprs)+ idx = *pprs - get_shape_prop(sh);+ /* clone the shape (the resulting one is no longer hashed) */+ sh = js_clone_shape(ctx, sh);+ if (!sh)+ return -1;+ js_free_shape(ctx->rt, p->shape);+ p->shape = sh;+ if (pprs)+ *pprs = get_shape_prop(sh) + idx;+ } else {+ js_shape_hash_unlink(ctx->rt, sh);+ sh->is_hashed = FALSE;+ }+ }+ return 0;+}++static int js_update_property_flags(JSContext *ctx, JSObject *p,+ JSShapeProperty **pprs, int flags)+{+ if (flags != (*pprs)->flags) {+ if (js_shape_prepare_update(ctx, p, pprs))+ return -1;+ (*pprs)->flags = flags;+ }+ return 0;+}++/* allowed flags:+ JS_PROP_CONFIGURABLE, JS_PROP_WRITABLE, JS_PROP_ENUMERABLE+ JS_PROP_HAS_GET, JS_PROP_HAS_SET, JS_PROP_HAS_VALUE,+ JS_PROP_HAS_CONFIGURABLE, JS_PROP_HAS_WRITABLE, JS_PROP_HAS_ENUMERABLE,+ JS_PROP_THROW, JS_PROP_NO_EXOTIC.+ If JS_PROP_THROW is set, return an exception instead of FALSE.+ if JS_PROP_NO_EXOTIC is set, do not call the exotic+ define_own_property callback.+ return -1 (exception), FALSE or TRUE.+*/+int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,+ JSAtom prop, JSValueConst val,+ JSValueConst getter, JSValueConst setter, int flags)+{+ JSObject *p;+ JSShapeProperty *prs;+ JSProperty *pr;+ int mask, res;++ if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) {+ JS_ThrowTypeErrorNotAnObject(ctx);+ return -1;+ }+ p = JS_VALUE_GET_OBJ(this_obj);++ redo_prop_update:+ prs = find_own_property(&pr, p, prop);+ if (prs) {+ /* the range of the Array length property is always tested before */+ if ((prs->flags & JS_PROP_LENGTH) && (flags & JS_PROP_HAS_VALUE)) {+ uint32_t array_length;+ if (JS_ToArrayLengthFree(ctx, &array_length,+ JS_DupValue(ctx, val), FALSE)) {+ return -1;+ }+ /* this code relies on the fact that Uint32 are never allocated */+ val = (JSValueConst)JS_NewUint32(ctx, array_length);+ /* prs may have been modified */+ prs = find_own_property(&pr, p, prop);+ assert(prs != NULL);+ }+ /* property already exists */+ if (!check_define_prop_flags(prs->flags, flags)) {+ not_configurable:+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable");+ }++ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {+ /* Instantiate property and retry */+ if (JS_AutoInitProperty(ctx, p, prop, pr, prs))+ return -1;+ goto redo_prop_update;+ }++ if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE |+ JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {+ if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {+ JSObject *new_getter, *new_setter;++ if (JS_IsFunction(ctx, getter)) {+ new_getter = JS_VALUE_GET_OBJ(getter);+ } else {+ new_getter = NULL;+ }+ if (JS_IsFunction(ctx, setter)) {+ new_setter = JS_VALUE_GET_OBJ(setter);+ } else {+ new_setter = NULL;+ }++ if ((prs->flags & JS_PROP_TMASK) != JS_PROP_GETSET) {+ if (js_shape_prepare_update(ctx, p, &prs))+ return -1;+ /* convert to getset */+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {+ free_var_ref(ctx->rt, pr->u.var_ref);+ } else {+ JS_FreeValue(ctx, pr->u.value);+ }+ prs->flags = (prs->flags &+ (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) |+ JS_PROP_GETSET;+ pr->u.getset.getter = NULL;+ pr->u.getset.setter = NULL;+ } else {+ if (!(prs->flags & JS_PROP_CONFIGURABLE)) {+ if ((flags & JS_PROP_HAS_GET) &&+ new_getter != pr->u.getset.getter) {+ goto not_configurable;+ }+ if ((flags & JS_PROP_HAS_SET) &&+ new_setter != pr->u.getset.setter) {+ goto not_configurable;+ }+ }+ }+ if (flags & JS_PROP_HAS_GET) {+ if (pr->u.getset.getter)+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));+ if (new_getter)+ JS_DupValue(ctx, getter);+ pr->u.getset.getter = new_getter;+ }+ if (flags & JS_PROP_HAS_SET) {+ if (pr->u.getset.setter)+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));+ if (new_setter)+ JS_DupValue(ctx, setter);+ pr->u.getset.setter = new_setter;+ }+ } else {+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {+ /* convert to data descriptor */+ if (js_shape_prepare_update(ctx, p, &prs))+ return -1;+ if (pr->u.getset.getter)+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));+ if (pr->u.getset.setter)+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));+ prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE);+ pr->u.value = JS_UNDEFINED;+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {+ /* Note: JS_PROP_VARREF is always writable */+ } else {+ if ((prs->flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 &&+ (flags & JS_PROP_HAS_VALUE)) {+ if (!js_same_value(ctx, val, pr->u.value)) {+ goto not_configurable;+ } else {+ return TRUE;+ }+ }+ }+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {+ if (flags & JS_PROP_HAS_VALUE) {+ if (p->class_id == JS_CLASS_MODULE_NS) {+ /* JS_PROP_WRITABLE is always true for variable+ references, but they are write protected in module name+ spaces. */+ if (!js_same_value(ctx, val, *pr->u.var_ref->pvalue))+ goto not_configurable;+ }+ /* update the reference */+ set_value(ctx, pr->u.var_ref->pvalue,+ JS_DupValue(ctx, val));+ }+ /* if writable is set to false, no longer a+ reference (for mapped arguments) */+ if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) {+ JSValue val1;+ if (js_shape_prepare_update(ctx, p, &prs))+ return -1;+ val1 = JS_DupValue(ctx, *pr->u.var_ref->pvalue);+ free_var_ref(ctx->rt, pr->u.var_ref);+ pr->u.value = val1;+ prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE);+ }+ } else if (prs->flags & JS_PROP_LENGTH) {+ if (flags & JS_PROP_HAS_VALUE) {+ /* Note: no JS code is executable because+ 'val' is guaranted to be a Uint32 */+ res = set_array_length(ctx, p, JS_DupValue(ctx, val),+ flags);+ } else {+ res = TRUE;+ }+ /* still need to reset the writable flag if+ needed. The JS_PROP_LENGTH is kept because the+ Uint32 test is still done if the length+ property is read-only. */+ if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) ==+ JS_PROP_HAS_WRITABLE) {+ prs = get_shape_prop(p->shape);+ if (js_update_property_flags(ctx, p, &prs,+ prs->flags & ~JS_PROP_WRITABLE))+ return -1;+ }+ return res;+ } else {+ if (flags & JS_PROP_HAS_VALUE) {+ JS_FreeValue(ctx, pr->u.value);+ pr->u.value = JS_DupValue(ctx, val);+ }+ if (flags & JS_PROP_HAS_WRITABLE) {+ if (js_update_property_flags(ctx, p, &prs,+ (prs->flags & ~JS_PROP_WRITABLE) |+ (flags & JS_PROP_WRITABLE)))+ return -1;+ }+ }+ }+ }+ mask = 0;+ if (flags & JS_PROP_HAS_CONFIGURABLE)+ mask |= JS_PROP_CONFIGURABLE;+ if (flags & JS_PROP_HAS_ENUMERABLE)+ mask |= JS_PROP_ENUMERABLE;+ if (js_update_property_flags(ctx, p, &prs,+ (prs->flags & ~mask) | (flags & mask)))+ return -1;+ return TRUE;+ }++ /* handle modification of fast array elements */+ if (p->fast_array) {+ uint32_t idx;+ uint32_t prop_flags;+ if (p->class_id == JS_CLASS_ARRAY) {+ if (__JS_AtomIsTaggedInt(prop)) {+ idx = __JS_AtomToUInt32(prop);+ if (idx < p->u.array.count) {+ prop_flags = get_prop_flags(flags, JS_PROP_C_W_E);+ if (prop_flags != JS_PROP_C_W_E)+ goto convert_to_slow_array;+ if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {+ convert_to_slow_array:+ if (convert_fast_array_to_array(ctx, p))+ return -1;+ else+ goto redo_prop_update;+ }+ if (flags & JS_PROP_HAS_VALUE) {+ set_value(ctx, &p->u.array.u.values[idx], JS_DupValue(ctx, val));+ }+ return TRUE;+ }+ }+ } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {+ JSValue num;+ int ret;++ if (!__JS_AtomIsTaggedInt(prop)) {+ /* slow path with to handle all numeric indexes */+ num = JS_AtomIsNumericIndex1(ctx, prop);+ if (JS_IsUndefined(num))+ goto typed_array_done;+ if (JS_IsException(num))+ return -1;+ ret = JS_NumberIsInteger(ctx, num);+ if (ret < 0) {+ JS_FreeValue(ctx, num);+ return -1;+ }+ if (!ret) {+ JS_FreeValue(ctx, num);+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "non integer index in typed array");+ }+ ret = JS_NumberIsNegativeOrMinusZero(ctx, num);+ JS_FreeValue(ctx, num);+ if (ret) {+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "negative index in typed array");+ }+ if (!__JS_AtomIsTaggedInt(prop))+ goto typed_array_oob;+ }+ idx = __JS_AtomToUInt32(prop);+ /* if the typed array is detached, p->u.array.count = 0 */+ if (idx >= typed_array_get_length(ctx, p)) {+ typed_array_oob:+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound index in typed array");+ }+ prop_flags = get_prop_flags(flags, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+ if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET) ||+ prop_flags != (JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE)) {+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "invalid descriptor flags");+ }+ if (flags & JS_PROP_HAS_VALUE) {+ return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), JS_DupValue(ctx, val), flags);+ }+ return TRUE;+ typed_array_done: ;+ }+ }++ return JS_CreateProperty(ctx, p, prop, val, getter, setter, flags);+}++static int JS_DefineAutoInitProperty(JSContext *ctx, JSValueConst this_obj,+ JSAtom prop, JSAutoInitIDEnum id,+ void *opaque, int flags)+{+ JSObject *p;+ JSProperty *pr;++ if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT)+ return FALSE;++ p = JS_VALUE_GET_OBJ(this_obj);++ if (find_own_property(&pr, p, prop)) {+ /* property already exists */+ abort();+ return FALSE;+ }++ /* Specialized CreateProperty */+ pr = add_property(ctx, p, prop, (flags & JS_PROP_C_W_E) | JS_PROP_AUTOINIT);+ if (unlikely(!pr))+ return -1;+ pr->u.init.realm_and_id = (uintptr_t)JS_DupContext(ctx);+ assert((pr->u.init.realm_and_id & 3) == 0);+ assert(id <= 3);+ pr->u.init.realm_and_id |= id;+ pr->u.init.opaque = opaque;+ return TRUE;+}++/* shortcut to add or redefine a new property value */+int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj,+ JSAtom prop, JSValue val, int flags)+{+ int ret;+ ret = JS_DefineProperty(ctx, this_obj, prop, val, JS_UNDEFINED, JS_UNDEFINED,+ flags | JS_PROP_HAS_VALUE | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE);+ JS_FreeValue(ctx, val);+ return ret;+}++int JS_DefinePropertyValueValue(JSContext *ctx, JSValueConst this_obj,+ JSValue prop, JSValue val, int flags)+{+ JSAtom atom;+ int ret;+ atom = JS_ValueToAtom(ctx, prop);+ JS_FreeValue(ctx, prop);+ if (unlikely(atom == JS_ATOM_NULL)) {+ JS_FreeValue(ctx, val);+ return -1;+ }+ ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags);+ JS_FreeAtom(ctx, atom);+ return ret;+}++int JS_DefinePropertyValueUint32(JSContext *ctx, JSValueConst this_obj,+ uint32_t idx, JSValue val, int flags)+{+ return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewUint32(ctx, idx),+ val, flags);+}++int JS_DefinePropertyValueInt64(JSContext *ctx, JSValueConst this_obj,+ int64_t idx, JSValue val, int flags)+{+ return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx),+ val, flags);+}++int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj,+ const char *prop, JSValue val, int flags)+{+ JSAtom atom;+ int ret;+ atom = JS_NewAtom(ctx, prop);+ ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags);+ JS_FreeAtom(ctx, atom);+ return ret;+}++/* shortcut to add getter & setter */+int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj,+ JSAtom prop, JSValue getter, JSValue setter,+ int flags)+{+ int ret;+ ret = JS_DefineProperty(ctx, this_obj, prop, JS_UNDEFINED, getter, setter,+ flags | JS_PROP_HAS_GET | JS_PROP_HAS_SET |+ JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE);+ JS_FreeValue(ctx, getter);+ JS_FreeValue(ctx, setter);+ return ret;+}++static int JS_CreateDataPropertyUint32(JSContext *ctx, JSValueConst this_obj,+ int64_t idx, JSValue val, int flags)+{+ return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx),+ val, flags | JS_PROP_CONFIGURABLE |+ JS_PROP_ENUMERABLE | JS_PROP_WRITABLE);+}+++/* return TRUE if 'obj' has a non empty 'name' string */+static BOOL js_object_has_name(JSContext *ctx, JSValueConst obj)+{+ JSProperty *pr;+ JSShapeProperty *prs;+ JSValueConst val;+ JSString *p;+ + prs = find_own_property(&pr, JS_VALUE_GET_OBJ(obj), JS_ATOM_name);+ if (!prs)+ return FALSE;+ if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL)+ return TRUE;+ val = pr->u.value;+ if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)+ return TRUE;+ p = JS_VALUE_GET_STRING(val);+ return (p->len != 0);+}++static int JS_DefineObjectName(JSContext *ctx, JSValueConst obj,+ JSAtom name, int flags)+{+ if (name != JS_ATOM_NULL+ && JS_IsObject(obj)+ && !js_object_has_name(ctx, obj)+ && JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, JS_AtomToString(ctx, name), flags) < 0) {+ return -1;+ }+ return 0;+}++static int JS_DefineObjectNameComputed(JSContext *ctx, JSValueConst obj,+ JSValueConst str, int flags)+{+ if (JS_IsObject(obj) &&+ !js_object_has_name(ctx, obj)) {+ JSAtom prop;+ JSValue name_str;+ prop = JS_ValueToAtom(ctx, str);+ if (prop == JS_ATOM_NULL)+ return -1;+ name_str = js_get_function_name(ctx, prop);+ JS_FreeAtom(ctx, prop);+ if (JS_IsException(name_str))+ return -1;+ if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, name_str, flags) < 0)+ return -1;+ }+ return 0;+}++#define DEFINE_GLOBAL_LEX_VAR (1 << 7)+#define DEFINE_GLOBAL_FUNC_VAR (1 << 6)++static JSValue JS_ThrowSyntaxErrorVarRedeclaration(JSContext *ctx, JSAtom prop)+{+ return JS_ThrowSyntaxErrorAtom(ctx, "redeclaration of '%s'", prop);+}++/* flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */+/* XXX: could support exotic global object. */+static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags)+{+ JSObject *p;+ JSShapeProperty *prs;++ p = JS_VALUE_GET_OBJ(ctx->global_obj);+ prs = find_own_property1(p, prop);+ /* XXX: should handle JS_PROP_AUTOINIT */+ if (flags & DEFINE_GLOBAL_LEX_VAR) {+ if (prs && !(prs->flags & JS_PROP_CONFIGURABLE))+ goto fail_redeclaration;+ } else {+ if (!prs && !p->extensible)+ goto define_error;+ if (flags & DEFINE_GLOBAL_FUNC_VAR) {+ if (prs) {+ if (!(prs->flags & JS_PROP_CONFIGURABLE) &&+ ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET ||+ ((prs->flags & (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)) !=+ (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)))) {+ define_error:+ JS_ThrowTypeErrorAtom(ctx, "cannot define variable '%s'",+ prop);+ return -1;+ }+ }+ }+ }+ /* check if there already is a lexical declaration */+ p = JS_VALUE_GET_OBJ(ctx->global_var_obj);+ prs = find_own_property1(p, prop);+ if (prs) {+ fail_redeclaration:+ JS_ThrowSyntaxErrorVarRedeclaration(ctx, prop);+ return -1;+ }+ return 0;+}++/* def_flags is (0, DEFINE_GLOBAL_LEX_VAR) |+ JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE */+/* XXX: could support exotic global object. */+static int JS_DefineGlobalVar(JSContext *ctx, JSAtom prop, int def_flags)+{+ JSObject *p;+ JSShapeProperty *prs;+ JSProperty *pr;+ JSValue val;+ int flags;++ if (def_flags & DEFINE_GLOBAL_LEX_VAR) {+ p = JS_VALUE_GET_OBJ(ctx->global_var_obj);+ flags = JS_PROP_ENUMERABLE | (def_flags & JS_PROP_WRITABLE) |+ JS_PROP_CONFIGURABLE;+ val = JS_UNINITIALIZED;+ } else {+ p = JS_VALUE_GET_OBJ(ctx->global_obj);+ flags = JS_PROP_ENUMERABLE | JS_PROP_WRITABLE |+ (def_flags & JS_PROP_CONFIGURABLE);+ val = JS_UNDEFINED;+ }+ prs = find_own_property1(p, prop);+ if (prs)+ return 0;+ if (!p->extensible)+ return 0;+ pr = add_property(ctx, p, prop, flags);+ if (unlikely(!pr))+ return -1;+ pr->u.value = val;+ return 0;+}++/* 'def_flags' is 0 or JS_PROP_CONFIGURABLE. */+/* XXX: could support exotic global object. */+static int JS_DefineGlobalFunction(JSContext *ctx, JSAtom prop,+ JSValueConst func, int def_flags)+{++ JSObject *p;+ JSShapeProperty *prs;+ int flags;++ p = JS_VALUE_GET_OBJ(ctx->global_obj);+ prs = find_own_property1(p, prop);+ flags = JS_PROP_HAS_VALUE | JS_PROP_THROW;+ if (!prs || (prs->flags & JS_PROP_CONFIGURABLE)) {+ flags |= JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | def_flags |+ JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE;+ }+ if (JS_DefineProperty(ctx, ctx->global_obj, prop, func,+ JS_UNDEFINED, JS_UNDEFINED, flags) < 0)+ return -1;+ return 0;+}++static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop,+ BOOL throw_ref_error)+{+ JSObject *p;+ JSShapeProperty *prs;+ JSProperty *pr;++ /* no exotic behavior is possible in global_var_obj */+ p = JS_VALUE_GET_OBJ(ctx->global_var_obj);+ prs = find_own_property(&pr, p, prop);+ if (prs) {+ /* XXX: should handle JS_PROP_TMASK properties */+ if (unlikely(JS_IsUninitialized(pr->u.value)))+ return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);+ return JS_DupValue(ctx, pr->u.value);+ }+ return JS_GetPropertyInternal(ctx, ctx->global_obj, prop,+ ctx->global_obj, throw_ref_error);+}++/* construct a reference to a global variable */+static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom prop, JSValue *sp)+{+ JSObject *p;+ JSShapeProperty *prs;+ JSProperty *pr;++ /* no exotic behavior is possible in global_var_obj */+ p = JS_VALUE_GET_OBJ(ctx->global_var_obj);+ prs = find_own_property(&pr, p, prop);+ if (prs) {+ /* XXX: should handle JS_PROP_AUTOINIT properties? */+ /* XXX: conformance: do these tests in+ OP_put_var_ref/OP_get_var_ref ? */+ if (unlikely(JS_IsUninitialized(pr->u.value))) {+ JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);+ return -1;+ }+ if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) {+ return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop);+ }+ sp[0] = JS_DupValue(ctx, ctx->global_var_obj);+ } else {+ int ret;+ ret = JS_HasProperty(ctx, ctx->global_obj, prop);+ if (ret < 0)+ return -1;+ if (ret) {+ sp[0] = JS_DupValue(ctx, ctx->global_obj);+ } else {+ sp[0] = JS_UNDEFINED;+ }+ }+ sp[1] = JS_AtomToValue(ctx, prop);+ return 0;+}++/* use for strict variable access: test if the variable exists */+static int JS_CheckGlobalVar(JSContext *ctx, JSAtom prop)+{+ JSObject *p;+ JSShapeProperty *prs;+ int ret;++ /* no exotic behavior is possible in global_var_obj */+ p = JS_VALUE_GET_OBJ(ctx->global_var_obj);+ prs = find_own_property1(p, prop);+ if (prs) {+ ret = TRUE;+ } else {+ ret = JS_HasProperty(ctx, ctx->global_obj, prop);+ if (ret < 0)+ return -1;+ }+ return ret;+}++/* flag = 0: normal variable write+ flag = 1: initialize lexical variable+ flag = 2: normal variable write, strict check was done before+*/+static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val,+ int flag)+{+ JSObject *p;+ JSShapeProperty *prs;+ JSProperty *pr;+ int flags;++ /* no exotic behavior is possible in global_var_obj */+ p = JS_VALUE_GET_OBJ(ctx->global_var_obj);+ prs = find_own_property(&pr, p, prop);+ if (prs) {+ /* XXX: should handle JS_PROP_AUTOINIT properties? */+ if (flag != 1) {+ if (unlikely(JS_IsUninitialized(pr->u.value))) {+ JS_FreeValue(ctx, val);+ JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);+ return -1;+ }+ if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) {+ JS_FreeValue(ctx, val);+ return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop);+ }+ }+ set_value(ctx, &pr->u.value, val);+ return 0;+ }+ flags = JS_PROP_THROW_STRICT;+ if (is_strict_mode(ctx)) + flags |= JS_PROP_NO_ADD;+ return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, flags);+}++/* return -1, FALSE or TRUE. return FALSE if not configurable or+ invalid object. return -1 in case of exception.+ flags can be 0, JS_PROP_THROW or JS_PROP_THROW_STRICT */+int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags)+{+ JSValue obj1;+ JSObject *p;+ int res;+ + obj1 = JS_ToObject(ctx, obj);+ if (JS_IsException(obj1))+ return -1;+ p = JS_VALUE_GET_OBJ(obj1);+ res = delete_property(ctx, p, prop);+ JS_FreeValue(ctx, obj1);+ if (res != FALSE)+ return res;+ if ((flags & JS_PROP_THROW) ||+ ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {+ JS_ThrowTypeError(ctx, "could not delete property");+ return -1;+ }+ return FALSE;+}++int JS_DeletePropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, int flags)+{+ JSAtom prop;+ int res;++ if ((uint64_t)idx <= JS_ATOM_MAX_INT) {+ /* fast path for fast arrays */+ return JS_DeleteProperty(ctx, obj, __JS_AtomFromUInt32(idx), flags);+ }+ prop = JS_NewAtomInt64(ctx, idx);+ if (prop == JS_ATOM_NULL)+ return -1;+ res = JS_DeleteProperty(ctx, obj, prop, flags);+ JS_FreeAtom(ctx, prop);+ return res;+}++BOOL JS_IsFunction(JSContext *ctx, JSValueConst val)+{+ JSObject *p;+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)+ return FALSE;+ p = JS_VALUE_GET_OBJ(val);+ switch(p->class_id) {+ case JS_CLASS_BYTECODE_FUNCTION:+ return TRUE;+ case JS_CLASS_PROXY:+ return p->u.proxy_data->is_func;+ default:+ return (ctx->rt->class_array[p->class_id].call != NULL);+ }+}++BOOL JS_IsCFunction(JSContext *ctx, JSValueConst val, JSCFunction *func, int magic)+{+ JSObject *p;+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)+ return FALSE;+ p = JS_VALUE_GET_OBJ(val);+ if (p->class_id == JS_CLASS_C_FUNCTION)+ return (p->u.cfunc.c_function.generic == func && p->u.cfunc.magic == magic);+ else+ return FALSE;+}++BOOL JS_IsConstructor(JSContext *ctx, JSValueConst val)+{+ JSObject *p;+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)+ return FALSE;+ p = JS_VALUE_GET_OBJ(val);+ return p->is_constructor;+}++BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, BOOL val)+{+ JSObject *p;+ if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)+ return FALSE;+ p = JS_VALUE_GET_OBJ(func_obj);+ p->is_constructor = val;+ return TRUE;+}++BOOL JS_IsError(JSContext *ctx, JSValueConst val)+{+ JSObject *p;+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)+ return FALSE;+ p = JS_VALUE_GET_OBJ(val);+ return (p->class_id == JS_CLASS_ERROR);+}++/* used to avoid catching interrupt exceptions */+BOOL JS_IsUncatchableError(JSContext *ctx, JSValueConst val)+{+ JSObject *p;+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)+ return FALSE;+ p = JS_VALUE_GET_OBJ(val);+ return p->class_id == JS_CLASS_ERROR && p->is_uncatchable_error;+}++void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag)+{+ JSObject *p;+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)+ return;+ p = JS_VALUE_GET_OBJ(val);+ if (p->class_id == JS_CLASS_ERROR)+ p->is_uncatchable_error = flag;+}++void JS_ResetUncatchableError(JSContext *ctx)+{+ JS_SetUncatchableError(ctx, ctx->rt->current_exception, FALSE);+}++void JS_SetOpaque(JSValue obj, void *opaque)+{+ JSObject *p;+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {+ p = JS_VALUE_GET_OBJ(obj);+ p->u.opaque = opaque;+ }+}++/* return NULL if not an object of class class_id */+void *JS_GetOpaque(JSValueConst obj, JSClassID class_id)+{+ JSObject *p;+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)+ return NULL;+ p = JS_VALUE_GET_OBJ(obj);+ if (p->class_id != class_id)+ return NULL;+ return p->u.opaque;+}++void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id)+{+ void *p = JS_GetOpaque(obj, class_id);+ if (unlikely(!p)) {+ JS_ThrowTypeErrorInvalidClass(ctx, class_id);+ }+ return p;+}++#define HINT_STRING 0+#define HINT_NUMBER 1+#define HINT_NONE 2+/* don't try Symbol.toPrimitive */+#define HINT_FORCE_ORDINARY (1 << 4)++static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint)+{+ int i;+ BOOL force_ordinary;++ JSAtom method_name;+ JSValue method, ret;+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)+ return val;+ force_ordinary = hint & HINT_FORCE_ORDINARY;+ hint &= ~HINT_FORCE_ORDINARY;+ if (!force_ordinary) {+ method = JS_GetProperty(ctx, val, JS_ATOM_Symbol_toPrimitive);+ if (JS_IsException(method))+ goto exception;+ /* ECMA says *If exoticToPrim is not undefined* but tests in+ test262 use null as a non callable converter */+ if (!JS_IsUndefined(method) && !JS_IsNull(method)) {+ JSAtom atom;+ JSValue arg;+ switch(hint) {+ case HINT_STRING:+ atom = JS_ATOM_string;+ break;+ case HINT_NUMBER:+ atom = JS_ATOM_number;+ break;+ default:+ case HINT_NONE:+ atom = JS_ATOM_default;+ break;+ }+ arg = JS_AtomToString(ctx, atom);+ ret = JS_CallFree(ctx, method, val, 1, (JSValueConst *)&arg);+ JS_FreeValue(ctx, arg);+ if (JS_IsException(ret))+ goto exception;+ JS_FreeValue(ctx, val);+ if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT)+ return ret;+ JS_FreeValue(ctx, ret);+ return JS_ThrowTypeError(ctx, "toPrimitive");+ }+ }+ if (hint != HINT_STRING)+ hint = HINT_NUMBER;+ for(i = 0; i < 2; i++) {+ if ((i ^ hint) == 0) {+ method_name = JS_ATOM_toString;+ } else {+ method_name = JS_ATOM_valueOf;+ }+ method = JS_GetProperty(ctx, val, method_name);+ if (JS_IsException(method))+ goto exception;+ if (JS_IsFunction(ctx, method)) {+ ret = JS_CallFree(ctx, method, val, 0, NULL);+ if (JS_IsException(ret))+ goto exception;+ if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {+ JS_FreeValue(ctx, val);+ return ret;+ }+ JS_FreeValue(ctx, ret);+ } else {+ JS_FreeValue(ctx, method);+ }+ }+ JS_ThrowTypeError(ctx, "toPrimitive");+exception:+ JS_FreeValue(ctx, val);+ return JS_EXCEPTION;+}++static JSValue JS_ToPrimitive(JSContext *ctx, JSValueConst val, int hint)+{+ return JS_ToPrimitiveFree(ctx, JS_DupValue(ctx, val), hint);+}++void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj)+{+ JSObject *p;+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)+ return;+ p = JS_VALUE_GET_OBJ(obj);+ p->is_HTMLDDA = TRUE;+}++static inline BOOL JS_IsHTMLDDA(JSContext *ctx, JSValueConst obj)+{+ JSObject *p;+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)+ return FALSE;+ p = JS_VALUE_GET_OBJ(obj);+ return p->is_HTMLDDA;+}+ +static int JS_ToBoolFree(JSContext *ctx, JSValue val)+{+ uint32_t tag = JS_VALUE_GET_TAG(val);+ switch(tag) {+ case JS_TAG_INT:+ return JS_VALUE_GET_INT(val) != 0;+ case JS_TAG_BOOL:+ case JS_TAG_NULL:+ case JS_TAG_UNDEFINED:+ return JS_VALUE_GET_INT(val);+ case JS_TAG_EXCEPTION:+ return -1;+ case JS_TAG_STRING:+ {+ BOOL ret = JS_VALUE_GET_STRING(val)->len != 0;+ JS_FreeValue(ctx, val);+ return ret;+ }+#ifdef CONFIG_BIGNUM+ case JS_TAG_BIG_INT:+ case JS_TAG_BIG_FLOAT:+ {+ JSBigFloat *p = JS_VALUE_GET_PTR(val);+ BOOL ret;+ ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN;+ JS_FreeValue(ctx, val);+ return ret;+ }+ case JS_TAG_BIG_DECIMAL:+ {+ JSBigDecimal *p = JS_VALUE_GET_PTR(val);+ BOOL ret;+ ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN;+ JS_FreeValue(ctx, val);+ return ret;+ }+#endif+ case JS_TAG_OBJECT:+ {+ JSObject *p = JS_VALUE_GET_OBJ(val);+ BOOL ret;+ ret = !p->is_HTMLDDA;+ JS_FreeValue(ctx, val);+ return ret;+ }+ break;+ default:+ if (JS_TAG_IS_FLOAT64(tag)) {+ double d = JS_VALUE_GET_FLOAT64(val);+ return !isnan(d) && d != 0;+ } else {+ JS_FreeValue(ctx, val);+ return TRUE;+ }+ }+}++int JS_ToBool(JSContext *ctx, JSValueConst val)+{+ return JS_ToBoolFree(ctx, JS_DupValue(ctx, val));+}++static int skip_spaces(const char *pc)+{+ const uint8_t *p, *p_next, *p_start;+ uint32_t c;++ p = p_start = (const uint8_t *)pc;+ for (;;) {+ c = *p;+ if (c < 128) {+ if (!((c >= 0x09 && c <= 0x0d) || (c == 0x20)))+ break;+ p++;+ } else {+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);+ if (!lre_is_space(c))+ break;+ p = p_next;+ }+ }+ return p - p_start;+}++static inline int to_digit(int c)+{+ if (c >= '0' && c <= '9')+ return c - '0';+ else if (c >= 'A' && c <= 'Z')+ return c - 'A' + 10;+ else if (c >= 'a' && c <= 'z')+ return c - 'a' + 10;+ else+ return 36;+}++/* XXX: remove */+static double js_strtod(const char *p, int radix, BOOL is_float)+{+ double d;+ int c;+ + if (!is_float || radix != 10) {+ uint64_t n_max, n;+ int int_exp, is_neg;+ + is_neg = 0;+ if (*p == '-') {+ is_neg = 1;+ p++;+ }++ /* skip leading zeros */+ while (*p == '0')+ p++;+ n = 0;+ if (radix == 10)+ n_max = ((uint64_t)-1 - 9) / 10; /* most common case */+ else+ n_max = ((uint64_t)-1 - (radix - 1)) / radix;+ /* XXX: could be more precise */+ int_exp = 0;+ while (*p != '\0') {+ c = to_digit((uint8_t)*p);+ if (c >= radix)+ break;+ if (n <= n_max) {+ n = n * radix + c;+ } else {+ int_exp++;+ }+ p++;+ }+ d = n;+ if (int_exp != 0) {+ d *= pow(radix, int_exp);+ }+ if (is_neg)+ d = -d;+ } else {+ d = strtod(p, NULL);+ }+ return d;+}++#define ATOD_INT_ONLY (1 << 0)+/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */+#define ATOD_ACCEPT_BIN_OCT (1 << 2)+/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */+#define ATOD_ACCEPT_LEGACY_OCTAL (1 << 4)+/* accept _ between digits as a digit separator */+#define ATOD_ACCEPT_UNDERSCORES (1 << 5)+/* allow a suffix to override the type */+#define ATOD_ACCEPT_SUFFIX (1 << 6) +/* default type */+#define ATOD_TYPE_MASK (3 << 7)+#define ATOD_TYPE_FLOAT64 (0 << 7)+#define ATOD_TYPE_BIG_INT (1 << 7)+#define ATOD_TYPE_BIG_FLOAT (2 << 7)+#define ATOD_TYPE_BIG_DECIMAL (3 << 7)+/* assume bigint mode: floats are parsed as integers if no decimal+ point nor exponent */+#define ATOD_MODE_BIGINT (1 << 9) +/* accept -0x1 */+#define ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 10)++#ifdef CONFIG_BIGNUM+static JSValue js_string_to_bigint(JSContext *ctx, const char *buf,+ int radix, int flags, slimb_t *pexponent)+{+ bf_t a_s, *a = &a_s;+ int ret;+ JSValue val;+ val = JS_NewBigInt(ctx);+ if (JS_IsException(val))+ return val;+ a = JS_GetBigInt(val);+ ret = bf_atof(a, buf, NULL, radix, BF_PREC_INF, BF_RNDZ);+ if (ret & BF_ST_MEM_ERROR) {+ JS_FreeValue(ctx, val);+ return JS_ThrowOutOfMemory(ctx);+ }+ val = JS_CompactBigInt1(ctx, val, (flags & ATOD_MODE_BIGINT) != 0);+ return val;+}++static JSValue js_string_to_bigfloat(JSContext *ctx, const char *buf,+ int radix, int flags, slimb_t *pexponent)+{+ bf_t *a;+ int ret;+ JSValue val;+ + val = JS_NewBigFloat(ctx);+ if (JS_IsException(val))+ return val;+ a = JS_GetBigFloat(val);+ if (flags & ATOD_ACCEPT_SUFFIX) {+ /* return the exponent to get infinite precision */+ ret = bf_atof2(a, pexponent, buf, NULL, radix, BF_PREC_INF,+ BF_RNDZ | BF_ATOF_EXPONENT);+ } else {+ ret = bf_atof(a, buf, NULL, radix, ctx->fp_env.prec,+ ctx->fp_env.flags);+ }+ if (ret & BF_ST_MEM_ERROR) {+ JS_FreeValue(ctx, val);+ return JS_ThrowOutOfMemory(ctx);+ }+ return val;+}++static JSValue js_string_to_bigdecimal(JSContext *ctx, const char *buf,+ int radix, int flags, slimb_t *pexponent)+{+ bfdec_t *a;+ int ret;+ JSValue val;+ + val = JS_NewBigDecimal(ctx);+ if (JS_IsException(val))+ return val;+ a = JS_GetBigDecimal(val);+ ret = bfdec_atof(a, buf, NULL, BF_PREC_INF,+ BF_RNDZ | BF_ATOF_NO_NAN_INF);+ if (ret & BF_ST_MEM_ERROR) {+ JS_FreeValue(ctx, val);+ return JS_ThrowOutOfMemory(ctx);+ }+ return val;+}++#endif++/* return an exception in case of memory error. Return JS_NAN if+ invalid syntax */+#ifdef CONFIG_BIGNUM+static JSValue js_atof2(JSContext *ctx, const char *str, const char **pp,+ int radix, int flags, slimb_t *pexponent)+#else+static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,+ int radix, int flags)+#endif+{+ const char *p, *p_start;+ int sep, is_neg;+ BOOL is_float, has_legacy_octal;+ int atod_type = flags & ATOD_TYPE_MASK;+ char buf1[64], *buf;+ int i, j, len;+ BOOL buf_allocated = FALSE;+ JSValue val;+ + /* optional separator between digits */+ sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256;+ has_legacy_octal = FALSE;+ + p = str;+ p_start = p;+ is_neg = 0;+ if (p[0] == '+') {+ p++;+ p_start++;+ if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))+ goto no_radix_prefix;+ } else if (p[0] == '-') {+ p++;+ p_start++;+ is_neg = 1;+ if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))+ goto no_radix_prefix;+ }+ if (p[0] == '0') {+ if ((p[1] == 'x' || p[1] == 'X') &&+ (radix == 0 || radix == 16)) {+ p += 2;+ radix = 16;+ } else if ((p[1] == 'o' || p[1] == 'O') &&+ radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {+ p += 2;+ radix = 8;+ } else if ((p[1] == 'b' || p[1] == 'B') &&+ radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {+ p += 2;+ radix = 2;+ } else if ((p[1] >= '0' && p[1] <= '9') &&+ radix == 0 && (flags & ATOD_ACCEPT_LEGACY_OCTAL)) {+ int i;+ has_legacy_octal = TRUE;+ sep = 256;+ for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++)+ continue;+ if (p[i] == '8' || p[i] == '9')+ goto no_prefix;+ p += 1;+ radix = 8;+ } else {+ goto no_prefix;+ }+ /* there must be a digit after the prefix */+ if (to_digit((uint8_t)*p) >= radix)+ goto fail;+ no_prefix: ;+ } else {+ no_radix_prefix:+ if (!(flags & ATOD_INT_ONLY) &&+ (atod_type == ATOD_TYPE_FLOAT64 ||+ atod_type == ATOD_TYPE_BIG_FLOAT) &&+ strstart(p, "Infinity", &p)) {+#ifdef CONFIG_BIGNUM+ if (atod_type == ATOD_TYPE_BIG_FLOAT) {+ bf_t *a;+ val = JS_NewBigFloat(ctx);+ if (JS_IsException(val))+ goto done;+ a = JS_GetBigFloat(val);+ bf_set_inf(a, is_neg);+ } else+#endif+ {+ double d = 1.0 / 0.0;+ if (is_neg)+ d = -d;+ val = JS_NewFloat64(ctx, d);+ }+ goto done;+ }+ }+ if (radix == 0)+ radix = 10;+ is_float = FALSE;+ p_start = p;+ while (to_digit((uint8_t)*p) < radix+ || (*p == sep && (radix != 10 ||+ p != p_start + 1 || p[-1] != '0') &&+ to_digit((uint8_t)p[1]) < radix)) {+ p++;+ }+ if (!(flags & ATOD_INT_ONLY)) {+ if (*p == '.' && (p > p_start || to_digit((uint8_t)p[1]) < radix)) {+ is_float = TRUE;+ p++;+ if (*p == sep)+ goto fail;+ while (to_digit((uint8_t)*p) < radix ||+ (*p == sep && to_digit((uint8_t)p[1]) < radix))+ p++;+ }+ if (p > p_start &&+ (((*p == 'e' || *p == 'E') && radix == 10) ||+ ((*p == 'p' || *p == 'P') && (radix == 2 || radix == 8 || radix == 16)))) {+ const char *p1 = p + 1;+ is_float = TRUE;+ if (*p1 == '+') {+ p1++;+ } else if (*p1 == '-') {+ p1++;+ }+ if (is_digit((uint8_t)*p1)) {+ p = p1 + 1;+ while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1])))+ p++;+ }+ }+ }+ if (p == p_start)+ goto fail;++ buf = buf1;+ buf_allocated = FALSE;+ len = p - p_start;+ if (unlikely((len + 2) > sizeof(buf1))) {+ buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */+ if (!buf)+ goto mem_error;+ buf_allocated = TRUE;+ }+ /* remove the separators and the radix prefixes */+ j = 0;+ if (is_neg)+ buf[j++] = '-';+ for (i = 0; i < len; i++) {+ if (p_start[i] != '_')+ buf[j++] = p_start[i];+ }+ buf[j] = '\0';++#ifdef CONFIG_BIGNUM+ if (flags & ATOD_ACCEPT_SUFFIX) {+ if (*p == 'n') {+ p++;+ atod_type = ATOD_TYPE_BIG_INT;+ } else if (*p == 'l') {+ p++;+ atod_type = ATOD_TYPE_BIG_FLOAT;+ } else if (*p == 'm') {+ p++;+ atod_type = ATOD_TYPE_BIG_DECIMAL;+ } else {+ if (flags & ATOD_MODE_BIGINT) {+ if (!is_float)+ atod_type = ATOD_TYPE_BIG_INT;+ if (has_legacy_octal)+ goto fail;+ } else {+ if (is_float && radix != 10)+ goto fail;+ }+ }+ } else {+ if (atod_type == ATOD_TYPE_FLOAT64) {+ if (flags & ATOD_MODE_BIGINT) {+ if (!is_float)+ atod_type = ATOD_TYPE_BIG_INT;+ if (has_legacy_octal)+ goto fail;+ } else {+ if (is_float && radix != 10)+ goto fail;+ }+ }+ }++ switch(atod_type) {+ case ATOD_TYPE_FLOAT64:+ {+ double d;+ d = js_strtod(buf, radix, is_float);+ /* return int or float64 */+ val = JS_NewFloat64(ctx, d);+ }+ break;+ case ATOD_TYPE_BIG_INT:+ if (has_legacy_octal || is_float)+ goto fail;+ val = ctx->rt->bigint_ops.from_string(ctx, buf, radix, flags, NULL);+ break;+ case ATOD_TYPE_BIG_FLOAT:+ if (has_legacy_octal)+ goto fail;+ val = ctx->rt->bigfloat_ops.from_string(ctx, buf, radix, flags,+ pexponent);+ break;+ case ATOD_TYPE_BIG_DECIMAL:+ if (radix != 10)+ goto fail;+ val = ctx->rt->bigdecimal_ops.from_string(ctx, buf, radix, flags, NULL);+ break;+ default:+ abort();+ }+#else+ {+ double d;+ (void)has_legacy_octal;+ if (is_float && radix != 10)+ goto fail;+ d = js_strtod(buf, radix, is_float);+ val = JS_NewFloat64(ctx, d);+ }+#endif+ +done:+ if (buf_allocated)+ js_free_rt(ctx->rt, buf);+ if (pp)+ *pp = p;+ return val;+ fail:+ val = JS_NAN;+ goto done;+ mem_error:+ val = JS_ThrowOutOfMemory(ctx);+ goto done;+}++#ifdef CONFIG_BIGNUM+static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,+ int radix, int flags)+{+ return js_atof2(ctx, str, pp, radix, flags, NULL);+}+#endif++typedef enum JSToNumberHintEnum {+ TON_FLAG_NUMBER,+ TON_FLAG_NUMERIC,+} JSToNumberHintEnum;++static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val,+ JSToNumberHintEnum flag)+{+ uint32_t tag;+ JSValue ret;++ redo:+ tag = JS_VALUE_GET_NORM_TAG(val);+ switch(tag) {+#ifdef CONFIG_BIGNUM+ case JS_TAG_BIG_DECIMAL:+ if (flag != TON_FLAG_NUMERIC) {+ JS_FreeValue(ctx, val);+ return JS_ThrowTypeError(ctx, "cannot convert bigdecimal to number");+ }+ ret = val;+ break;+ case JS_TAG_BIG_INT:+ if (flag != TON_FLAG_NUMERIC) {+ JS_FreeValue(ctx, val);+ return JS_ThrowTypeError(ctx, "cannot convert bigint to number");+ }+ ret = val;+ break;+ case JS_TAG_BIG_FLOAT:+ if (flag != TON_FLAG_NUMERIC) {+ JS_FreeValue(ctx, val);+ return JS_ThrowTypeError(ctx, "cannot convert bigfloat to number");+ }+ ret = val;+ break;+#endif+ case JS_TAG_FLOAT64:+ case JS_TAG_INT:+ case JS_TAG_EXCEPTION:+ ret = val;+ break;+ case JS_TAG_BOOL:+ case JS_TAG_NULL:+ ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));+ break;+ case JS_TAG_UNDEFINED:+ ret = JS_NAN;+ break;+ case JS_TAG_OBJECT:+ val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);+ if (JS_IsException(val))+ return JS_EXCEPTION;+ goto redo;+ case JS_TAG_STRING:+ {+ const char *str;+ const char *p;+ size_t len;+ + str = JS_ToCStringLen(ctx, &len, val);+ JS_FreeValue(ctx, val);+ if (!str)+ return JS_EXCEPTION;+ p = str;+ p += skip_spaces(p);+ if ((p - str) == len) {+ ret = JS_NewInt32(ctx, 0);+ } else {+ int flags = ATOD_ACCEPT_BIN_OCT;+ ret = js_atof(ctx, p, &p, 0, flags);+ if (!JS_IsException(ret)) {+ p += skip_spaces(p);+ if ((p - str) != len) {+ JS_FreeValue(ctx, ret);+ ret = JS_NAN;+ }+ }+ }+ JS_FreeCString(ctx, str);+ }+ break;+ case JS_TAG_SYMBOL:+ JS_FreeValue(ctx, val);+ return JS_ThrowTypeError(ctx, "cannot convert symbol to number");+ default:+ JS_FreeValue(ctx, val);+ ret = JS_NAN;+ break;+ }+ return ret;+}++static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val)+{+ return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER);+}++static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val)+{+ return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC);+}++static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val)+{+ return JS_ToNumericFree(ctx, JS_DupValue(ctx, val));+}++static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres,+ JSValue val)+{+ double d;+ uint32_t tag;++ val = JS_ToNumberFree(ctx, val);+ if (JS_IsException(val)) {+ *pres = JS_FLOAT64_NAN;+ return -1;+ }+ tag = JS_VALUE_GET_NORM_TAG(val);+ switch(tag) {+ case JS_TAG_INT:+ d = JS_VALUE_GET_INT(val);+ break;+ case JS_TAG_FLOAT64:+ d = JS_VALUE_GET_FLOAT64(val);+ break;+#ifdef CONFIG_BIGNUM+ case JS_TAG_BIG_INT:+ case JS_TAG_BIG_FLOAT:+ {+ JSBigFloat *p = JS_VALUE_GET_PTR(val);+ /* XXX: there can be a double rounding issue with some+ primitives (such as JS_ToUint8ClampFree()), but it is+ not critical to fix it. */+ bf_get_float64(&p->num, &d, BF_RNDN);+ JS_FreeValue(ctx, val);+ }+ break;+#endif+ default:+ abort();+ }+ *pres = d;+ return 0;+}++static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val)+{+ uint32_t tag;++ tag = JS_VALUE_GET_TAG(val);+ if (tag <= JS_TAG_NULL) {+ *pres = JS_VALUE_GET_INT(val);+ return 0;+ } else if (JS_TAG_IS_FLOAT64(tag)) {+ *pres = JS_VALUE_GET_FLOAT64(val);+ return 0;+ } else {+ return __JS_ToFloat64Free(ctx, pres, val);+ }+}++int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val)+{+ return JS_ToFloat64Free(ctx, pres, JS_DupValue(ctx, val));+}++static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val)+{+ return JS_ToNumberFree(ctx, JS_DupValue(ctx, val));+}++/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */+static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val)+{+ uint32_t tag;+ JSValue ret;++ redo:+ tag = JS_VALUE_GET_NORM_TAG(val);+ switch(tag) {+ case JS_TAG_INT:+ case JS_TAG_BOOL:+ case JS_TAG_NULL:+ case JS_TAG_UNDEFINED:+ ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));+ break;+ case JS_TAG_FLOAT64:+ {+ double d = JS_VALUE_GET_FLOAT64(val);+ if (isnan(d)) {+ ret = JS_NewInt32(ctx, 0);+ } else {+ /* convert -0 to +0 */+ d = trunc(d) + 0.0;+ ret = JS_NewFloat64(ctx, d);+ }+ }+ break;+#ifdef CONFIG_BIGNUM+ case JS_TAG_BIG_FLOAT:+ {+ bf_t a_s, *a, r_s, *r = &r_s;+ BOOL is_nan;++ a = JS_ToBigFloat(ctx, &a_s, val);+ if (!bf_is_finite(a)) {+ is_nan = bf_is_nan(a);+ if (is_nan)+ ret = JS_NewInt32(ctx, 0);+ else+ ret = JS_DupValue(ctx, val);+ } else {+ ret = JS_NewBigInt(ctx);+ if (!JS_IsException(ret)) {+ r = JS_GetBigInt(ret);+ bf_set(r, a);+ bf_rint(r, BF_RNDZ);+ ret = JS_CompactBigInt(ctx, ret);+ }+ }+ if (a == &a_s)+ bf_delete(a);+ JS_FreeValue(ctx, val);+ }+ break;+#endif+ default:+ val = JS_ToNumberFree(ctx, val);+ if (JS_IsException(val))+ return val;+ goto redo;+ }+ return ret;+}++/* Note: the integer value is satured to 32 bits */+static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val)+{+ uint32_t tag;+ int ret;++ redo:+ tag = JS_VALUE_GET_NORM_TAG(val);+ switch(tag) {+ case JS_TAG_INT:+ case JS_TAG_BOOL:+ case JS_TAG_NULL:+ case JS_TAG_UNDEFINED:+ ret = JS_VALUE_GET_INT(val);+ break;+ case JS_TAG_EXCEPTION:+ *pres = 0;+ return -1;+ case JS_TAG_FLOAT64:+ {+ double d = JS_VALUE_GET_FLOAT64(val);+ if (isnan(d)) {+ ret = 0;+ } else {+ if (d < INT32_MIN)+ ret = INT32_MIN;+ else if (d > INT32_MAX)+ ret = INT32_MAX;+ else+ ret = (int)d;+ }+ }+ break;+#ifdef CONFIG_BIGNUM+ case JS_TAG_BIG_FLOAT:+ {+ JSBigFloat *p = JS_VALUE_GET_PTR(val);+ bf_get_int32(&ret, &p->num, 0);+ JS_FreeValue(ctx, val);+ }+ break;+#endif+ default:+ val = JS_ToNumberFree(ctx, val);+ if (JS_IsException(val)) {+ *pres = 0;+ return -1;+ }+ goto redo;+ }+ *pres = ret;+ return 0;+}++int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValueConst val)+{+ return JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));+}++int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValueConst val,+ int min, int max, int min_offset)+{+ int res = JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));+ if (res == 0) {+ if (*pres < min) {+ *pres += min_offset;+ if (*pres < min)+ *pres = min;+ } else {+ if (*pres > max)+ *pres = max;+ }+ }+ return res;+}++static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val)+{+ uint32_t tag;++ redo:+ tag = JS_VALUE_GET_NORM_TAG(val);+ switch(tag) {+ case JS_TAG_INT:+ case JS_TAG_BOOL:+ case JS_TAG_NULL:+ case JS_TAG_UNDEFINED:+ *pres = JS_VALUE_GET_INT(val);+ return 0;+ case JS_TAG_EXCEPTION:+ *pres = 0;+ return -1;+ case JS_TAG_FLOAT64:+ {+ double d = JS_VALUE_GET_FLOAT64(val);+ if (isnan(d)) {+ *pres = 0;+ } else {+ if (d < INT64_MIN)+ *pres = INT64_MIN;+ else if (d > INT64_MAX)+ *pres = INT64_MAX;+ else+ *pres = (int64_t)d;+ }+ }+ return 0;+#ifdef CONFIG_BIGNUM+ case JS_TAG_BIG_FLOAT:+ {+ JSBigFloat *p = JS_VALUE_GET_PTR(val);+ bf_get_int64(pres, &p->num, 0);+ JS_FreeValue(ctx, val);+ }+ return 0;+#endif+ default:+ val = JS_ToNumberFree(ctx, val);+ if (JS_IsException(val)) {+ *pres = 0;+ return -1;+ }+ goto redo;+ }+}++int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValueConst val)+{+ return JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));+}++int JS_ToInt64Clamp(JSContext *ctx, int64_t *pres, JSValueConst val,+ int64_t min, int64_t max, int64_t neg_offset)+{+ int res = JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));+ if (res == 0) {+ if (*pres < 0)+ *pres += neg_offset;+ if (*pres < min)+ *pres = min;+ else if (*pres > max)+ *pres = max;+ }+ return res;+}++/* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0)+ in case of exception */+static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val)+{+ uint32_t tag;+ int64_t ret;++ redo:+ tag = JS_VALUE_GET_NORM_TAG(val);+ switch(tag) {+ case JS_TAG_INT:+ case JS_TAG_BOOL:+ case JS_TAG_NULL:+ case JS_TAG_UNDEFINED:+ ret = JS_VALUE_GET_INT(val);+ break;+ case JS_TAG_FLOAT64:+ {+ JSFloat64Union u;+ double d;+ int e;+ d = JS_VALUE_GET_FLOAT64(val);+ u.d = d;+ /* we avoid doing fmod(x, 2^64) */+ e = (u.u64 >> 52) & 0x7ff;+ if (likely(e <= (1023 + 62))) {+ /* fast case */+ ret = (int64_t)d;+ } else if (e <= (1023 + 62 + 53)) {+ uint64_t v;+ /* remainder modulo 2^64 */+ v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);+ ret = v << ((e - 1023) - 52);+ /* take the sign into account */+ if (u.u64 >> 63)+ ret = -ret;+ } else {+ ret = 0; /* also handles NaN and +inf */+ }+ }+ break;+#ifdef CONFIG_BIGNUM+ case JS_TAG_BIG_FLOAT:+ {+ JSBigFloat *p = JS_VALUE_GET_PTR(val);+ bf_get_int64(&ret, &p->num, BF_GET_INT_MOD);+ JS_FreeValue(ctx, val);+ }+ break;+#endif+ default:+ val = JS_ToNumberFree(ctx, val);+ if (JS_IsException(val)) {+ *pres = 0;+ return -1;+ }+ goto redo;+ }+ *pres = ret;+ return 0;+}++int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val)+{+ return JS_ToInt64Free(ctx, pres, JS_DupValue(ctx, val));+}++int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val)+{+ if (JS_IsBigInt(ctx, val))+ return JS_ToBigInt64(ctx, pres, val);+ else+ return JS_ToInt64(ctx, pres, val);+}++/* return (<0, 0) in case of exception */+static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val)+{+ uint32_t tag;+ int32_t ret;++ redo:+ tag = JS_VALUE_GET_NORM_TAG(val);+ switch(tag) {+ case JS_TAG_INT:+ case JS_TAG_BOOL:+ case JS_TAG_NULL:+ case JS_TAG_UNDEFINED:+ ret = JS_VALUE_GET_INT(val);+ break;+ case JS_TAG_FLOAT64:+ {+ JSFloat64Union u;+ double d;+ int e;+ d = JS_VALUE_GET_FLOAT64(val);+ u.d = d;+ /* we avoid doing fmod(x, 2^32) */+ e = (u.u64 >> 52) & 0x7ff;+ if (likely(e <= (1023 + 30))) {+ /* fast case */+ ret = (int32_t)d;+ } else if (e <= (1023 + 30 + 53)) {+ uint64_t v;+ /* remainder modulo 2^32 */+ v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);+ v = v << ((e - 1023) - 52 + 32);+ ret = v >> 32;+ /* take the sign into account */+ if (u.u64 >> 63)+ ret = -ret;+ } else {+ ret = 0; /* also handles NaN and +inf */+ }+ }+ break;+#ifdef CONFIG_BIGNUM+ case JS_TAG_BIG_FLOAT:+ {+ JSBigFloat *p = JS_VALUE_GET_PTR(val);+ bf_get_int32(&ret, &p->num, BF_GET_INT_MOD);+ JS_FreeValue(ctx, val);+ }+ break;+#endif+ default:+ val = JS_ToNumberFree(ctx, val);+ if (JS_IsException(val)) {+ *pres = 0;+ return -1;+ }+ goto redo;+ }+ *pres = ret;+ return 0;+}++int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val)+{+ return JS_ToInt32Free(ctx, pres, JS_DupValue(ctx, val));+}++static inline int JS_ToUint32Free(JSContext *ctx, uint32_t *pres, JSValue val)+{+ return JS_ToInt32Free(ctx, (int32_t *)pres, val);+}++static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val)+{+ uint32_t tag;+ int res;++ redo:+ tag = JS_VALUE_GET_NORM_TAG(val);+ switch(tag) {+ case JS_TAG_INT:+ case JS_TAG_BOOL:+ case JS_TAG_NULL:+ case JS_TAG_UNDEFINED:+ res = JS_VALUE_GET_INT(val);+#ifdef CONFIG_BIGNUM+ int_clamp:+#endif+ res = max_int(0, min_int(255, res));+ break;+ case JS_TAG_FLOAT64:+ {+ double d = JS_VALUE_GET_FLOAT64(val);+ if (isnan(d)) {+ res = 0;+ } else {+ if (d < 0)+ res = 0;+ else if (d > 255)+ res = 255;+ else+ res = lrint(d);+ }+ }+ break;+#ifdef CONFIG_BIGNUM+ case JS_TAG_BIG_FLOAT:+ {+ JSBigFloat *p = JS_VALUE_GET_PTR(val);+ bf_t r_s, *r = &r_s;+ bf_init(ctx->bf_ctx, r);+ bf_set(r, &p->num);+ bf_rint(r, BF_RNDN);+ bf_get_int32(&res, r, 0);+ bf_delete(r);+ JS_FreeValue(ctx, val);+ }+ goto int_clamp;+#endif+ default:+ val = JS_ToNumberFree(ctx, val);+ if (JS_IsException(val)) {+ *pres = 0;+ return -1;+ }+ goto redo;+ }+ *pres = res;+ return 0;+}++static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,+ JSValue val, BOOL is_array_ctor)+{+ uint32_t tag, len;++ tag = JS_VALUE_GET_TAG(val);+ switch(tag) {+ case JS_TAG_INT:+ case JS_TAG_BOOL:+ case JS_TAG_NULL:+ {+ int v;+ v = JS_VALUE_GET_INT(val);+ if (v < 0)+ goto fail;+ len = v;+ }+ break;+#ifdef CONFIG_BIGNUM+ case JS_TAG_BIG_INT:+ case JS_TAG_BIG_FLOAT:+ {+ JSBigFloat *p = JS_VALUE_GET_PTR(val);+ bf_t a;+ BOOL res;+ bf_get_int32((int32_t *)&len, &p->num, BF_GET_INT_MOD);+ bf_init(ctx->bf_ctx, &a);+ bf_set_ui(&a, len);+ res = bf_cmp_eq(&a, &p->num);+ bf_delete(&a);+ JS_FreeValue(ctx, val);+ if (!res)+ goto fail;+ }+ break;+#endif+ default:+ if (JS_TAG_IS_FLOAT64(tag)) {+ double d;+ d = JS_VALUE_GET_FLOAT64(val);+ len = (uint32_t)d;+ if (len != d)+ goto fail;+ } else {+ uint32_t len1;++ if (is_array_ctor) {+ val = JS_ToNumberFree(ctx, val);+ if (JS_IsException(val))+ return -1;+ /* cannot recurse because val is a number */+ if (JS_ToArrayLengthFree(ctx, &len, val, TRUE))+ return -1;+ } else {+ /* legacy behavior: must do the conversion twice and compare */+ if (JS_ToUint32(ctx, &len, val)) {+ JS_FreeValue(ctx, val);+ return -1;+ }+ val = JS_ToNumberFree(ctx, val);+ if (JS_IsException(val))+ return -1;+ /* cannot recurse because val is a number */+ if (JS_ToArrayLengthFree(ctx, &len1, val, FALSE))+ return -1;+ if (len1 != len) {+ fail:+ JS_ThrowRangeError(ctx, "invalid array length");+ return -1;+ }+ }+ }+ break;+ }+ *plen = len;+ return 0;+}++#define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1)++static BOOL is_safe_integer(double d)+{+ return isfinite(d) && floor(d) == d &&+ fabs(d) <= (double)MAX_SAFE_INTEGER;+}++int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val)+{+ int64_t v;+ if (JS_ToInt64Sat(ctx, &v, val))+ return -1;+ if (v < 0 || v > MAX_SAFE_INTEGER) {+ JS_ThrowRangeError(ctx, "invalid array index");+ *plen = 0;+ return -1;+ }+ *plen = v;+ return 0;+}++/* convert a value to a length between 0 and MAX_SAFE_INTEGER.+ return -1 for exception */+static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen,+ JSValue val)+{+ int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0);+ JS_FreeValue(ctx, val);+ return res;+}++/* Note: can return an exception */+static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val)+{+ double d;+ if (!JS_IsNumber(val))+ return FALSE;+ if (unlikely(JS_ToFloat64(ctx, &d, val)))+ return -1;+ return isfinite(d) && floor(d) == d;+}++static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val)+{+ uint32_t tag;++ tag = JS_VALUE_GET_NORM_TAG(val);+ switch(tag) {+ case JS_TAG_INT:+ {+ int v;+ v = JS_VALUE_GET_INT(val);+ return (v < 0);+ }+ case JS_TAG_FLOAT64:+ {+ JSFloat64Union u;+ u.d = JS_VALUE_GET_FLOAT64(val);+ return (u.u64 >> 63);+ }+#ifdef CONFIG_BIGNUM+ case JS_TAG_BIG_INT:+ {+ JSBigFloat *p = JS_VALUE_GET_PTR(val);+ /* Note: integer zeros are not necessarily positive */+ return p->num.sign && !bf_is_zero(&p->num);+ }+ case JS_TAG_BIG_FLOAT:+ {+ JSBigFloat *p = JS_VALUE_GET_PTR(val);+ return p->num.sign;+ }+ break;+ case JS_TAG_BIG_DECIMAL:+ {+ JSBigDecimal *p = JS_VALUE_GET_PTR(val);+ return p->num.sign;+ }+ break;+#endif+ default:+ return FALSE;+ }+}++#ifdef CONFIG_BIGNUM++static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix)+{+ JSValue ret;+ bf_t a_s, *a;+ char *str;+ int saved_sign;++ a = JS_ToBigInt(ctx, &a_s, val);+ if (!a)+ return JS_EXCEPTION;+ saved_sign = a->sign;+ if (a->expn == BF_EXP_ZERO)+ a->sign = 0;+ str = bf_ftoa(NULL, a, radix, 0, BF_RNDZ | BF_FTOA_FORMAT_FRAC |+ BF_FTOA_JS_QUIRKS);+ a->sign = saved_sign;+ JS_FreeBigInt(ctx, a, &a_s);+ if (!str)+ return JS_ThrowOutOfMemory(ctx);+ ret = JS_NewString(ctx, str);+ bf_free(ctx->bf_ctx, str);+ return ret;+}++static JSValue js_bigint_to_string(JSContext *ctx, JSValueConst val)+{+ return js_bigint_to_string1(ctx, val, 10);+}++static JSValue js_ftoa(JSContext *ctx, JSValueConst val1, int radix,+ limb_t prec, bf_flags_t flags)+{+ JSValue val, ret;+ bf_t a_s, *a;+ char *str;+ int saved_sign;++ val = JS_ToNumeric(ctx, val1);+ if (JS_IsException(val))+ return val;+ a = JS_ToBigFloat(ctx, &a_s, val);+ saved_sign = a->sign;+ if (a->expn == BF_EXP_ZERO)+ a->sign = 0;+ flags |= BF_FTOA_JS_QUIRKS;+ if ((flags & BF_FTOA_FORMAT_MASK) == BF_FTOA_FORMAT_FREE_MIN) {+ /* Note: for floating point numbers with a radix which is not+ a power of two, the current precision is used to compute+ the number of digits. */+ if ((radix & (radix - 1)) != 0) {+ bf_t r_s, *r = &r_s;+ int prec, flags1;+ /* must round first */+ if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {+ prec = ctx->fp_env.prec;+ flags1 = ctx->fp_env.flags &+ (BF_FLAG_SUBNORMAL | (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT));+ } else {+ prec = 53;+ flags1 = bf_set_exp_bits(11) | BF_FLAG_SUBNORMAL;+ }+ bf_init(ctx->bf_ctx, r);+ bf_set(r, a);+ bf_round(r, prec, flags1 | BF_RNDN);+ str = bf_ftoa(NULL, r, radix, prec, flags1 | flags);+ bf_delete(r);+ } else {+ str = bf_ftoa(NULL, a, radix, BF_PREC_INF, flags);+ }+ } else {+ str = bf_ftoa(NULL, a, radix, prec, flags);+ }+ a->sign = saved_sign;+ if (a == &a_s)+ bf_delete(a);+ JS_FreeValue(ctx, val);+ if (!str)+ return JS_ThrowOutOfMemory(ctx);+ ret = JS_NewString(ctx, str);+ bf_free(ctx->bf_ctx, str);+ return ret;+}++static JSValue js_bigfloat_to_string(JSContext *ctx, JSValueConst val)+{+ return js_ftoa(ctx, val, 10, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN);+}++static JSValue js_bigdecimal_to_string1(JSContext *ctx, JSValueConst val,+ limb_t prec, int flags)+{+ JSValue ret;+ bfdec_t *a;+ char *str;+ int saved_sign;++ a = JS_ToBigDecimal(ctx, val);+ saved_sign = a->sign;+ if (a->expn == BF_EXP_ZERO)+ a->sign = 0;+ str = bfdec_ftoa(NULL, a, prec, flags | BF_FTOA_JS_QUIRKS);+ a->sign = saved_sign;+ if (!str)+ return JS_ThrowOutOfMemory(ctx);+ ret = JS_NewString(ctx, str);+ bf_free(ctx->bf_ctx, str);+ return ret;+}++static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val)+{+ return js_bigdecimal_to_string1(ctx, val, 0,+ BF_RNDZ | BF_FTOA_FORMAT_FREE);+}++#endif /* CONFIG_BIGNUM */++/* 2 <= base <= 36 */+static char *i64toa(char *buf_end, int64_t n, unsigned int base)+{+ char *q = buf_end;+ int digit, is_neg;++ is_neg = 0;+ if (n < 0) {+ is_neg = 1;+ n = -n;+ }+ *--q = '\0';+ do {+ digit = (uint64_t)n % base;+ n = (uint64_t)n / base;+ if (digit < 10)+ digit += '0';+ else+ digit += 'a' - 10;+ *--q = digit;+ } while (n != 0);+ if (is_neg)+ *--q = '-';+ return q;+}++/* buf1 contains the printf result */+static void js_ecvt1(double d, int n_digits, int *decpt, int *sign, char *buf,+ int rounding_mode, char *buf1, int buf1_size)+{+ if (rounding_mode != FE_TONEAREST)+ fesetround(rounding_mode);+ snprintf(buf1, buf1_size, "%+.*e", n_digits - 1, d);+ if (rounding_mode != FE_TONEAREST)+ fesetround(FE_TONEAREST);+ *sign = (buf1[0] == '-');+ /* mantissa */+ buf[0] = buf1[1];+ if (n_digits > 1)+ memcpy(buf + 1, buf1 + 3, n_digits - 1);+ buf[n_digits] = '\0';+ /* exponent */+ *decpt = atoi(buf1 + n_digits + 2 + (n_digits > 1)) + 1;+}++/* maximum buffer size for js_dtoa */+#define JS_DTOA_BUF_SIZE 128++/* needed because ecvt usually limits the number of digits to+ 17. Return the number of digits. */+static int js_ecvt(double d, int n_digits, int *decpt, int *sign, char *buf,+ BOOL is_fixed)+{+ int rounding_mode;+ char buf_tmp[JS_DTOA_BUF_SIZE];++ if (!is_fixed) {+ unsigned int n_digits_min, n_digits_max;+ /* find the minimum amount of digits (XXX: inefficient but simple) */+ n_digits_min = 1;+ n_digits_max = 17;+ while (n_digits_min < n_digits_max) {+ n_digits = (n_digits_min + n_digits_max) / 2;+ js_ecvt1(d, n_digits, decpt, sign, buf, FE_TONEAREST,+ buf_tmp, sizeof(buf_tmp));+ if (strtod(buf_tmp, NULL) == d) {+ /* no need to keep the trailing zeros */+ while (n_digits >= 2 && buf[n_digits - 1] == '0')+ n_digits--;+ n_digits_max = n_digits;+ } else {+ n_digits_min = n_digits + 1;+ }+ }+ n_digits = n_digits_max;+ rounding_mode = FE_TONEAREST;+ } else {+ rounding_mode = FE_TONEAREST;+#ifdef CONFIG_PRINTF_RNDN+ {+ char buf1[JS_DTOA_BUF_SIZE], buf2[JS_DTOA_BUF_SIZE];+ int decpt1, sign1, decpt2, sign2;+ /* The JS rounding is specified as round to nearest ties away+ from zero (RNDNA), but in printf the "ties" case is not+ specified (for example it is RNDN for glibc, RNDNA for+ Windows), so we must round manually. */+ js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_TONEAREST,+ buf_tmp, sizeof(buf_tmp));+ /* XXX: could use 2 digits to reduce the average running time */+ if (buf1[n_digits] == '5') {+ js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_DOWNWARD,+ buf_tmp, sizeof(buf_tmp));+ js_ecvt1(d, n_digits + 1, &decpt2, &sign2, buf2, FE_UPWARD,+ buf_tmp, sizeof(buf_tmp));+ if (memcmp(buf1, buf2, n_digits + 1) == 0 && decpt1 == decpt2) {+ /* exact result: round away from zero */+ if (sign1)+ rounding_mode = FE_DOWNWARD;+ else+ rounding_mode = FE_UPWARD;+ }+ }+ }+#endif /* CONFIG_PRINTF_RNDN */+ }+ js_ecvt1(d, n_digits, decpt, sign, buf, rounding_mode,+ buf_tmp, sizeof(buf_tmp));+ return n_digits;+}++static int js_fcvt1(char *buf, int buf_size, double d, int n_digits,+ int rounding_mode)+{+ int n;+ if (rounding_mode != FE_TONEAREST)+ fesetround(rounding_mode);+ n = snprintf(buf, buf_size, "%.*f", n_digits, d);+ if (rounding_mode != FE_TONEAREST)+ fesetround(FE_TONEAREST);+ assert(n < buf_size);+ return n;+}++static void js_fcvt(char *buf, int buf_size, double d, int n_digits)+{+ int rounding_mode;+ rounding_mode = FE_TONEAREST;+#ifdef CONFIG_PRINTF_RNDN+ {+ int n1, n2;+ char buf1[JS_DTOA_BUF_SIZE];+ char buf2[JS_DTOA_BUF_SIZE];++ /* The JS rounding is specified as round to nearest ties away from+ zero (RNDNA), but in printf the "ties" case is not specified+ (for example it is RNDN for glibc, RNDNA for Windows), so we+ must round manually. */+ n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_TONEAREST);+ rounding_mode = FE_TONEAREST;+ /* XXX: could use 2 digits to reduce the average running time */+ if (buf1[n1 - 1] == '5') {+ n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_DOWNWARD);+ n2 = js_fcvt1(buf2, sizeof(buf2), d, n_digits + 1, FE_UPWARD);+ if (n1 == n2 && memcmp(buf1, buf2, n1) == 0) {+ /* exact result: round away from zero */+ if (buf1[0] == '-')+ rounding_mode = FE_DOWNWARD;+ else+ rounding_mode = FE_UPWARD;+ }+ }+ }+#endif /* CONFIG_PRINTF_RNDN */+ js_fcvt1(buf, buf_size, d, n_digits, rounding_mode);+}++/* radix != 10 is only supported with flags = JS_DTOA_VAR_FORMAT */+/* use as many digits as necessary */+#define JS_DTOA_VAR_FORMAT (0 << 0)+/* use n_digits significant digits (1 <= n_digits <= 101) */+#define JS_DTOA_FIXED_FORMAT (1 << 0)+/* force fractional format: [-]dd.dd with n_digits fractional digits */+#define JS_DTOA_FRAC_FORMAT (2 << 0)+/* force exponential notation either in fixed or variable format */+#define JS_DTOA_FORCE_EXP (1 << 2)++/* XXX: slow and maybe not fully correct. Use libbf when it is fast enough.+ XXX: radix != 10 is only supported for small integers+*/+static void js_dtoa1(char *buf, double d, int radix, int n_digits, int flags)+{+ char *q;++ if (!isfinite(d)) {+ if (isnan(d)) {+ strcpy(buf, "NaN");+ } else {+ q = buf;+ if (d < 0)+ *q++ = '-';+ strcpy(q, "Infinity");+ }+ } else if (flags == JS_DTOA_VAR_FORMAT) {+ int64_t i64;+ char buf1[70], *ptr;+ i64 = (int64_t)d;+ if (d != i64 || i64 > MAX_SAFE_INTEGER || i64 < -MAX_SAFE_INTEGER)+ goto generic_conv;+ /* fast path for integers */+ ptr = i64toa(buf1 + sizeof(buf1), i64, radix);+ strcpy(buf, ptr);+ } else {+ if (d == 0.0)+ d = 0.0; /* convert -0 to 0 */+ if (flags == JS_DTOA_FRAC_FORMAT) {+ js_fcvt(buf, JS_DTOA_BUF_SIZE, d, n_digits);+ } else {+ char buf1[JS_DTOA_BUF_SIZE];+ int sign, decpt, k, n, i, p, n_max;+ BOOL is_fixed;+ generic_conv:+ is_fixed = ((flags & 3) == JS_DTOA_FIXED_FORMAT);+ if (is_fixed) {+ n_max = n_digits;+ } else {+ n_max = 21;+ }+ /* the number has k digits (k >= 1) */+ k = js_ecvt(d, n_digits, &decpt, &sign, buf1, is_fixed);+ n = decpt; /* d=10^(n-k)*(buf1) i.e. d= < x.yyyy 10^(n-1) */+ q = buf;+ if (sign)+ *q++ = '-';+ if (flags & JS_DTOA_FORCE_EXP)+ goto force_exp;+ if (n >= 1 && n <= n_max) {+ if (k <= n) {+ memcpy(q, buf1, k);+ q += k;+ for(i = 0; i < (n - k); i++)+ *q++ = '0';+ *q = '\0';+ } else {+ /* k > n */+ memcpy(q, buf1, n);+ q += n;+ *q++ = '.';+ for(i = 0; i < (k - n); i++)+ *q++ = buf1[n + i];+ *q = '\0';+ }+ } else if (n >= -5 && n <= 0) {+ *q++ = '0';+ *q++ = '.';+ for(i = 0; i < -n; i++)+ *q++ = '0';+ memcpy(q, buf1, k);+ q += k;+ *q = '\0';+ } else {+ force_exp:+ /* exponential notation */+ *q++ = buf1[0];+ if (k > 1) {+ *q++ = '.';+ for(i = 1; i < k; i++)+ *q++ = buf1[i];+ }+ *q++ = 'e';+ p = n - 1;+ if (p >= 0)+ *q++ = '+';+ sprintf(q, "%d", p);+ }+ }+ }+}++static JSValue js_dtoa(JSContext *ctx,+ double d, int radix, int n_digits, int flags)+{+ char buf[JS_DTOA_BUF_SIZE];+ js_dtoa1(buf, d, radix, n_digits, flags);+ return JS_NewString(ctx, buf);+}++JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToPropertyKey)+{+ uint32_t tag;+ const char *str;+ char buf[32];++ tag = JS_VALUE_GET_NORM_TAG(val);+ switch(tag) {+ case JS_TAG_STRING:+ return JS_DupValue(ctx, val);+ case JS_TAG_INT:+ snprintf(buf, sizeof(buf), "%d", JS_VALUE_GET_INT(val));+ str = buf;+ goto new_string;+ case JS_TAG_BOOL:+ return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ?+ JS_ATOM_true : JS_ATOM_false);+ case JS_TAG_NULL:+ return JS_AtomToString(ctx, JS_ATOM_null);+ case JS_TAG_UNDEFINED:+ return JS_AtomToString(ctx, JS_ATOM_undefined);+ case JS_TAG_EXCEPTION:+ return JS_EXCEPTION;+ case JS_TAG_OBJECT:+ {+ JSValue val1, ret;+ val1 = JS_ToPrimitive(ctx, val, HINT_STRING);+ if (JS_IsException(val1))+ return val1;+ ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey);+ JS_FreeValue(ctx, val1);+ return ret;+ }+ break;+ case JS_TAG_FUNCTION_BYTECODE:+ str = "[function bytecode]";+ goto new_string;+ case JS_TAG_SYMBOL:+ if (is_ToPropertyKey) {+ return JS_DupValue(ctx, val);+ } else {+ return JS_ThrowTypeError(ctx, "cannot convert symbol to string");+ }+ case JS_TAG_FLOAT64:+ return js_dtoa(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0,+ JS_DTOA_VAR_FORMAT);+#ifdef CONFIG_BIGNUM+ case JS_TAG_BIG_INT:+ return ctx->rt->bigint_ops.to_string(ctx, val);+ case JS_TAG_BIG_FLOAT:+ return ctx->rt->bigfloat_ops.to_string(ctx, val);+ case JS_TAG_BIG_DECIMAL:+ return ctx->rt->bigdecimal_ops.to_string(ctx, val);+#endif+ default:+ str = "[unsupported type]";+ new_string:+ return JS_NewString(ctx, str);+ }+}++JSValue JS_ToString(JSContext *ctx, JSValueConst val)+{+ return JS_ToStringInternal(ctx, val, FALSE);+}++static JSValue JS_ToStringFree(JSContext *ctx, JSValue val)+{+ JSValue ret;+ ret = JS_ToString(ctx, val);+ JS_FreeValue(ctx, val);+ return ret;+}++static JSValue JS_ToLocaleStringFree(JSContext *ctx, JSValue val)+{+ if (JS_IsUndefined(val) || JS_IsNull(val))+ return JS_ToStringFree(ctx, val);+ return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL);+}++JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val)+{+ return JS_ToStringInternal(ctx, val, TRUE);+}++static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValueConst val)+{+ uint32_t tag = JS_VALUE_GET_TAG(val);+ if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)+ return JS_ThrowTypeError(ctx, "null or undefined are forbidden");+ return JS_ToString(ctx, val);+}++static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1)+{+ JSValue val;+ JSString *p;+ int i;+ uint32_t c;+ StringBuffer b_s, *b = &b_s;+ char buf[16];++ val = JS_ToStringCheckObject(ctx, val1);+ if (JS_IsException(val))+ return val;+ p = JS_VALUE_GET_STRING(val);++ if (string_buffer_init(ctx, b, p->len + 2))+ goto fail;++ if (string_buffer_putc8(b, '\"'))+ goto fail;+ for(i = 0; i < p->len; ) {+ c = string_getc(p, &i);+ switch(c) {+ case '\t':+ c = 't';+ goto quote;+ case '\r':+ c = 'r';+ goto quote;+ case '\n':+ c = 'n';+ goto quote;+ case '\b':+ c = 'b';+ goto quote;+ case '\f':+ c = 'f';+ goto quote;+ case '\"':+ case '\\':+ quote:+ if (string_buffer_putc8(b, '\\'))+ goto fail;+ if (string_buffer_putc8(b, c))+ goto fail;+ break;+ default:+ if (c < 32 || (c >= 0xd800 && c < 0xe000)) {+ snprintf(buf, sizeof(buf), "\\u%04x", c);+ if (string_buffer_puts8(b, buf))+ goto fail;+ } else {+ if (string_buffer_putc(b, c))+ goto fail;+ }+ break;+ }+ }+ if (string_buffer_putc8(b, '\"'))+ goto fail;+ JS_FreeValue(ctx, val);+ return string_buffer_end(b);+ fail:+ JS_FreeValue(ctx, val);+ string_buffer_free(b);+ return JS_EXCEPTION;+}++static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt)+{+ printf("%14s %4s %4s %14s %10s %s\n",+ "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS");+}++/* for debug only: dump an object without side effect */+static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)+{+ uint32_t i;+ char atom_buf[ATOM_GET_STR_BUF_SIZE];+ JSShape *sh;+ JSShapeProperty *prs;+ JSProperty *pr;+ BOOL is_first = TRUE;++ /* XXX: should encode atoms with special characters */+ sh = p->shape; /* the shape can be NULL while freeing an object */+ printf("%14p %4d ",+ (void *)p,+ p->header.ref_count);+ if (sh) {+ printf("%3d%c %14p ",+ sh->header.ref_count,+ " *"[sh->is_hashed],+ (void *)sh->proto);+ } else {+ printf("%3s %14s ", "-", "-");+ }+ printf("%10s ",+ JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), rt->class_array[p->class_id].class_name));+ if (p->is_exotic && p->fast_array) {+ printf("[ ");+ for(i = 0; i < p->u.array.count; i++) {+ if (i != 0)+ printf(", ");+ switch (p->class_id) {+ case JS_CLASS_ARRAY:+ case JS_CLASS_ARGUMENTS:+ JS_DumpValueShort(rt, p->u.array.u.values[i]);+ break;+ case JS_CLASS_UINT8C_ARRAY:+ case JS_CLASS_INT8_ARRAY:+ case JS_CLASS_UINT8_ARRAY:+ case JS_CLASS_INT16_ARRAY:+ case JS_CLASS_UINT16_ARRAY:+ case JS_CLASS_INT32_ARRAY:+ case JS_CLASS_UINT32_ARRAY:+#ifdef CONFIG_BIGNUM+ case JS_CLASS_BIG_INT64_ARRAY:+ case JS_CLASS_BIG_UINT64_ARRAY:+#endif+ case JS_CLASS_FLOAT32_ARRAY:+ case JS_CLASS_FLOAT64_ARRAY:+ {+ int size = 1 << typed_array_size_log2(p->class_id);+ const uint8_t *b = p->u.array.u.uint8_ptr + i * size;+ while (size-- > 0)+ printf("%02X", *b++);+ }+ break;+ }+ }+ printf(" ] ");+ }++ if (sh) {+ printf("{ ");+ for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {+ if (prs->atom != JS_ATOM_NULL) {+ pr = &p->prop[i];+ if (!is_first)+ printf(", ");+ printf("%s: ",+ JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), prs->atom));+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {+ printf("[getset %p %p]", (void *)pr->u.getset.getter,+ (void *)pr->u.getset.setter);+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {+ printf("[varref %p]", (void *)pr->u.var_ref);+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {+ printf("[autoinit %p %d %p]",+ (void *)js_autoinit_get_realm(pr),+ js_autoinit_get_id(pr),+ (void *)pr->u.init.opaque);+ } else {+ JS_DumpValueShort(rt, pr->u.value);+ }+ is_first = FALSE;+ }+ }+ printf(" }");+ }+ + if (js_class_has_bytecode(p->class_id)) {+ JSFunctionBytecode *b = p->u.func.function_bytecode;+ JSVarRef **var_refs;+ if (b->closure_var_count) {+ var_refs = p->u.func.var_refs;+ printf(" Closure:");+ for(i = 0; i < b->closure_var_count; i++) {+ printf(" ");+ JS_DumpValueShort(rt, var_refs[i]->value);+ }+ if (p->u.func.home_object) {+ printf(" HomeObject: ");+ JS_DumpValueShort(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object));+ }+ }+ }+ printf("\n");+}++static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p)+{+ if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {+ JS_DumpObject(rt, (JSObject *)p);+ } else {+ printf("%14p %4d ",+ (void *)p,+ p->ref_count);+ switch(p->gc_obj_type) {+ case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:+ printf("[function bytecode]");+ break;+ case JS_GC_OBJ_TYPE_SHAPE:+ printf("[shape]");+ break;+ case JS_GC_OBJ_TYPE_VAR_REF:+ printf("[var_ref]");+ break;+ case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:+ printf("[async_function]");+ break;+ case JS_GC_OBJ_TYPE_JS_CONTEXT:+ printf("[js_context]");+ break;+ default:+ printf("[unknown %d]", p->gc_obj_type);+ break;+ }+ printf("\n");+ }+}++static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,+ JSValueConst val)+{+ uint32_t tag = JS_VALUE_GET_NORM_TAG(val);+ const char *str;++ switch(tag) {+ case JS_TAG_INT:+ printf("%d", JS_VALUE_GET_INT(val));+ break;+ case JS_TAG_BOOL:+ if (JS_VALUE_GET_BOOL(val))+ str = "true";+ else+ str = "false";+ goto print_str;+ case JS_TAG_NULL:+ str = "null";+ goto print_str;+ case JS_TAG_EXCEPTION:+ str = "exception";+ goto print_str;+ case JS_TAG_UNINITIALIZED:+ str = "uninitialized";+ goto print_str;+ case JS_TAG_UNDEFINED:+ str = "undefined";+ print_str:+ printf("%s", str);+ break;+ case JS_TAG_FLOAT64:+ printf("%.14g", JS_VALUE_GET_FLOAT64(val));+ break;+#ifdef CONFIG_BIGNUM+ case JS_TAG_BIG_INT:+ {+ JSBigFloat *p = JS_VALUE_GET_PTR(val);+ char *str;+ str = bf_ftoa(NULL, &p->num, 10, 0,+ BF_RNDZ | BF_FTOA_FORMAT_FRAC);+ printf("%sn", str);+ bf_realloc(&rt->bf_ctx, str, 0);+ }+ break;+ case JS_TAG_BIG_FLOAT:+ {+ JSBigFloat *p = JS_VALUE_GET_PTR(val);+ char *str;+ str = bf_ftoa(NULL, &p->num, 16, BF_PREC_INF,+ BF_RNDZ | BF_FTOA_FORMAT_FREE | BF_FTOA_ADD_PREFIX);+ printf("%sl", str);+ bf_free(&rt->bf_ctx, str);+ }+ break;+ case JS_TAG_BIG_DECIMAL:+ {+ JSBigDecimal *p = JS_VALUE_GET_PTR(val);+ char *str;+ str = bfdec_ftoa(NULL, &p->num, BF_PREC_INF,+ BF_RNDZ | BF_FTOA_FORMAT_FREE);+ printf("%sm", str);+ bf_free(&rt->bf_ctx, str);+ }+ break;+#endif+ case JS_TAG_STRING:+ {+ JSString *p;+ p = JS_VALUE_GET_STRING(val);+ JS_DumpString(rt, p);+ }+ break;+ case JS_TAG_FUNCTION_BYTECODE:+ {+ JSFunctionBytecode *b = JS_VALUE_GET_PTR(val);+ char buf[ATOM_GET_STR_BUF_SIZE];+ printf("[bytecode %s]", JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));+ }+ break;+ case JS_TAG_OBJECT:+ {+ JSObject *p = JS_VALUE_GET_OBJ(val);+ JSAtom atom = rt->class_array[p->class_id].class_name;+ char atom_buf[ATOM_GET_STR_BUF_SIZE];+ printf("[%s %p]",+ JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), atom), (void *)p);+ }+ break;+ case JS_TAG_SYMBOL:+ {+ JSAtomStruct *p = JS_VALUE_GET_PTR(val);+ char atom_buf[ATOM_GET_STR_BUF_SIZE];+ printf("Symbol(%s)",+ JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), js_get_atom_index(rt, p)));+ }+ break;+ case JS_TAG_MODULE:+ printf("[module]");+ break;+ default:+ printf("[unknown tag %d]", tag);+ break;+ }+}++static __maybe_unused void JS_DumpValue(JSContext *ctx,+ JSValueConst val)+{+ JS_DumpValueShort(ctx->rt, val);+}++static __maybe_unused void JS_PrintValue(JSContext *ctx,+ const char *str,+ JSValueConst val)+{+ printf("%s=", str);+ JS_DumpValueShort(ctx->rt, val);+ printf("\n");+}++/* return -1 if exception (proxy case) or TRUE/FALSE */+int JS_IsArray(JSContext *ctx, JSValueConst val)+{+ JSObject *p;+ if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) {+ p = JS_VALUE_GET_OBJ(val);+ if (unlikely(p->class_id == JS_CLASS_PROXY))+ return js_proxy_isArray(ctx, val);+ else+ return p->class_id == JS_CLASS_ARRAY;+ } else {+ return FALSE;+ }+}++static double js_pow(double a, double b)+{+ if (unlikely(!isfinite(b)) && fabs(a) == 1) {+ /* not compatible with IEEE 754 */+ return JS_FLOAT64_NAN;+ } else {+ return pow(a, b);+ }+}++#ifdef CONFIG_BIGNUM++JSValue JS_NewBigInt64_1(JSContext *ctx, int64_t v)+{+ JSValue val;+ bf_t *a;+ val = JS_NewBigInt(ctx);+ if (JS_IsException(val))+ return val;+ a = JS_GetBigInt(val);+ if (bf_set_si(a, v)) {+ JS_FreeValue(ctx, val);+ return JS_ThrowOutOfMemory(ctx);+ }+ return val;+}++JSValue JS_NewBigInt64(JSContext *ctx, int64_t v)+{+ if (is_math_mode(ctx) &&+ v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) {+ return JS_NewInt64(ctx, v);+ } else {+ return JS_NewBigInt64_1(ctx, v);+ }+}++JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v)+{+ JSValue val;+ if (is_math_mode(ctx) && v <= MAX_SAFE_INTEGER) {+ val = JS_NewInt64(ctx, v);+ } else {+ bf_t *a;+ val = JS_NewBigInt(ctx);+ if (JS_IsException(val))+ return val;+ a = JS_GetBigInt(val);+ if (bf_set_ui(a, v)) {+ JS_FreeValue(ctx, val);+ return JS_ThrowOutOfMemory(ctx);+ }+ }+ return val;+}++/* if the returned bigfloat is allocated it is equal to+ 'buf'. Otherwise it is a pointer to the bigfloat in 'val'. Return+ NULL in case of error. */+static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val)+{+ uint32_t tag;+ bf_t *r;+ JSBigFloat *p;++ tag = JS_VALUE_GET_NORM_TAG(val);+ switch(tag) {+ case JS_TAG_INT:+ case JS_TAG_BOOL:+ case JS_TAG_NULL:+ r = buf;+ bf_init(ctx->bf_ctx, r);+ if (bf_set_si(r, JS_VALUE_GET_INT(val)))+ goto fail;+ break;+ case JS_TAG_FLOAT64:+ r = buf;+ bf_init(ctx->bf_ctx, r);+ if (bf_set_float64(r, JS_VALUE_GET_FLOAT64(val))) {+ fail:+ bf_delete(r);+ return NULL;+ }+ break;+ case JS_TAG_BIG_INT:+ case JS_TAG_BIG_FLOAT:+ p = JS_VALUE_GET_PTR(val);+ r = &p->num;+ break;+ case JS_TAG_UNDEFINED:+ default:+ r = buf;+ bf_init(ctx->bf_ctx, r);+ bf_set_nan(r);+ break;+ }+ return r;+}++/* return NULL if invalid type */+static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val)+{+ uint32_t tag;+ JSBigDecimal *p;+ bfdec_t *r;+ + tag = JS_VALUE_GET_NORM_TAG(val);+ switch(tag) {+ case JS_TAG_BIG_DECIMAL:+ p = JS_VALUE_GET_PTR(val);+ r = &p->num;+ break;+ default:+ JS_ThrowTypeError(ctx, "bigdecimal expected");+ r = NULL;+ break;+ }+ return r;+}++/* return NaN if bad bigint literal */+static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val)+{+ const char *str, *p;+ size_t len;+ int flags;+ + str = JS_ToCStringLen(ctx, &len, val);+ JS_FreeValue(ctx, val);+ if (!str)+ return JS_EXCEPTION;+ p = str;+ p += skip_spaces(p);+ if ((p - str) == len) {+ val = JS_NewBigInt64(ctx, 0);+ } else {+ flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT;+ if (is_math_mode(ctx))+ flags |= ATOD_MODE_BIGINT;+ val = js_atof(ctx, p, &p, 0, flags);+ p += skip_spaces(p);+ if (!JS_IsException(val)) {+ if ((p - str) != len) {+ JS_FreeValue(ctx, val);+ val = JS_NAN;+ }+ }+ }+ JS_FreeCString(ctx, str);+ return val;+}++static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val)+{+ val = JS_StringToBigInt(ctx, val);+ if (JS_VALUE_IS_NAN(val))+ return JS_ThrowSyntaxError(ctx, "invalid bigint literal");+ return val;+}++/* if the returned bigfloat is allocated it is equal to+ 'buf'. Otherwise it is a pointer to the bigfloat in 'val'. */+static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val)+{+ uint32_t tag;+ bf_t *r;+ JSBigFloat *p;++ redo:+ tag = JS_VALUE_GET_NORM_TAG(val);+ switch(tag) {+ case JS_TAG_INT:+ case JS_TAG_NULL:+ case JS_TAG_UNDEFINED:+ if (!is_math_mode(ctx))+ goto fail;+ /* fall tru */+ case JS_TAG_BOOL:+ r = buf;+ bf_init(ctx->bf_ctx, r);+ bf_set_si(r, JS_VALUE_GET_INT(val));+ break;+ case JS_TAG_FLOAT64:+ {+ double d = JS_VALUE_GET_FLOAT64(val);+ if (!is_math_mode(ctx))+ goto fail;+ if (!isfinite(d))+ goto fail;+ r = buf;+ bf_init(ctx->bf_ctx, r);+ d = trunc(d);+ bf_set_float64(r, d);+ }+ break;+ case JS_TAG_BIG_INT:+ p = JS_VALUE_GET_PTR(val);+ r = &p->num;+ break;+ case JS_TAG_BIG_FLOAT:+ if (!is_math_mode(ctx))+ goto fail;+ p = JS_VALUE_GET_PTR(val);+ if (!bf_is_finite(&p->num))+ goto fail;+ r = buf;+ bf_init(ctx->bf_ctx, r);+ bf_set(r, &p->num);+ bf_rint(r, BF_RNDZ);+ JS_FreeValue(ctx, val);+ break;+ case JS_TAG_STRING:+ val = JS_StringToBigIntErr(ctx, val);+ if (JS_IsException(val))+ return NULL;+ goto redo;+ case JS_TAG_OBJECT:+ val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);+ if (JS_IsException(val))+ return NULL;+ goto redo;+ default:+ fail:+ JS_FreeValue(ctx, val);+ JS_ThrowTypeError(ctx, "cannot convert to bigint");+ return NULL;+ }+ return r;+}++static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val)+{+ return JS_ToBigIntFree(ctx, buf, JS_DupValue(ctx, val));+}++static __maybe_unused JSValue JS_ToBigIntValueFree(JSContext *ctx, JSValue val)+{+ if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT) {+ return val;+ } else {+ bf_t a_s, *a, *r;+ int ret;+ JSValue res; ++ res = JS_NewBigInt(ctx);+ if (JS_IsException(res))+ return JS_EXCEPTION;+ a = JS_ToBigIntFree(ctx, &a_s, val);+ if (!a) {+ JS_FreeValue(ctx, res);+ return JS_EXCEPTION;+ }+ r = JS_GetBigInt(res);+ ret = bf_set(r, a);+ JS_FreeBigInt(ctx, a, &a_s);+ if (ret) {+ JS_FreeValue(ctx, res);+ return JS_ThrowOutOfMemory(ctx);+ }+ return JS_CompactBigInt(ctx, res);+ }+}++/* free the bf_t allocated by JS_ToBigInt */+static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf)+{+ if (a == buf) {+ bf_delete(a);+ } else {+ JSBigFloat *p = (JSBigFloat *)((uint8_t *)a -+ offsetof(JSBigFloat, num));+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_BIG_FLOAT, p));+ }+}++/* XXX: merge with JS_ToInt64Free with a specific flag */+static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val)+{+ bf_t a_s, *a;++ a = JS_ToBigIntFree(ctx, &a_s, val);+ if (!a) {+ *pres = 0;+ return -1;+ }+ bf_get_int64(pres, a, BF_GET_INT_MOD);+ JS_FreeBigInt(ctx, a, &a_s);+ return 0;+}++int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)+{+ return JS_ToBigInt64Free(ctx, pres, JS_DupValue(ctx, val));+}++static JSBigFloat *js_new_bf(JSContext *ctx)+{+ JSBigFloat *p;+ p = js_malloc(ctx, sizeof(*p));+ if (!p)+ return NULL;+ p->header.ref_count = 1;+ bf_init(ctx->bf_ctx, &p->num);+ return p;+}++static JSValue JS_NewBigFloat(JSContext *ctx)+{+ JSBigFloat *p;+ p = js_malloc(ctx, sizeof(*p));+ if (!p)+ return JS_EXCEPTION;+ p->header.ref_count = 1;+ bf_init(ctx->bf_ctx, &p->num);+ return JS_MKPTR(JS_TAG_BIG_FLOAT, p);+}++static JSValue JS_NewBigDecimal(JSContext *ctx)+{+ JSBigDecimal *p;+ p = js_malloc(ctx, sizeof(*p));+ if (!p)+ return JS_EXCEPTION;+ p->header.ref_count = 1;+ bfdec_init(ctx->bf_ctx, &p->num);+ return JS_MKPTR(JS_TAG_BIG_DECIMAL, p);+}++static JSValue JS_NewBigInt(JSContext *ctx)+{+ JSBigFloat *p;+ p = js_malloc(ctx, sizeof(*p));+ if (!p)+ return JS_EXCEPTION;+ p->header.ref_count = 1;+ bf_init(ctx->bf_ctx, &p->num);+ return JS_MKPTR(JS_TAG_BIG_INT, p);+}++static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val,+ BOOL convert_to_safe_integer)+{+ int64_t v;+ bf_t *a;+ + if (JS_VALUE_GET_TAG(val) != JS_TAG_BIG_INT)+ return val; /* fail safe */+ a = JS_GetBigInt(val);+ if (convert_to_safe_integer && bf_get_int64(&v, a, 0) == 0 &&+ v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) {+ JS_FreeValue(ctx, val);+ return JS_NewInt64(ctx, v);+ } else if (a->expn == BF_EXP_ZERO && a->sign) {+ JSBigFloat *p = JS_VALUE_GET_PTR(val);+ assert(p->header.ref_count == 1);+ a->sign = 0;+ }+ return val;+}++/* Convert the big int to a safe integer if in math mode. normalize+ the zero representation. Could also be used to convert the bigint+ to a short bigint value. The reference count of the value must be+ 1. Cannot fail */+static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val)+{+ return JS_CompactBigInt1(ctx, val, is_math_mode(ctx));+}++/* must be kept in sync with JSOverloadableOperatorEnum */+/* XXX: use atoms ? */+static const char js_overloadable_operator_names[JS_OVOP_COUNT][4] = {+ "+",+ "-",+ "*",+ "/",+ "%",+ "**",+ "|",+ "&",+ "^",+ "<<",+ ">>",+ ">>>",+ "==",+ "<",+ "pos",+ "neg",+ "++",+ "--",+ "~",+};++static int get_ovop_from_opcode(OPCodeEnum op)+{+ switch(op) {+ case OP_add:+ return JS_OVOP_ADD;+ case OP_sub:+ return JS_OVOP_SUB;+ case OP_mul:+ return JS_OVOP_MUL;+ case OP_div:+ return JS_OVOP_DIV;+ case OP_mod:+ case OP_math_mod:+ return JS_OVOP_MOD;+ case OP_pow:+ return JS_OVOP_POW;+ case OP_or:+ return JS_OVOP_OR;+ case OP_and:+ return JS_OVOP_AND;+ case OP_xor:+ return JS_OVOP_XOR;+ case OP_shl:+ return JS_OVOP_SHL;+ case OP_sar:+ return JS_OVOP_SAR;+ case OP_shr:+ return JS_OVOP_SHR;+ case OP_eq:+ case OP_neq:+ return JS_OVOP_EQ;+ case OP_lt:+ case OP_lte:+ case OP_gt:+ case OP_gte:+ return JS_OVOP_LESS;+ case OP_plus:+ return JS_OVOP_POS;+ case OP_neg:+ return JS_OVOP_NEG;+ case OP_inc:+ return JS_OVOP_INC;+ case OP_dec:+ return JS_OVOP_DEC;+ default:+ abort();+ }+}++/* return NULL if not present */+static JSObject *find_binary_op(JSBinaryOperatorDef *def,+ uint32_t operator_index,+ JSOverloadableOperatorEnum op)+{+ JSBinaryOperatorDefEntry *ent;+ int i;+ for(i = 0; i < def->count; i++) {+ ent = &def->tab[i];+ if (ent->operator_index == operator_index)+ return ent->ops[op];+ }+ return NULL;+}++/* return -1 if exception, 0 if no operator overloading, 1 if+ overloaded operator called */+static __exception int js_call_binary_op_fallback(JSContext *ctx,+ JSValue *pret,+ JSValueConst op1,+ JSValueConst op2,+ OPCodeEnum op,+ BOOL is_numeric,+ int hint)+{+ JSValue opset1_obj, opset2_obj, method, ret, new_op1, new_op2;+ JSOperatorSetData *opset1, *opset2;+ JSOverloadableOperatorEnum ovop;+ JSObject *p;+ JSValueConst args[2];+ + if (!ctx->allow_operator_overloading)+ return 0;+ + opset2_obj = JS_UNDEFINED;+ opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet);+ if (JS_IsException(opset1_obj))+ goto exception;+ if (JS_IsUndefined(opset1_obj))+ return 0;+ opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);+ if (!opset1)+ goto exception;++ opset2_obj = JS_GetProperty(ctx, op2, JS_ATOM_Symbol_operatorSet);+ if (JS_IsException(opset2_obj))+ goto exception;+ if (JS_IsUndefined(opset2_obj)) {+ JS_FreeValue(ctx, opset1_obj);+ return 0;+ }+ opset2 = JS_GetOpaque2(ctx, opset2_obj, JS_CLASS_OPERATOR_SET);+ if (!opset2)+ goto exception;++ if (opset1->is_primitive && opset2->is_primitive) {+ JS_FreeValue(ctx, opset1_obj);+ JS_FreeValue(ctx, opset2_obj);+ return 0;+ }++ ovop = get_ovop_from_opcode(op);+ + if (opset1->operator_counter == opset2->operator_counter) {+ p = opset1->self_ops[ovop];+ } else if (opset1->operator_counter > opset2->operator_counter) {+ p = find_binary_op(&opset1->left, opset2->operator_counter, ovop);+ } else {+ p = find_binary_op(&opset2->right, opset1->operator_counter, ovop);+ }+ if (!p) {+ JS_ThrowTypeError(ctx, "operator %s: no function defined",+ js_overloadable_operator_names[ovop]);+ goto exception;+ }++ if (opset1->is_primitive) {+ if (is_numeric) {+ new_op1 = JS_ToNumeric(ctx, op1);+ } else {+ new_op1 = JS_ToPrimitive(ctx, op1, hint);+ }+ if (JS_IsException(new_op1))+ goto exception;+ } else {+ new_op1 = JS_DupValue(ctx, op1);+ }+ + if (opset2->is_primitive) {+ if (is_numeric) {+ new_op2 = JS_ToNumeric(ctx, op2);+ } else {+ new_op2 = JS_ToPrimitive(ctx, op2, hint);+ }+ if (JS_IsException(new_op2)) {+ JS_FreeValue(ctx, new_op1);+ goto exception;+ }+ } else {+ new_op2 = JS_DupValue(ctx, op2);+ }++ /* XXX: could apply JS_ToPrimitive() if primitive type so that the+ operator function does not get a value object */+ + method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));+ if (ovop == JS_OVOP_LESS && (op == OP_lte || op == OP_gt)) {+ args[0] = new_op2;+ args[1] = new_op1;+ } else {+ args[0] = new_op1;+ args[1] = new_op2;+ }+ ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args);+ JS_FreeValue(ctx, new_op1);+ JS_FreeValue(ctx, new_op2);+ if (JS_IsException(ret))+ goto exception;+ if (ovop == JS_OVOP_EQ) {+ BOOL res = JS_ToBoolFree(ctx, ret);+ if (op == OP_neq)+ res ^= 1;+ ret = JS_NewBool(ctx, res);+ } else if (ovop == JS_OVOP_LESS) {+ if (JS_IsUndefined(ret)) {+ ret = JS_FALSE;+ } else {+ BOOL res = JS_ToBoolFree(ctx, ret);+ if (op == OP_lte || op == OP_gte)+ res ^= 1;+ ret = JS_NewBool(ctx, res);+ }+ }+ JS_FreeValue(ctx, opset1_obj);+ JS_FreeValue(ctx, opset2_obj);+ *pret = ret;+ return 1;+ exception:+ JS_FreeValue(ctx, opset1_obj);+ JS_FreeValue(ctx, opset2_obj);+ *pret = JS_UNDEFINED;+ return -1;+}++/* try to call the operation on the operatorSet field of 'obj'. Only+ used for "/" and "**" on the BigInt prototype in math mode */+static __exception int js_call_binary_op_simple(JSContext *ctx,+ JSValue *pret,+ JSValueConst obj,+ JSValueConst op1,+ JSValueConst op2,+ OPCodeEnum op)+{+ JSValue opset1_obj, method, ret, new_op1, new_op2;+ JSOperatorSetData *opset1;+ JSOverloadableOperatorEnum ovop;+ JSObject *p;+ JSValueConst args[2];++ opset1_obj = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet);+ if (JS_IsException(opset1_obj))+ goto exception;+ if (JS_IsUndefined(opset1_obj))+ return 0;+ opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);+ if (!opset1)+ goto exception;+ ovop = get_ovop_from_opcode(op);++ p = opset1->self_ops[ovop];+ if (!p) {+ JS_FreeValue(ctx, opset1_obj);+ return 0;+ }++ new_op1 = JS_ToNumeric(ctx, op1);+ if (JS_IsException(new_op1))+ goto exception;+ new_op2 = JS_ToNumeric(ctx, op2);+ if (JS_IsException(new_op2)) {+ JS_FreeValue(ctx, new_op1);+ goto exception;+ }++ method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));+ args[0] = new_op1;+ args[1] = new_op2;+ ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args);+ JS_FreeValue(ctx, new_op1);+ JS_FreeValue(ctx, new_op2);+ if (JS_IsException(ret))+ goto exception;+ JS_FreeValue(ctx, opset1_obj);+ *pret = ret;+ return 1;+ exception:+ JS_FreeValue(ctx, opset1_obj);+ *pret = JS_UNDEFINED;+ return -1;+}++/* return -1 if exception, 0 if no operator overloading, 1 if+ overloaded operator called */+static __exception int js_call_unary_op_fallback(JSContext *ctx,+ JSValue *pret,+ JSValueConst op1,+ OPCodeEnum op)+{+ JSValue opset1_obj, method, ret;+ JSOperatorSetData *opset1;+ JSOverloadableOperatorEnum ovop;+ JSObject *p;++ if (!ctx->allow_operator_overloading)+ return 0;+ + opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet);+ if (JS_IsException(opset1_obj))+ goto exception;+ if (JS_IsUndefined(opset1_obj))+ return 0;+ opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);+ if (!opset1)+ goto exception;+ if (opset1->is_primitive) {+ JS_FreeValue(ctx, opset1_obj);+ return 0;+ }++ ovop = get_ovop_from_opcode(op);++ p = opset1->self_ops[ovop];+ if (!p) {+ JS_ThrowTypeError(ctx, "no overloaded operator %s",+ js_overloadable_operator_names[ovop]);+ goto exception;+ }+ method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));+ ret = JS_CallFree(ctx, method, JS_UNDEFINED, 1, &op1);+ if (JS_IsException(ret))+ goto exception;+ JS_FreeValue(ctx, opset1_obj);+ *pret = ret;+ return 1;+ exception:+ JS_FreeValue(ctx, opset1_obj);+ *pret = JS_UNDEFINED;+ return -1;+}++static JSValue throw_bf_exception(JSContext *ctx, int status)+{+ const char *str;+ if (status & BF_ST_MEM_ERROR)+ return JS_ThrowOutOfMemory(ctx);+ if (status & BF_ST_DIVIDE_ZERO) {+ str = "division by zero";+ } else if (status & BF_ST_INVALID_OP) {+ str = "invalid operation";+ } else {+ str = "integer overflow";+ }+ return JS_ThrowRangeError(ctx, "%s", str);+}++static int js_unary_arith_bigint(JSContext *ctx,+ JSValue *pres, OPCodeEnum op, JSValue op1)+{+ bf_t a_s, *r, *a;+ int ret, v;+ JSValue res;+ + if (op == OP_plus && !is_math_mode(ctx)) {+ JS_ThrowTypeError(ctx, "bigint argument with unary +");+ JS_FreeValue(ctx, op1);+ return -1;+ }+ res = JS_NewBigInt(ctx);+ if (JS_IsException(res)) {+ JS_FreeValue(ctx, op1);+ return -1;+ }+ r = JS_GetBigInt(res);+ a = JS_ToBigInt(ctx, &a_s, op1);+ ret = 0;+ switch(op) {+ case OP_inc:+ case OP_dec:+ v = 2 * (op - OP_dec) - 1;+ ret = bf_add_si(r, a, v, BF_PREC_INF, BF_RNDZ);+ break;+ case OP_plus:+ ret = bf_set(r, a);+ break;+ case OP_neg:+ ret = bf_set(r, a);+ bf_neg(r);+ break;+ case OP_not:+ ret = bf_add_si(r, a, 1, BF_PREC_INF, BF_RNDZ);+ bf_neg(r);+ break;+ default:+ abort();+ }+ JS_FreeBigInt(ctx, a, &a_s);+ JS_FreeValue(ctx, op1);+ if (unlikely(ret)) {+ JS_FreeValue(ctx, res);+ throw_bf_exception(ctx, ret);+ return -1;+ }+ res = JS_CompactBigInt(ctx, res);+ *pres = res;+ return 0;+}++static int js_unary_arith_bigfloat(JSContext *ctx,+ JSValue *pres, OPCodeEnum op, JSValue op1)+{+ bf_t a_s, *r, *a;+ int ret, v;+ JSValue res;+ + if (op == OP_plus && !is_math_mode(ctx)) {+ JS_ThrowTypeError(ctx, "bigfloat argument with unary +");+ JS_FreeValue(ctx, op1);+ return -1;+ }++ res = JS_NewBigFloat(ctx);+ if (JS_IsException(res)) {+ JS_FreeValue(ctx, op1);+ return -1;+ }+ r = JS_GetBigFloat(res);+ a = JS_ToBigFloat(ctx, &a_s, op1);+ ret = 0;+ switch(op) {+ case OP_inc:+ case OP_dec:+ v = 2 * (op - OP_dec) - 1;+ ret = bf_add_si(r, a, v, ctx->fp_env.prec, ctx->fp_env.flags);+ break;+ case OP_plus:+ ret = bf_set(r, a);+ break;+ case OP_neg:+ ret = bf_set(r, a);+ bf_neg(r);+ break;+ default:+ abort();+ }+ if (a == &a_s)+ bf_delete(a);+ JS_FreeValue(ctx, op1);+ if (unlikely(ret & BF_ST_MEM_ERROR)) {+ JS_FreeValue(ctx, res);+ throw_bf_exception(ctx, ret);+ return -1;+ }+ *pres = res;+ return 0;+}++static int js_unary_arith_bigdecimal(JSContext *ctx,+ JSValue *pres, OPCodeEnum op, JSValue op1)+{+ bfdec_t *r, *a;+ int ret, v;+ JSValue res;+ + if (op == OP_plus && !is_math_mode(ctx)) {+ JS_ThrowTypeError(ctx, "bigdecimal argument with unary +");+ JS_FreeValue(ctx, op1);+ return -1;+ }++ res = JS_NewBigDecimal(ctx);+ if (JS_IsException(res)) {+ JS_FreeValue(ctx, op1);+ return -1;+ }+ r = JS_GetBigDecimal(res);+ a = JS_ToBigDecimal(ctx, op1);+ ret = 0;+ switch(op) {+ case OP_inc:+ case OP_dec:+ v = 2 * (op - OP_dec) - 1;+ ret = bfdec_add_si(r, a, v, BF_PREC_INF, BF_RNDZ);+ break;+ case OP_plus:+ ret = bfdec_set(r, a);+ break;+ case OP_neg:+ ret = bfdec_set(r, a);+ bfdec_neg(r);+ break;+ default:+ abort();+ }+ JS_FreeValue(ctx, op1);+ if (unlikely(ret)) {+ JS_FreeValue(ctx, res);+ throw_bf_exception(ctx, ret);+ return -1;+ }+ *pres = res;+ return 0;+}++static no_inline __exception int js_unary_arith_slow(JSContext *ctx,+ JSValue *sp,+ OPCodeEnum op)+{+ JSValue op1, val;+ int v, ret;+ uint32_t tag;++ op1 = sp[-1];+ /* fast path for float64 */+ if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1)))+ goto handle_float64;+ if (JS_IsObject(op1)) {+ ret = js_call_unary_op_fallback(ctx, &val, op1, op);+ if (ret < 0)+ return -1;+ if (ret) {+ JS_FreeValue(ctx, op1);+ sp[-1] = val;+ return 0;+ }+ }++ op1 = JS_ToNumericFree(ctx, op1);+ if (JS_IsException(op1))+ goto exception;+ tag = JS_VALUE_GET_TAG(op1);+ switch(tag) {+ case JS_TAG_INT:+ {+ int64_t v64;+ v64 = JS_VALUE_GET_INT(op1);+ switch(op) {+ case OP_inc:+ case OP_dec:+ v = 2 * (op - OP_dec) - 1;+ v64 += v;+ break;+ case OP_plus:+ break;+ case OP_neg:+ if (v64 == 0) {+ sp[-1] = __JS_NewFloat64(ctx, -0.0);+ return 0;+ } else {+ v64 = -v64;+ }+ break;+ default:+ abort();+ }+ sp[-1] = JS_NewInt64(ctx, v64);+ }+ break;+ case JS_TAG_BIG_INT:+ handle_bigint:+ if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, op, op1))+ goto exception;+ break;+ case JS_TAG_BIG_FLOAT:+ if (ctx->rt->bigfloat_ops.unary_arith(ctx, sp - 1, op, op1))+ goto exception;+ break;+ case JS_TAG_BIG_DECIMAL:+ if (ctx->rt->bigdecimal_ops.unary_arith(ctx, sp - 1, op, op1))+ goto exception;+ break;+ default:+ handle_float64:+ {+ double d;+ if (is_math_mode(ctx))+ goto handle_bigint;+ d = JS_VALUE_GET_FLOAT64(op1);+ switch(op) {+ case OP_inc:+ case OP_dec:+ v = 2 * (op - OP_dec) - 1;+ d += v;+ break;+ case OP_plus:+ break;+ case OP_neg:+ d = -d;+ break;+ default:+ abort();+ }+ sp[-1] = __JS_NewFloat64(ctx, d);+ }+ break;+ }+ return 0;+ exception:+ sp[-1] = JS_UNDEFINED;+ return -1;+}++static __exception int js_post_inc_slow(JSContext *ctx,+ JSValue *sp, OPCodeEnum op)+{+ JSValue op1;++ /* XXX: allow custom operators */+ op1 = sp[-1];+ op1 = JS_ToNumericFree(ctx, op1);+ if (JS_IsException(op1)) {+ sp[-1] = JS_UNDEFINED;+ return -1;+ }+ sp[-1] = op1;+ sp[0] = JS_DupValue(ctx, op1);+ return js_unary_arith_slow(ctx, sp + 1, op - OP_post_dec + OP_dec);+}++static no_inline int js_not_slow(JSContext *ctx, JSValue *sp)+{+ JSValue op1, val;+ int ret;+ + op1 = sp[-1];+ if (JS_IsObject(op1)) {+ ret = js_call_unary_op_fallback(ctx, &val, op1, OP_not);+ if (ret < 0)+ return -1;+ if (ret) {+ JS_FreeValue(ctx, op1);+ sp[-1] = val;+ return 0;+ }+ }++ op1 = JS_ToNumericFree(ctx, op1);+ if (JS_IsException(op1))+ goto exception;+ if (is_math_mode(ctx) || JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) {+ if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, OP_not, op1))+ goto exception;+ } else {+ int32_t v1;+ if (unlikely(JS_ToInt32Free(ctx, &v1, op1)))+ goto exception;+ sp[-1] = JS_NewInt32(ctx, ~v1);+ }+ return 0;+ exception:+ sp[-1] = JS_UNDEFINED;+ return -1;+}++static int js_binary_arith_bigfloat(JSContext *ctx, OPCodeEnum op,+ JSValue *pres, JSValue op1, JSValue op2)+{+ bf_t a_s, b_s, *r, *a, *b;+ int ret;+ JSValue res;+ + res = JS_NewBigFloat(ctx);+ if (JS_IsException(res)) {+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ return -1;+ }+ r = JS_GetBigFloat(res);+ a = JS_ToBigFloat(ctx, &a_s, op1);+ b = JS_ToBigFloat(ctx, &b_s, op2);+ bf_init(ctx->bf_ctx, r);+ switch(op) {+ case OP_add:+ ret = bf_add(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);+ break;+ case OP_sub:+ ret = bf_sub(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);+ break;+ case OP_mul:+ ret = bf_mul(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);+ break;+ case OP_div:+ ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);+ break;+ case OP_math_mod:+ /* Euclidian remainder */+ ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags,+ BF_DIVREM_EUCLIDIAN);+ break;+ case OP_mod:+ ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags,+ BF_RNDZ);+ break;+ case OP_pow:+ ret = bf_pow(r, a, b, ctx->fp_env.prec,+ ctx->fp_env.flags | BF_POW_JS_QUIRKS);+ break;+ default:+ abort();+ }+ if (a == &a_s)+ bf_delete(a);+ if (b == &b_s)+ bf_delete(b);+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ if (unlikely(ret & BF_ST_MEM_ERROR)) {+ JS_FreeValue(ctx, res);+ throw_bf_exception(ctx, ret);+ return -1;+ }+ *pres = res;+ return 0;+}++static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op,+ JSValue *pres, JSValue op1, JSValue op2)+{+ bf_t a_s, b_s, *r, *a, *b;+ int ret;+ JSValue res;+ + res = JS_NewBigInt(ctx);+ if (JS_IsException(res))+ goto fail;+ a = JS_ToBigInt(ctx, &a_s, op1);+ if (!a)+ goto fail;+ b = JS_ToBigInt(ctx, &b_s, op2);+ if (!b) {+ JS_FreeBigInt(ctx, a, &a_s);+ goto fail;+ }+ r = JS_GetBigInt(res);+ ret = 0;+ switch(op) {+ case OP_add:+ ret = bf_add(r, a, b, BF_PREC_INF, BF_RNDZ);+ break;+ case OP_sub:+ ret = bf_sub(r, a, b, BF_PREC_INF, BF_RNDZ);+ break;+ case OP_mul:+ ret = bf_mul(r, a, b, BF_PREC_INF, BF_RNDZ);+ break;+ case OP_div:+ if (!is_math_mode(ctx)) {+ bf_t rem_s, *rem = &rem_s;+ bf_init(ctx->bf_ctx, rem);+ ret = bf_divrem(r, rem, a, b, BF_PREC_INF, BF_RNDZ,+ BF_RNDZ);+ bf_delete(rem);+ } else {+ goto math_mode_div_pow;+ }+ break;+ case OP_math_mod:+ /* Euclidian remainder */+ ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ,+ BF_DIVREM_EUCLIDIAN) & BF_ST_INVALID_OP;+ break;+ case OP_mod:+ ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ,+ BF_RNDZ) & BF_ST_INVALID_OP;+ break;+ case OP_pow:+ if (b->sign) {+ if (!is_math_mode(ctx)) {+ ret = BF_ST_INVALID_OP;+ } else {+ math_mode_div_pow:+ JS_FreeValue(ctx, res);+ ret = js_call_binary_op_simple(ctx, &res, ctx->class_proto[JS_CLASS_BIG_INT], op1, op2, op);+ if (ret != 0) {+ JS_FreeBigInt(ctx, a, &a_s);+ JS_FreeBigInt(ctx, b, &b_s);+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ if (ret < 0) {+ return -1;+ } else {+ *pres = res;+ return 0;+ }+ }+ /* if no BigInt power operator defined, return a+ bigfloat */+ res = JS_NewBigFloat(ctx);+ if (JS_IsException(res)) {+ JS_FreeBigInt(ctx, a, &a_s);+ JS_FreeBigInt(ctx, b, &b_s);+ goto fail;+ }+ r = JS_GetBigFloat(res);+ if (op == OP_div) {+ ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags) & BF_ST_MEM_ERROR;+ } else {+ ret = bf_pow(r, a, b, ctx->fp_env.prec,+ ctx->fp_env.flags | BF_POW_JS_QUIRKS) & BF_ST_MEM_ERROR;+ }+ JS_FreeBigInt(ctx, a, &a_s);+ JS_FreeBigInt(ctx, b, &b_s);+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ if (unlikely(ret)) {+ JS_FreeValue(ctx, res);+ throw_bf_exception(ctx, ret);+ return -1;+ }+ *pres = res;+ return 0;+ }+ } else {+ ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUIRKS);+ }+ break;++ /* logical operations */+ case OP_shl:+ case OP_sar:+ {+ slimb_t v2;+#if LIMB_BITS == 32+ bf_get_int32(&v2, b, 0);+ if (v2 == INT32_MIN)+ v2 = INT32_MIN + 1;+#else+ bf_get_int64(&v2, b, 0);+ if (v2 == INT64_MIN)+ v2 = INT64_MIN + 1;+#endif+ if (op == OP_sar)+ v2 = -v2;+ ret = bf_set(r, a);+ ret |= bf_mul_2exp(r, v2, BF_PREC_INF, BF_RNDZ);+ if (v2 < 0) {+ ret |= bf_rint(r, BF_RNDD) & (BF_ST_OVERFLOW | BF_ST_MEM_ERROR);+ }+ }+ break;+ case OP_and:+ ret = bf_logic_and(r, a, b);+ break;+ case OP_or:+ ret = bf_logic_or(r, a, b);+ break;+ case OP_xor:+ ret = bf_logic_xor(r, a, b);+ break;+ default:+ abort();+ }+ JS_FreeBigInt(ctx, a, &a_s);+ JS_FreeBigInt(ctx, b, &b_s);+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ if (unlikely(ret)) {+ JS_FreeValue(ctx, res);+ throw_bf_exception(ctx, ret);+ return -1;+ }+ *pres = JS_CompactBigInt(ctx, res);+ return 0;+ fail:+ JS_FreeValue(ctx, res);+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ return -1;+}++/* b must be a positive integer */+static int js_bfdec_pow(bfdec_t *r, const bfdec_t *a, const bfdec_t *b)+{+ bfdec_t b1;+ int32_t b2;+ int ret;++ bfdec_init(b->ctx, &b1);+ ret = bfdec_set(&b1, b);+ if (ret) {+ bfdec_delete(&b1);+ return ret;+ }+ ret = bfdec_rint(&b1, BF_RNDZ);+ if (ret) {+ bfdec_delete(&b1);+ return BF_ST_INVALID_OP; /* must be an integer */+ }+ ret = bfdec_get_int32(&b2, &b1);+ bfdec_delete(&b1);+ if (ret)+ return ret; /* overflow */+ if (b2 < 0)+ return BF_ST_INVALID_OP; /* must be positive */+ return bfdec_pow_ui(r, a, b2);+}++static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op,+ JSValue *pres, JSValue op1, JSValue op2)+{+ bfdec_t *r, *a, *b;+ int ret;+ JSValue res;++ res = JS_NewBigDecimal(ctx);+ if (JS_IsException(res))+ goto fail;+ r = JS_GetBigDecimal(res);+ + a = JS_ToBigDecimal(ctx, op1);+ if (!a)+ goto fail;+ b = JS_ToBigDecimal(ctx, op2);+ if (!b)+ goto fail;+ switch(op) {+ case OP_add:+ ret = bfdec_add(r, a, b, BF_PREC_INF, BF_RNDZ);+ break;+ case OP_sub:+ ret = bfdec_sub(r, a, b, BF_PREC_INF, BF_RNDZ);+ break;+ case OP_mul:+ ret = bfdec_mul(r, a, b, BF_PREC_INF, BF_RNDZ);+ break;+ case OP_div:+ ret = bfdec_div(r, a, b, BF_PREC_INF, BF_RNDZ);+ break;+ case OP_math_mod:+ /* Euclidian remainder */+ ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_DIVREM_EUCLIDIAN);+ break;+ case OP_mod:+ ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_RNDZ);+ break;+ case OP_pow:+ ret = js_bfdec_pow(r, a, b);+ break;+ default:+ abort();+ }+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ if (unlikely(ret)) {+ JS_FreeValue(ctx, res);+ throw_bf_exception(ctx, ret);+ return -1;+ }+ *pres = res;+ return 0;+ fail:+ JS_FreeValue(ctx, res);+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ return -1;+}++static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp,+ OPCodeEnum op)+{+ JSValue op1, op2, res;+ uint32_t tag1, tag2;+ int ret;+ double d1, d2;++ op1 = sp[-2];+ op2 = sp[-1];+ tag1 = JS_VALUE_GET_NORM_TAG(op1);+ tag2 = JS_VALUE_GET_NORM_TAG(op2);+ /* fast path for float operations */+ if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) {+ d1 = JS_VALUE_GET_FLOAT64(op1);+ d2 = JS_VALUE_GET_FLOAT64(op2);+ goto handle_float64;+ }++ /* try to call an overloaded operator */+ if ((tag1 == JS_TAG_OBJECT &&+ (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||+ (tag2 == JS_TAG_OBJECT &&+ (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {+ ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0);+ if (ret != 0) {+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ if (ret < 0) {+ goto exception;+ } else {+ sp[-2] = res;+ return 0;+ }+ }+ }++ op1 = JS_ToNumericFree(ctx, op1);+ if (JS_IsException(op1)) {+ JS_FreeValue(ctx, op2);+ goto exception;+ }+ op2 = JS_ToNumericFree(ctx, op2);+ if (JS_IsException(op2)) {+ JS_FreeValue(ctx, op1);+ goto exception;+ }+ tag1 = JS_VALUE_GET_NORM_TAG(op1);+ tag2 = JS_VALUE_GET_NORM_TAG(op2);++ if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {+ int32_t v1, v2;+ int64_t v;+ v1 = JS_VALUE_GET_INT(op1);+ v2 = JS_VALUE_GET_INT(op2);+ switch(op) {+ case OP_sub:+ v = (int64_t)v1 - (int64_t)v2;+ break;+ case OP_mul:+ v = (int64_t)v1 * (int64_t)v2;+ if (is_math_mode(ctx) &&+ (v < -MAX_SAFE_INTEGER || v > MAX_SAFE_INTEGER))+ goto handle_bigint;+ if (v == 0 && (v1 | v2) < 0) {+ sp[-2] = __JS_NewFloat64(ctx, -0.0);+ return 0;+ }+ break;+ case OP_div:+ if (is_math_mode(ctx))+ goto handle_bigint;+ sp[-2] = __JS_NewFloat64(ctx, (double)v1 / (double)v2);+ return 0;+ case OP_math_mod:+ if (unlikely(v2 == 0)) {+ throw_bf_exception(ctx, BF_ST_DIVIDE_ZERO);+ goto exception;+ }+ v = (int64_t)v1 % (int64_t)v2;+ if (v < 0) {+ if (v2 < 0)+ v -= v2;+ else+ v += v2;+ }+ break;+ case OP_mod:+ if (v1 < 0 || v2 <= 0) {+ sp[-2] = JS_NewFloat64(ctx, fmod(v1, v2));+ return 0;+ } else {+ v = (int64_t)v1 % (int64_t)v2;+ }+ break;+ case OP_pow:+ if (!is_math_mode(ctx)) {+ sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2));+ return 0;+ } else {+ goto handle_bigint;+ }+ break;+ default:+ abort();+ }+ sp[-2] = JS_NewInt64(ctx, v);+ } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {+ if (ctx->rt->bigdecimal_ops.binary_arith(ctx, op, sp - 2, op1, op2))+ goto exception;+ } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {+ if (ctx->rt->bigfloat_ops.binary_arith(ctx, op, sp - 2, op1, op2))+ goto exception;+ } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {+ handle_bigint:+ if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))+ goto exception;+ } else {+ double dr;+ /* float64 result */+ if (JS_ToFloat64Free(ctx, &d1, op1)) {+ JS_FreeValue(ctx, op2);+ goto exception;+ }+ if (JS_ToFloat64Free(ctx, &d2, op2))+ goto exception;+ handle_float64:+ if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2))+ goto handle_bigint;+ switch(op) {+ case OP_sub:+ dr = d1 - d2;+ break;+ case OP_mul:+ dr = d1 * d2;+ break;+ case OP_div:+ dr = d1 / d2;+ break;+ case OP_mod:+ dr = fmod(d1, d2);+ break;+ case OP_math_mod:+ d2 = fabs(d2);+ dr = fmod(d1, d2);+ /* XXX: loss of accuracy if dr < 0 */+ if (dr < 0)+ dr += d2;+ break;+ case OP_pow:+ dr = js_pow(d1, d2);+ break;+ default:+ abort();+ }+ sp[-2] = __JS_NewFloat64(ctx, dr);+ }+ return 0;+ exception:+ sp[-2] = JS_UNDEFINED;+ sp[-1] = JS_UNDEFINED;+ return -1;+}++static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)+{+ JSValue op1, op2, res;+ uint32_t tag1, tag2;+ int ret;++ op1 = sp[-2];+ op2 = sp[-1];++ tag1 = JS_VALUE_GET_NORM_TAG(op1);+ tag2 = JS_VALUE_GET_NORM_TAG(op2);+ /* fast path for float64 */+ if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) {+ double d1, d2;+ d1 = JS_VALUE_GET_FLOAT64(op1);+ d2 = JS_VALUE_GET_FLOAT64(op2);+ sp[-2] = __JS_NewFloat64(ctx, d1 + d2);+ return 0;+ }++ if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) {+ /* try to call an overloaded operator */+ if ((tag1 == JS_TAG_OBJECT &&+ (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED &&+ tag2 != JS_TAG_STRING)) ||+ (tag2 == JS_TAG_OBJECT &&+ (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED &&+ tag1 != JS_TAG_STRING))) {+ ret = js_call_binary_op_fallback(ctx, &res, op1, op2, OP_add,+ FALSE, HINT_NONE);+ if (ret != 0) {+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ if (ret < 0) {+ goto exception;+ } else {+ sp[-2] = res;+ return 0;+ }+ }+ }++ op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);+ if (JS_IsException(op1)) {+ JS_FreeValue(ctx, op2);+ goto exception;+ }++ op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);+ if (JS_IsException(op2)) {+ JS_FreeValue(ctx, op1);+ goto exception;+ }+ tag1 = JS_VALUE_GET_NORM_TAG(op1);+ tag2 = JS_VALUE_GET_NORM_TAG(op2);+ }++ if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) {+ sp[-2] = JS_ConcatString(ctx, op1, op2);+ if (JS_IsException(sp[-2]))+ goto exception;+ return 0;+ }++ op1 = JS_ToNumericFree(ctx, op1);+ if (JS_IsException(op1)) {+ JS_FreeValue(ctx, op2);+ goto exception;+ }+ op2 = JS_ToNumericFree(ctx, op2);+ if (JS_IsException(op2)) {+ JS_FreeValue(ctx, op1);+ goto exception;+ }+ tag1 = JS_VALUE_GET_NORM_TAG(op1);+ tag2 = JS_VALUE_GET_NORM_TAG(op2);++ if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {+ int32_t v1, v2;+ int64_t v;+ v1 = JS_VALUE_GET_INT(op1);+ v2 = JS_VALUE_GET_INT(op2);+ v = (int64_t)v1 + (int64_t)v2;+ sp[-2] = JS_NewInt64(ctx, v);+ } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {+ if (ctx->rt->bigdecimal_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))+ goto exception;+ } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {+ if (ctx->rt->bigfloat_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))+ goto exception;+ } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {+ handle_bigint:+ if (ctx->rt->bigint_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))+ goto exception;+ } else {+ double d1, d2;+ /* float64 result */+ if (JS_ToFloat64Free(ctx, &d1, op1)) {+ JS_FreeValue(ctx, op2);+ goto exception;+ }+ if (JS_ToFloat64Free(ctx, &d2, op2))+ goto exception;+ if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2))+ goto handle_bigint;+ sp[-2] = __JS_NewFloat64(ctx, d1 + d2);+ }+ return 0;+ exception:+ sp[-2] = JS_UNDEFINED;+ sp[-1] = JS_UNDEFINED;+ return -1;+}++static no_inline __exception int js_binary_logic_slow(JSContext *ctx,+ JSValue *sp,+ OPCodeEnum op)+{+ JSValue op1, op2, res;+ int ret;+ uint32_t tag1, tag2;+ uint32_t v1, v2, r;++ op1 = sp[-2];+ op2 = sp[-1];+ tag1 = JS_VALUE_GET_NORM_TAG(op1);+ tag2 = JS_VALUE_GET_NORM_TAG(op2);++ /* try to call an overloaded operator */+ if ((tag1 == JS_TAG_OBJECT &&+ (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||+ (tag2 == JS_TAG_OBJECT &&+ (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {+ ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0);+ if (ret != 0) {+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ if (ret < 0) {+ goto exception;+ } else {+ sp[-2] = res;+ return 0;+ }+ }+ }++ op1 = JS_ToNumericFree(ctx, op1);+ if (JS_IsException(op1)) {+ JS_FreeValue(ctx, op2);+ goto exception;+ }+ op2 = JS_ToNumericFree(ctx, op2);+ if (JS_IsException(op2)) {+ JS_FreeValue(ctx, op1);+ goto exception;+ }++ if (is_math_mode(ctx))+ goto bigint_op;++ tag1 = JS_VALUE_GET_TAG(op1);+ tag2 = JS_VALUE_GET_TAG(op2);+ if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {+ if (tag1 != tag2) {+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ JS_ThrowTypeError(ctx, "both operands must be bigint");+ goto exception;+ } else {+ bigint_op:+ if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))+ goto exception;+ }+ } else {+ if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) {+ JS_FreeValue(ctx, op2);+ goto exception;+ }+ if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2)))+ goto exception;+ switch(op) {+ case OP_shl:+ r = v1 << (v2 & 0x1f);+ break;+ case OP_sar:+ r = (int)v1 >> (v2 & 0x1f);+ break;+ case OP_and:+ r = v1 & v2;+ break;+ case OP_or:+ r = v1 | v2;+ break;+ case OP_xor:+ r = v1 ^ v2;+ break;+ default:+ abort();+ }+ sp[-2] = JS_NewInt32(ctx, r);+ }+ return 0;+ exception:+ sp[-2] = JS_UNDEFINED;+ sp[-1] = JS_UNDEFINED;+ return -1;+}++/* Note: also used for bigint */+static int js_compare_bigfloat(JSContext *ctx, OPCodeEnum op,+ JSValue op1, JSValue op2)+{+ bf_t a_s, b_s, *a, *b;+ int res;+ + a = JS_ToBigFloat(ctx, &a_s, op1);+ if (!a) {+ JS_FreeValue(ctx, op2);+ return -1;+ }+ b = JS_ToBigFloat(ctx, &b_s, op2);+ if (!b) {+ if (a == &a_s)+ bf_delete(a);+ JS_FreeValue(ctx, op1);+ return -1;+ }+ switch(op) {+ case OP_lt:+ res = bf_cmp_lt(a, b); /* if NaN return false */+ break;+ case OP_lte:+ res = bf_cmp_le(a, b); /* if NaN return false */+ break;+ case OP_gt:+ res = bf_cmp_lt(b, a); /* if NaN return false */+ break;+ case OP_gte:+ res = bf_cmp_le(b, a); /* if NaN return false */+ break;+ case OP_eq:+ res = bf_cmp_eq(a, b); /* if NaN return false */+ break;+ default:+ abort();+ }+ if (a == &a_s)+ bf_delete(a);+ if (b == &b_s)+ bf_delete(b);+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ return res;+}++static int js_compare_bigdecimal(JSContext *ctx, OPCodeEnum op,+ JSValue op1, JSValue op2)+{+ bfdec_t *a, *b;+ int res;++ /* Note: binary floats are converted to bigdecimal with+ toString(). It is not mathematically correct but is consistent+ with the BigDecimal() constructor behavior */+ op1 = JS_ToBigDecimalFree(ctx, op1, TRUE);+ if (JS_IsException(op1)) {+ JS_FreeValue(ctx, op2);+ return -1;+ }+ op2 = JS_ToBigDecimalFree(ctx, op2, TRUE);+ if (JS_IsException(op2)) {+ JS_FreeValue(ctx, op1);+ return -1;+ }+ a = JS_ToBigDecimal(ctx, op1);+ b = JS_ToBigDecimal(ctx, op2);+ + switch(op) {+ case OP_lt:+ res = bfdec_cmp_lt(a, b); /* if NaN return false */+ break;+ case OP_lte:+ res = bfdec_cmp_le(a, b); /* if NaN return false */+ break;+ case OP_gt:+ res = bfdec_cmp_lt(b, a); /* if NaN return false */+ break;+ case OP_gte:+ res = bfdec_cmp_le(b, a); /* if NaN return false */+ break;+ case OP_eq:+ res = bfdec_cmp_eq(a, b); /* if NaN return false */+ break;+ default:+ abort();+ }+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ return res;+}++static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,+ OPCodeEnum op)+{+ JSValue op1, op2, ret;+ int res;+ uint32_t tag1, tag2;++ op1 = sp[-2];+ op2 = sp[-1];+ tag1 = JS_VALUE_GET_NORM_TAG(op1);+ tag2 = JS_VALUE_GET_NORM_TAG(op2);+ /* try to call an overloaded operator */+ if ((tag1 == JS_TAG_OBJECT &&+ (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||+ (tag2 == JS_TAG_OBJECT &&+ (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {+ res = js_call_binary_op_fallback(ctx, &ret, op1, op2, op,+ FALSE, HINT_NUMBER);+ if (res != 0) {+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ if (res < 0) {+ goto exception;+ } else {+ sp[-2] = ret;+ return 0;+ }+ }+ }+ op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER);+ if (JS_IsException(op1)) {+ JS_FreeValue(ctx, op2);+ goto exception;+ }+ op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER);+ if (JS_IsException(op2)) {+ JS_FreeValue(ctx, op1);+ goto exception;+ }+ tag1 = JS_VALUE_GET_NORM_TAG(op1);+ tag2 = JS_VALUE_GET_NORM_TAG(op2);++ if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) {+ JSString *p1, *p2;+ p1 = JS_VALUE_GET_STRING(op1);+ p2 = JS_VALUE_GET_STRING(op2);+ res = js_string_compare(ctx, p1, p2);+ switch(op) {+ case OP_lt:+ res = (res < 0);+ break;+ case OP_lte:+ res = (res <= 0);+ break;+ case OP_gt:+ res = (res > 0);+ break;+ default:+ case OP_gte:+ res = (res >= 0);+ break;+ }+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ } else if ((tag1 <= JS_TAG_NULL || tag1 == JS_TAG_FLOAT64) &&+ (tag2 <= JS_TAG_NULL || tag2 == JS_TAG_FLOAT64)) {+ /* fast path for float64/int */+ goto float64_compare;+ } else {+ if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) ||+ (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING)) &&+ !is_math_mode(ctx)) {+ if (tag1 == JS_TAG_STRING) {+ op1 = JS_StringToBigInt(ctx, op1);+ if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT)+ goto invalid_bigint_string;+ }+ if (tag2 == JS_TAG_STRING) {+ op2 = JS_StringToBigInt(ctx, op2);+ if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) {+ invalid_bigint_string:+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ res = FALSE;+ goto done;+ }+ }+ } else {+ op1 = JS_ToNumericFree(ctx, op1);+ if (JS_IsException(op1)) {+ JS_FreeValue(ctx, op2);+ goto exception;+ }+ op2 = JS_ToNumericFree(ctx, op2);+ if (JS_IsException(op2)) {+ JS_FreeValue(ctx, op1);+ goto exception;+ }+ }++ tag1 = JS_VALUE_GET_NORM_TAG(op1);+ tag2 = JS_VALUE_GET_NORM_TAG(op2);++ if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {+ res = ctx->rt->bigdecimal_ops.compare(ctx, op, op1, op2);+ if (res < 0)+ goto exception;+ } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {+ res = ctx->rt->bigfloat_ops.compare(ctx, op, op1, op2);+ if (res < 0)+ goto exception;+ } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {+ res = ctx->rt->bigint_ops.compare(ctx, op, op1, op2);+ if (res < 0)+ goto exception;+ } else {+ double d1, d2;++ float64_compare:+ /* can use floating point comparison */+ if (tag1 == JS_TAG_FLOAT64) {+ d1 = JS_VALUE_GET_FLOAT64(op1);+ } else {+ d1 = JS_VALUE_GET_INT(op1);+ }+ if (tag2 == JS_TAG_FLOAT64) {+ d2 = JS_VALUE_GET_FLOAT64(op2);+ } else {+ d2 = JS_VALUE_GET_INT(op2);+ }+ switch(op) {+ case OP_lt:+ res = (d1 < d2); /* if NaN return false */+ break;+ case OP_lte:+ res = (d1 <= d2); /* if NaN return false */+ break;+ case OP_gt:+ res = (d1 > d2); /* if NaN return false */+ break;+ default:+ case OP_gte:+ res = (d1 >= d2); /* if NaN return false */+ break;+ }+ }+ }+ done:+ sp[-2] = JS_NewBool(ctx, res);+ return 0;+ exception:+ sp[-2] = JS_UNDEFINED;+ sp[-1] = JS_UNDEFINED;+ return -1;+}++static BOOL tag_is_number(uint32_t tag)+{+ return (tag == JS_TAG_INT || tag == JS_TAG_BIG_INT ||+ tag == JS_TAG_FLOAT64 || tag == JS_TAG_BIG_FLOAT ||+ tag == JS_TAG_BIG_DECIMAL);+}++static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,+ BOOL is_neq)+{+ JSValue op1, op2, ret;+ int res;+ uint32_t tag1, tag2;++ op1 = sp[-2];+ op2 = sp[-1];+ redo:+ tag1 = JS_VALUE_GET_NORM_TAG(op1);+ tag2 = JS_VALUE_GET_NORM_TAG(op2);+ if (tag_is_number(tag1) && tag_is_number(tag2)) {+ if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {+ res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2);+ } else if ((tag1 == JS_TAG_FLOAT64 &&+ (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64)) ||+ (tag2 == JS_TAG_FLOAT64 &&+ (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64))) {+ double d1, d2;+ if (tag1 == JS_TAG_FLOAT64) {+ d1 = JS_VALUE_GET_FLOAT64(op1);+ } else {+ d1 = JS_VALUE_GET_INT(op1);+ }+ if (tag2 == JS_TAG_FLOAT64) {+ d2 = JS_VALUE_GET_FLOAT64(op2);+ } else {+ d2 = JS_VALUE_GET_INT(op2);+ }+ res = (d1 == d2);+ } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {+ res = ctx->rt->bigdecimal_ops.compare(ctx, OP_eq, op1, op2);+ if (res < 0)+ goto exception;+ } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {+ res = ctx->rt->bigfloat_ops.compare(ctx, OP_eq, op1, op2);+ if (res < 0)+ goto exception;+ } else {+ res = ctx->rt->bigint_ops.compare(ctx, OP_eq, op1, op2);+ if (res < 0)+ goto exception;+ }+ } else if (tag1 == tag2) {+ if (tag1 == JS_TAG_OBJECT) {+ /* try the fallback operator */+ res = js_call_binary_op_fallback(ctx, &ret, op1, op2,+ is_neq ? OP_neq : OP_eq,+ FALSE, HINT_NONE);+ if (res != 0) {+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ if (res < 0) {+ goto exception;+ } else {+ sp[-2] = ret;+ return 0;+ }+ }+ }+ res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);+ } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) ||+ (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) {+ res = TRUE;+ } else if ((tag1 == JS_TAG_STRING && tag_is_number(tag2)) ||+ (tag2 == JS_TAG_STRING && tag_is_number(tag1))) {++ if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) &&+ !is_math_mode(ctx)) {+ if (tag1 == JS_TAG_STRING) {+ op1 = JS_StringToBigInt(ctx, op1);+ if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT)+ goto invalid_bigint_string;+ }+ if (tag2 == JS_TAG_STRING) {+ op2 = JS_StringToBigInt(ctx, op2);+ if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) {+ invalid_bigint_string:+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ res = FALSE;+ goto done;+ }+ }+ } else {+ op1 = JS_ToNumericFree(ctx, op1);+ if (JS_IsException(op1)) {+ JS_FreeValue(ctx, op2);+ goto exception;+ }+ op2 = JS_ToNumericFree(ctx, op2);+ if (JS_IsException(op2)) {+ JS_FreeValue(ctx, op1);+ goto exception;+ }+ }+ res = js_strict_eq(ctx, op1, op2);+ } else if (tag1 == JS_TAG_BOOL) {+ op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1));+ goto redo;+ } else if (tag2 == JS_TAG_BOOL) {+ op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2));+ goto redo;+ } else if ((tag1 == JS_TAG_OBJECT &&+ (tag_is_number(tag2) || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) ||+ (tag2 == JS_TAG_OBJECT &&+ (tag_is_number(tag1) || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL))) {++ /* try the fallback operator */+ res = js_call_binary_op_fallback(ctx, &ret, op1, op2,+ is_neq ? OP_neq : OP_eq,+ FALSE, HINT_NONE);+ if (res != 0) {+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ if (res < 0) {+ goto exception;+ } else {+ sp[-2] = ret;+ return 0;+ }+ }++ op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);+ if (JS_IsException(op1)) {+ JS_FreeValue(ctx, op2);+ goto exception;+ }+ op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);+ if (JS_IsException(op2)) {+ JS_FreeValue(ctx, op1);+ goto exception;+ }+ goto redo;+ } else {+ /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */+ if ((JS_IsHTMLDDA(ctx, op1) &&+ (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) ||+ (JS_IsHTMLDDA(ctx, op2) &&+ (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) {+ res = TRUE;+ } else {+ res = FALSE;+ }+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ }+ done:+ sp[-2] = JS_NewBool(ctx, res ^ is_neq);+ return 0;+ exception:+ sp[-2] = JS_UNDEFINED;+ sp[-1] = JS_UNDEFINED;+ return -1;+}++static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp)+{+ JSValue op1, op2;+ uint32_t v1, v2, r;++ op1 = sp[-2];+ op2 = sp[-1];+ op1 = JS_ToNumericFree(ctx, op1);+ if (JS_IsException(op1)) {+ JS_FreeValue(ctx, op2);+ goto exception;+ }+ op2 = JS_ToNumericFree(ctx, op2);+ if (JS_IsException(op2)) {+ JS_FreeValue(ctx, op1);+ goto exception;+ }+ /* XXX: could forbid >>> in bignum mode */+ if (!is_math_mode(ctx) &&+ (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT ||+ JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT)) {+ JS_ThrowTypeError(ctx, "bigint operands are forbidden for >>>");+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ goto exception;+ }+ /* cannot give an exception */+ JS_ToUint32Free(ctx, &v1, op1);+ JS_ToUint32Free(ctx, &v2, op2);+ r = v1 >> (v2 & 0x1f);+ sp[-2] = JS_NewUint32(ctx, r);+ return 0;+ exception:+ sp[-2] = JS_UNDEFINED;+ sp[-1] = JS_UNDEFINED;+ return -1;+}++static JSValue js_mul_pow10_to_float64(JSContext *ctx, const bf_t *a,+ int64_t exponent)+{+ bf_t r_s, *r = &r_s;+ double d;+ int ret;+ + /* always convert to Float64 */+ bf_init(ctx->bf_ctx, r);+ ret = bf_mul_pow_radix(r, a, 10, exponent,+ 53, bf_set_exp_bits(11) | BF_RNDN |+ BF_FLAG_SUBNORMAL);+ bf_get_float64(r, &d, BF_RNDN);+ bf_delete(r);+ if (ret & BF_ST_MEM_ERROR)+ return JS_ThrowOutOfMemory(ctx);+ else+ return __JS_NewFloat64(ctx, d);+}++static no_inline int js_mul_pow10(JSContext *ctx, JSValue *sp)+{+ bf_t a_s, *a, *r;+ JSValue op1, op2, res;+ int64_t e;+ int ret;++ res = JS_NewBigFloat(ctx);+ if (JS_IsException(res))+ return -1;+ r = JS_GetBigFloat(res);+ op1 = sp[-2];+ op2 = sp[-1];+ a = JS_ToBigFloat(ctx, &a_s, op1);+ if (!a)+ return -1;+ if (JS_IsBigInt(ctx, op2)) {+ ret = JS_ToBigInt64(ctx, &e, op2);+ } else {+ ret = JS_ToInt64(ctx, &e, op2);+ }+ if (ret) {+ if (a == &a_s)+ bf_delete(a);+ JS_FreeValue(ctx, res);+ return -1;+ }++ bf_mul_pow_radix(r, a, 10, e, ctx->fp_env.prec, ctx->fp_env.flags);+ if (a == &a_s)+ bf_delete(a);+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ sp[-2] = res;+ return 0;+}++#else /* !CONFIG_BIGNUM */++static JSValue JS_ThrowUnsupportedBigint(JSContext *ctx)+{+ return JS_ThrowTypeError(ctx, "bigint is not supported");+}++JSValue JS_NewBigInt64(JSContext *ctx, int64_t v)+{+ return JS_ThrowUnsupportedBigint(ctx);+}++JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v)+{+ return JS_ThrowUnsupportedBigint(ctx);+}++int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)+{+ JS_ThrowUnsupportedBigint(ctx);+ *pres = 0;+ return -1;+}++static no_inline __exception int js_unary_arith_slow(JSContext *ctx,+ JSValue *sp,+ OPCodeEnum op)+{+ JSValue op1;+ double d;++ op1 = sp[-1];+ if (unlikely(JS_ToFloat64Free(ctx, &d, op1))) {+ sp[-1] = JS_UNDEFINED;+ return -1;+ }+ switch(op) {+ case OP_inc:+ d++;+ break;+ case OP_dec:+ d--;+ break;+ case OP_plus:+ break;+ case OP_neg:+ d = -d;+ break;+ default:+ abort();+ }+ sp[-1] = JS_NewFloat64(ctx, d);+ return 0;+}++/* specific case necessary for correct return value semantics */+static __exception int js_post_inc_slow(JSContext *ctx,+ JSValue *sp, OPCodeEnum op)+{+ JSValue op1;+ double d, r;++ op1 = sp[-1];+ if (unlikely(JS_ToFloat64Free(ctx, &d, op1))) {+ sp[-1] = JS_UNDEFINED;+ return -1;+ }+ r = d + 2 * (op - OP_post_dec) - 1;+ sp[0] = JS_NewFloat64(ctx, r);+ sp[-1] = JS_NewFloat64(ctx, d);+ return 0;+}++static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp,+ OPCodeEnum op)+{+ JSValue op1, op2;+ double d1, d2, r;++ op1 = sp[-2];+ op2 = sp[-1];+ if (unlikely(JS_ToFloat64Free(ctx, &d1, op1))) {+ JS_FreeValue(ctx, op2);+ goto exception;+ }+ if (unlikely(JS_ToFloat64Free(ctx, &d2, op2))) {+ goto exception;+ }+ switch(op) {+ case OP_sub:+ r = d1 - d2;+ break;+ case OP_mul:+ r = d1 * d2;+ break;+ case OP_div:+ r = d1 / d2;+ break;+ case OP_mod:+ r = fmod(d1, d2);+ break;+ case OP_pow:+ r = js_pow(d1, d2);+ break;+ default:+ abort();+ }+ sp[-2] = JS_NewFloat64(ctx, r);+ return 0;+ exception:+ sp[-2] = JS_UNDEFINED;+ sp[-1] = JS_UNDEFINED;+ return -1;+}++static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)+{+ JSValue op1, op2;+ uint32_t tag1, tag2;++ op1 = sp[-2];+ op2 = sp[-1];+ tag1 = JS_VALUE_GET_TAG(op1);+ tag2 = JS_VALUE_GET_TAG(op2);+ if ((tag1 == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag1)) &&+ (tag2 == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag2))) {+ goto add_numbers;+ } else {+ op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);+ if (JS_IsException(op1)) {+ JS_FreeValue(ctx, op2);+ goto exception;+ }+ op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);+ if (JS_IsException(op2)) {+ JS_FreeValue(ctx, op1);+ goto exception;+ }+ tag1 = JS_VALUE_GET_TAG(op1);+ tag2 = JS_VALUE_GET_TAG(op2);+ if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) {+ sp[-2] = JS_ConcatString(ctx, op1, op2);+ if (JS_IsException(sp[-2]))+ goto exception;+ } else {+ double d1, d2;+ add_numbers:+ if (JS_ToFloat64Free(ctx, &d1, op1)) {+ JS_FreeValue(ctx, op2);+ goto exception;+ }+ if (JS_ToFloat64Free(ctx, &d2, op2))+ goto exception;+ sp[-2] = JS_NewFloat64(ctx, d1 + d2);+ }+ }+ return 0;+ exception:+ sp[-2] = JS_UNDEFINED;+ sp[-1] = JS_UNDEFINED;+ return -1;+}++static no_inline __exception int js_binary_logic_slow(JSContext *ctx,+ JSValue *sp,+ OPCodeEnum op)+{+ JSValue op1, op2;+ uint32_t v1, v2, r;++ op1 = sp[-2];+ op2 = sp[-1];+ if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) {+ JS_FreeValue(ctx, op2);+ goto exception;+ }+ if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2)))+ goto exception;+ switch(op) {+ case OP_shl:+ r = v1 << (v2 & 0x1f);+ break;+ case OP_sar:+ r = (int)v1 >> (v2 & 0x1f);+ break;+ case OP_and:+ r = v1 & v2;+ break;+ case OP_or:+ r = v1 | v2;+ break;+ case OP_xor:+ r = v1 ^ v2;+ break;+ default:+ abort();+ }+ sp[-2] = JS_NewInt32(ctx, r);+ return 0;+ exception:+ sp[-2] = JS_UNDEFINED;+ sp[-1] = JS_UNDEFINED;+ return -1;+}++static no_inline int js_not_slow(JSContext *ctx, JSValue *sp)+{+ int32_t v1;++ if (unlikely(JS_ToInt32Free(ctx, &v1, sp[-1]))) {+ sp[-1] = JS_UNDEFINED;+ return -1;+ }+ sp[-1] = JS_NewInt32(ctx, ~v1);+ return 0;+}++static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,+ OPCodeEnum op)+{+ JSValue op1, op2;+ int res;++ op1 = sp[-2];+ op2 = sp[-1];+ op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER);+ if (JS_IsException(op1)) {+ JS_FreeValue(ctx, op2);+ goto exception;+ }+ op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER);+ if (JS_IsException(op2)) {+ JS_FreeValue(ctx, op1);+ goto exception;+ }+ if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING &&+ JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) {+ JSString *p1, *p2;+ p1 = JS_VALUE_GET_STRING(op1);+ p2 = JS_VALUE_GET_STRING(op2);+ res = js_string_compare(ctx, p1, p2);+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ switch(op) {+ case OP_lt:+ res = (res < 0);+ break;+ case OP_lte:+ res = (res <= 0);+ break;+ case OP_gt:+ res = (res > 0);+ break;+ default:+ case OP_gte:+ res = (res >= 0);+ break;+ }+ } else {+ double d1, d2;+ if (JS_ToFloat64Free(ctx, &d1, op1)) {+ JS_FreeValue(ctx, op2);+ goto exception;+ }+ if (JS_ToFloat64Free(ctx, &d2, op2))+ goto exception;+ switch(op) {+ case OP_lt:+ res = (d1 < d2); /* if NaN return false */+ break;+ case OP_lte:+ res = (d1 <= d2); /* if NaN return false */+ break;+ case OP_gt:+ res = (d1 > d2); /* if NaN return false */+ break;+ default:+ case OP_gte:+ res = (d1 >= d2); /* if NaN return false */+ break;+ }+ }+ sp[-2] = JS_NewBool(ctx, res);+ return 0;+ exception:+ sp[-2] = JS_UNDEFINED;+ sp[-1] = JS_UNDEFINED;+ return -1;+}++static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,+ BOOL is_neq)+{+ JSValue op1, op2;+ int tag1, tag2;+ BOOL res;++ op1 = sp[-2];+ op2 = sp[-1];+ redo:+ tag1 = JS_VALUE_GET_NORM_TAG(op1);+ tag2 = JS_VALUE_GET_NORM_TAG(op2);+ if (tag1 == tag2 ||+ (tag1 == JS_TAG_INT && tag2 == JS_TAG_FLOAT64) ||+ (tag2 == JS_TAG_INT && tag1 == JS_TAG_FLOAT64)) {+ res = js_strict_eq(ctx, op1, op2);+ } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) ||+ (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) {+ res = TRUE;+ } else if ((tag1 == JS_TAG_STRING && (tag2 == JS_TAG_INT ||+ tag2 == JS_TAG_FLOAT64)) ||+ (tag2 == JS_TAG_STRING && (tag1 == JS_TAG_INT ||+ tag1 == JS_TAG_FLOAT64))) {+ double d1;+ double d2;+ if (JS_ToFloat64Free(ctx, &d1, op1)) {+ JS_FreeValue(ctx, op2);+ goto exception;+ }+ if (JS_ToFloat64Free(ctx, &d2, op2))+ goto exception;+ res = (d1 == d2);+ } else if (tag1 == JS_TAG_BOOL) {+ op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1));+ goto redo;+ } else if (tag2 == JS_TAG_BOOL) {+ op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2));+ goto redo;+ } else if (tag1 == JS_TAG_OBJECT &&+ (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64 || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) {+ op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);+ if (JS_IsException(op1)) {+ JS_FreeValue(ctx, op2);+ goto exception;+ }+ goto redo;+ } else if (tag2 == JS_TAG_OBJECT &&+ (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64 || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL)) {+ op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);+ if (JS_IsException(op2)) {+ JS_FreeValue(ctx, op1);+ goto exception;+ }+ goto redo;+ } else {+ /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */+ if ((JS_IsHTMLDDA(ctx, op1) &&+ (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) ||+ (JS_IsHTMLDDA(ctx, op2) &&+ (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) {+ res = TRUE;+ } else {+ res = FALSE;+ }+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ }+ sp[-2] = JS_NewBool(ctx, res ^ is_neq);+ return 0;+ exception:+ sp[-2] = JS_UNDEFINED;+ sp[-1] = JS_UNDEFINED;+ return -1;+}++static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp)+{+ JSValue op1, op2;+ uint32_t v1, v2, r;++ op1 = sp[-2];+ op2 = sp[-1];+ if (unlikely(JS_ToUint32Free(ctx, &v1, op1))) {+ JS_FreeValue(ctx, op2);+ goto exception;+ }+ if (unlikely(JS_ToUint32Free(ctx, &v2, op2)))+ goto exception;+ r = v1 >> (v2 & 0x1f);+ sp[-2] = JS_NewUint32(ctx, r);+ return 0;+ exception:+ sp[-2] = JS_UNDEFINED;+ sp[-1] = JS_UNDEFINED;+ return -1;+}++#endif /* !CONFIG_BIGNUM */++/* XXX: Should take JSValueConst arguments */+static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,+ JSStrictEqModeEnum eq_mode)+{+ BOOL res;+ int tag1, tag2;+ double d1, d2;++ tag1 = JS_VALUE_GET_NORM_TAG(op1);+ tag2 = JS_VALUE_GET_NORM_TAG(op2);+ switch(tag1) {+ case JS_TAG_BOOL:+ if (tag1 != tag2) {+ res = FALSE;+ } else {+ res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2);+ goto done_no_free;+ }+ break;+ case JS_TAG_NULL:+ case JS_TAG_UNDEFINED:+ res = (tag1 == tag2);+ break;+ case JS_TAG_STRING:+ {+ JSString *p1, *p2;+ if (tag1 != tag2) {+ res = FALSE;+ } else {+ p1 = JS_VALUE_GET_STRING(op1);+ p2 = JS_VALUE_GET_STRING(op2);+ res = (js_string_compare(ctx, p1, p2) == 0);+ }+ }+ break;+ case JS_TAG_SYMBOL:+ {+ JSAtomStruct *p1, *p2;+ if (tag1 != tag2) {+ res = FALSE;+ } else {+ p1 = JS_VALUE_GET_PTR(op1);+ p2 = JS_VALUE_GET_PTR(op2);+ res = (p1 == p2);+ }+ }+ break;+ case JS_TAG_OBJECT:+ if (tag1 != tag2)+ res = FALSE;+ else+ res = JS_VALUE_GET_OBJ(op1) == JS_VALUE_GET_OBJ(op2);+ break;+ case JS_TAG_INT:+ d1 = JS_VALUE_GET_INT(op1);+ if (tag2 == JS_TAG_INT) {+ d2 = JS_VALUE_GET_INT(op2);+ goto number_test;+ } else if (tag2 == JS_TAG_FLOAT64) {+ d2 = JS_VALUE_GET_FLOAT64(op2);+ goto number_test;+ } else {+ res = FALSE;+ }+ break;+ case JS_TAG_FLOAT64:+ d1 = JS_VALUE_GET_FLOAT64(op1);+ if (tag2 == JS_TAG_FLOAT64) {+ d2 = JS_VALUE_GET_FLOAT64(op2);+ } else if (tag2 == JS_TAG_INT) {+ d2 = JS_VALUE_GET_INT(op2);+ } else {+ res = FALSE;+ break;+ }+ number_test:+ if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) {+ JSFloat64Union u1, u2;+ /* NaN is not always normalized, so this test is necessary */+ if (isnan(d1) || isnan(d2)) {+ res = isnan(d1) == isnan(d2);+ } else if (eq_mode == JS_EQ_SAME_VALUE_ZERO) {+ res = (d1 == d2); /* +0 == -0 */+ } else {+ u1.d = d1;+ u2.d = d2;+ res = (u1.u64 == u2.u64); /* +0 != -0 */+ }+ } else {+ res = (d1 == d2); /* if NaN return false and +0 == -0 */+ }+ goto done_no_free;+#ifdef CONFIG_BIGNUM+ case JS_TAG_BIG_INT:+ {+ bf_t a_s, *a, b_s, *b;+ if (tag1 != tag2) {+ res = FALSE;+ break;+ }+ a = JS_ToBigFloat(ctx, &a_s, op1);+ b = JS_ToBigFloat(ctx, &b_s, op2);+ res = bf_cmp_eq(a, b);+ if (a == &a_s)+ bf_delete(a);+ if (b == &b_s)+ bf_delete(b);+ }+ break;+ case JS_TAG_BIG_FLOAT:+ {+ JSBigFloat *p1, *p2;+ const bf_t *a, *b;+ if (tag1 != tag2) {+ res = FALSE;+ break;+ }+ p1 = JS_VALUE_GET_PTR(op1);+ p2 = JS_VALUE_GET_PTR(op2);+ a = &p1->num;+ b = &p2->num;+ if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) {+ if (eq_mode == JS_EQ_SAME_VALUE_ZERO &&+ a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO) {+ res = TRUE;+ } else {+ res = (bf_cmp_full(a, b) == 0);+ }+ } else {+ res = bf_cmp_eq(a, b);+ }+ }+ break;+ case JS_TAG_BIG_DECIMAL:+ {+ JSBigDecimal *p1, *p2;+ const bfdec_t *a, *b;+ if (tag1 != tag2) {+ res = FALSE;+ break;+ }+ p1 = JS_VALUE_GET_PTR(op1);+ p2 = JS_VALUE_GET_PTR(op2);+ a = &p1->num;+ b = &p2->num;+ res = bfdec_cmp_eq(a, b);+ }+ break;+#endif+ default:+ res = FALSE;+ break;+ }+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ done_no_free:+ return res;+}++static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2)+{+ return js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);+}++static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2)+{+ return js_strict_eq2(ctx,+ JS_DupValue(ctx, op1), JS_DupValue(ctx, op2),+ JS_EQ_SAME_VALUE);+}++static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2)+{+ return js_strict_eq2(ctx,+ JS_DupValue(ctx, op1), JS_DupValue(ctx, op2),+ JS_EQ_SAME_VALUE_ZERO);+}++static no_inline int js_strict_eq_slow(JSContext *ctx, JSValue *sp,+ BOOL is_neq)+{+ BOOL res;+ res = js_strict_eq(ctx, sp[-2], sp[-1]);+ sp[-2] = JS_NewBool(ctx, res ^ is_neq);+ return 0;+}++static __exception int js_operator_in(JSContext *ctx, JSValue *sp)+{+ JSValue op1, op2;+ JSAtom atom;+ int ret;++ op1 = sp[-2];+ op2 = sp[-1];++ if (JS_VALUE_GET_TAG(op2) != JS_TAG_OBJECT) {+ JS_ThrowTypeError(ctx, "invalid 'in' operand");+ return -1;+ }+ atom = JS_ValueToAtom(ctx, op1);+ if (unlikely(atom == JS_ATOM_NULL))+ return -1;+ ret = JS_HasProperty(ctx, op2, atom);+ JS_FreeAtom(ctx, atom);+ if (ret < 0)+ return -1;+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ sp[-2] = JS_NewBool(ctx, ret);+ return 0;+}++static __exception int js_has_unscopable(JSContext *ctx, JSValueConst obj,+ JSAtom atom)+{+ JSValue arr, val;+ int ret;++ arr = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_unscopables);+ if (JS_IsException(arr))+ return -1;+ ret = 0;+ if (JS_IsObject(arr)) {+ val = JS_GetProperty(ctx, arr, atom);+ ret = JS_ToBoolFree(ctx, val);+ }+ JS_FreeValue(ctx, arr);+ return ret;+}++static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp)+{+ JSValue op1, op2;+ BOOL ret;++ op1 = sp[-2];+ op2 = sp[-1];+ ret = JS_IsInstanceOf(ctx, op1, op2);+ if (ret < 0)+ return ret;+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ sp[-2] = JS_NewBool(ctx, ret);+ return 0;+}++static __exception int js_operator_typeof(JSContext *ctx, JSValueConst op1)+{+ JSAtom atom;+ uint32_t tag;++ tag = JS_VALUE_GET_NORM_TAG(op1);+ switch(tag) {+#ifdef CONFIG_BIGNUM+ case JS_TAG_BIG_INT:+ atom = JS_ATOM_bigint;+ break;+ case JS_TAG_BIG_FLOAT:+ atom = JS_ATOM_bigfloat;+ break;+ case JS_TAG_BIG_DECIMAL:+ atom = JS_ATOM_bigdecimal;+ break;+#endif+ case JS_TAG_INT:+ case JS_TAG_FLOAT64:+ atom = JS_ATOM_number;+ break;+ case JS_TAG_UNDEFINED:+ atom = JS_ATOM_undefined;+ break;+ case JS_TAG_BOOL:+ atom = JS_ATOM_boolean;+ break;+ case JS_TAG_STRING:+ atom = JS_ATOM_string;+ break;+ case JS_TAG_OBJECT:+ {+ JSObject *p;+ p = JS_VALUE_GET_OBJ(op1);+ if (unlikely(p->is_HTMLDDA)) + atom = JS_ATOM_undefined;+ else if (JS_IsFunction(ctx, op1))+ atom = JS_ATOM_function;+ else+ goto obj_type;+ }+ break;+ case JS_TAG_NULL:+ obj_type:+ atom = JS_ATOM_object;+ break;+ case JS_TAG_SYMBOL:+ atom = JS_ATOM_symbol;+ break;+ default:+ atom = JS_ATOM_unknown;+ break;+ }+ return atom;+}++static __exception int js_operator_delete(JSContext *ctx, JSValue *sp)+{+ JSValue op1, op2;+ JSAtom atom;+ int ret;++ op1 = sp[-2];+ op2 = sp[-1];+ atom = JS_ValueToAtom(ctx, op2);+ if (unlikely(atom == JS_ATOM_NULL))+ return -1;+ ret = JS_DeleteProperty(ctx, op1, atom, JS_PROP_THROW_STRICT);+ JS_FreeAtom(ctx, atom);+ if (unlikely(ret < 0))+ return -1;+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ sp[-2] = JS_NewBool(ctx, ret);+ return 0;+}++static JSValue js_throw_type_error(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return JS_ThrowTypeError(ctx, "invalid property access");+}++/* XXX: not 100% compatible, but mozilla seems to use a similar+ implementation to ensure that caller in non strict mode does not+ throw (ES5 compatibility) */+static JSValue js_function_proto_caller(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);+ if (!b || (b->js_mode & JS_MODE_STRICT) || !b->has_prototype) {+ return js_throw_type_error(ctx, this_val, 0, NULL);+ }+ return JS_UNDEFINED;+}++static JSValue js_function_proto_fileName(JSContext *ctx,+ JSValueConst this_val)+{+ JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);+ if (b && b->has_debug) {+ return JS_AtomToString(ctx, b->debug.filename);+ }+ return JS_UNDEFINED;+}++static JSValue js_function_proto_lineNumber(JSContext *ctx,+ JSValueConst this_val)+{+ JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);+ if (b && b->has_debug) {+ return JS_NewInt32(ctx, b->debug.line_num);+ }+ return JS_UNDEFINED;+}++static int js_arguments_define_own_property(JSContext *ctx,+ JSValueConst this_obj,+ JSAtom prop, JSValueConst val,+ JSValueConst getter, JSValueConst setter, int flags)+{+ JSObject *p;+ uint32_t idx;+ p = JS_VALUE_GET_OBJ(this_obj);+ /* convert to normal array when redefining an existing numeric field */+ if (p->fast_array && JS_AtomIsArrayIndex(ctx, &idx, prop) &&+ idx < p->u.array.count) {+ if (convert_fast_array_to_array(ctx, p))+ return -1;+ }+ /* run the default define own property */+ return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter,+ flags | JS_PROP_NO_EXOTIC);+}++static const JSClassExoticMethods js_arguments_exotic_methods = {+ .define_own_property = js_arguments_define_own_property,+};++static JSValue js_build_arguments(JSContext *ctx, int argc, JSValueConst *argv)+{+ JSValue val, *tab;+ JSProperty *pr;+ JSObject *p;+ int i;++ val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],+ JS_CLASS_ARGUMENTS);+ if (JS_IsException(val))+ return val;+ p = JS_VALUE_GET_OBJ(val);++ /* add the length field (cannot fail) */+ pr = add_property(ctx, p, JS_ATOM_length,+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+ pr->u.value = JS_NewInt32(ctx, argc);++ /* initialize the fast array part */+ tab = NULL;+ if (argc > 0) {+ tab = js_malloc(ctx, sizeof(tab[0]) * argc);+ if (!tab) {+ JS_FreeValue(ctx, val);+ return JS_EXCEPTION;+ }+ for(i = 0; i < argc; i++) {+ tab[i] = JS_DupValue(ctx, argv[i]);+ }+ }+ p->u.array.u.values = tab;+ p->u.array.count = argc;++ JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator,+ JS_DupValue(ctx, ctx->array_proto_values),+ JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);+ /* add callee property to throw a TypeError in strict mode */+ JS_DefineProperty(ctx, val, JS_ATOM_callee, JS_UNDEFINED,+ ctx->throw_type_error, ctx->throw_type_error,+ JS_PROP_HAS_GET | JS_PROP_HAS_SET);+ return val;+}++#define GLOBAL_VAR_OFFSET 0x40000000+#define ARGUMENT_VAR_OFFSET 0x20000000++/* legacy arguments object: add references to the function arguments */+static JSValue js_build_mapped_arguments(JSContext *ctx, int argc,+ JSValueConst *argv,+ JSStackFrame *sf, int arg_count)+{+ JSValue val;+ JSProperty *pr;+ JSObject *p;+ int i;++ val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],+ JS_CLASS_MAPPED_ARGUMENTS);+ if (JS_IsException(val))+ return val;+ p = JS_VALUE_GET_OBJ(val);++ /* add the length field (cannot fail) */+ pr = add_property(ctx, p, JS_ATOM_length,+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+ pr->u.value = JS_NewInt32(ctx, argc);++ for(i = 0; i < arg_count; i++) {+ JSVarRef *var_ref;+ var_ref = get_var_ref(ctx, sf, i, TRUE);+ if (!var_ref)+ goto fail;+ pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF);+ if (!pr) {+ free_var_ref(ctx->rt, var_ref);+ goto fail;+ }+ pr->u.var_ref = var_ref;+ }++ /* the arguments not mapped to the arguments of the function can+ be normal properties */+ for(i = arg_count; i < argc; i++) {+ if (JS_DefinePropertyValueUint32(ctx, val, i,+ JS_DupValue(ctx, argv[i]),+ JS_PROP_C_W_E) < 0)+ goto fail;+ }++ JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator,+ JS_DupValue(ctx, ctx->array_proto_values),+ JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);+ /* callee returns this function in non strict mode */+ JS_DefinePropertyValue(ctx, val, JS_ATOM_callee,+ JS_DupValue(ctx, ctx->rt->current_stack_frame->cur_func),+ JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);+ return val;+ fail:+ JS_FreeValue(ctx, val);+ return JS_EXCEPTION;+}++static JSValue js_build_rest(JSContext *ctx, int first, int argc, JSValueConst *argv)+{+ JSValue val;+ int i, ret;++ val = JS_NewArray(ctx);+ if (JS_IsException(val))+ return val;+ for (i = first; i < argc; i++) {+ ret = JS_DefinePropertyValueUint32(ctx, val, i - first,+ JS_DupValue(ctx, argv[i]),+ JS_PROP_C_W_E);+ if (ret < 0) {+ JS_FreeValue(ctx, val);+ return JS_EXCEPTION;+ }+ }+ return val;+}++static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj)+{+ JSObject *p, *p1;+ JSPropertyEnum *tab_atom;+ int i;+ JSValue enum_obj;+ JSForInIterator *it;+ uint32_t tag, tab_atom_count;++ tag = JS_VALUE_GET_TAG(obj);+ if (tag != JS_TAG_OBJECT && tag != JS_TAG_NULL && tag != JS_TAG_UNDEFINED) {+ obj = JS_ToObjectFree(ctx, obj);+ }++ it = js_malloc(ctx, sizeof(*it));+ if (!it) {+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+ }+ enum_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_FOR_IN_ITERATOR);+ if (JS_IsException(enum_obj)) {+ js_free(ctx, it);+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+ }+ it->is_array = FALSE;+ it->obj = obj;+ it->idx = 0;+ p = JS_VALUE_GET_OBJ(enum_obj);+ p->u.for_in_iterator = it;++ if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)+ return enum_obj;++ p = JS_VALUE_GET_OBJ(obj);++ /* fast path: assume no enumerable properties in the prototype chain */+ p1 = p->shape->proto;+ while (p1 != NULL) {+ if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p1,+ JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY))+ goto fail;+ js_free_prop_enum(ctx, tab_atom, tab_atom_count);+ if (tab_atom_count != 0) {+ goto slow_path;+ }+ p1 = p1->shape->proto;+ }+ if (p->fast_array) {+ JSShape *sh;+ JSShapeProperty *prs;+ /* check that there are no enumerable normal fields */+ sh = p->shape;+ for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {+ if (prs->flags & JS_PROP_ENUMERABLE)+ goto normal_case;+ }+ /* for fast arrays, we only store the number of elements */+ it->is_array = TRUE;+ it->array_length = p->u.array.count;+ } else {+ normal_case:+ if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,+ JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY))+ goto fail;+ for(i = 0; i < tab_atom_count; i++) {+ JS_SetPropertyInternal(ctx, enum_obj, tab_atom[i].atom, JS_NULL, 0);+ }+ js_free_prop_enum(ctx, tab_atom, tab_atom_count);+ }+ return enum_obj;++ slow_path:+ /* non enumerable properties hide the enumerables ones in the+ prototype chain */+ while (p != NULL) {+ if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,+ JS_GPN_STRING_MASK | JS_GPN_SET_ENUM))+ goto fail;+ for(i = 0; i < tab_atom_count; i++) {+ JS_DefinePropertyValue(ctx, enum_obj, tab_atom[i].atom, JS_NULL,+ (tab_atom[i].is_enumerable ?+ JS_PROP_ENUMERABLE : 0));+ }+ js_free_prop_enum(ctx, tab_atom, tab_atom_count);+ p = p->shape->proto;+ }+ return enum_obj;++ fail:+ JS_FreeValue(ctx, enum_obj);+ return JS_EXCEPTION;+}++/* obj -> enum_obj */+static __exception int js_for_in_start(JSContext *ctx, JSValue *sp)+{+ sp[-1] = build_for_in_iterator(ctx, sp[-1]);+ if (JS_IsException(sp[-1]))+ return -1;+ return 0;+}++/* enum_obj -> enum_obj value done */+static __exception int js_for_in_next(JSContext *ctx, JSValue *sp)+{+ JSValueConst enum_obj;+ JSObject *p;+ JSAtom prop;+ JSForInIterator *it;+ int ret;++ enum_obj = sp[-1];+ /* fail safe */+ if (JS_VALUE_GET_TAG(enum_obj) != JS_TAG_OBJECT)+ goto done;+ p = JS_VALUE_GET_OBJ(enum_obj);+ if (p->class_id != JS_CLASS_FOR_IN_ITERATOR)+ goto done;+ it = p->u.for_in_iterator;++ for(;;) {+ if (it->is_array) {+ if (it->idx >= it->array_length)+ goto done;+ prop = __JS_AtomFromUInt32(it->idx);+ it->idx++;+ } else {+ JSShape *sh = p->shape;+ JSShapeProperty *prs;+ if (it->idx >= sh->prop_count)+ goto done;+ prs = get_shape_prop(sh) + it->idx;+ prop = prs->atom;+ it->idx++;+ if (prop == JS_ATOM_NULL || !(prs->flags & JS_PROP_ENUMERABLE))+ continue;+ }+ /* check if the property was deleted */+ ret = JS_HasProperty(ctx, it->obj, prop);+ if (ret < 0)+ return ret;+ if (ret)+ break;+ }+ /* return the property */+ sp[0] = JS_AtomToValue(ctx, prop);+ sp[1] = JS_FALSE;+ return 0;+ done:+ /* return the end */+ sp[0] = JS_UNDEFINED;+ sp[1] = JS_TRUE;+ return 0;+}++static JSValue JS_GetIterator2(JSContext *ctx, JSValueConst obj,+ JSValueConst method)+{+ JSValue enum_obj;++ enum_obj = JS_Call(ctx, method, obj, 0, NULL);+ if (JS_IsException(enum_obj))+ return enum_obj;+ if (!JS_IsObject(enum_obj)) {+ JS_FreeValue(ctx, enum_obj);+ return JS_ThrowTypeErrorNotAnObject(ctx);+ }+ return enum_obj;+}++static JSValue JS_GetIterator(JSContext *ctx, JSValueConst obj, BOOL is_async)+{+ JSValue method, ret, sync_iter;++ if (is_async) {+ method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_asyncIterator);+ if (JS_IsException(method))+ return method;+ if (JS_IsUndefined(method) || JS_IsNull(method)) {+ method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);+ if (JS_IsException(method))+ return method;+ sync_iter = JS_GetIterator2(ctx, obj, method);+ JS_FreeValue(ctx, method);+ if (JS_IsException(sync_iter))+ return sync_iter;+ ret = JS_CreateAsyncFromSyncIterator(ctx, sync_iter);+ JS_FreeValue(ctx, sync_iter);+ return ret;+ }+ } else {+ method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);+ if (JS_IsException(method))+ return method;+ }+ if (!JS_IsFunction(ctx, method)) {+ JS_FreeValue(ctx, method);+ return JS_ThrowTypeError(ctx, "value is not iterable");+ }+ ret = JS_GetIterator2(ctx, obj, method);+ JS_FreeValue(ctx, method);+ return ret;+}++/* return *pdone = 2 if the iterator object is not parsed */+static JSValue JS_IteratorNext2(JSContext *ctx, JSValueConst enum_obj,+ JSValueConst method,+ int argc, JSValueConst *argv, int *pdone)+{+ JSValue obj;++ /* fast path for the built-in iterators (avoid creating the+ intermediate result object) */+ if (JS_IsObject(method)) {+ JSObject *p = JS_VALUE_GET_OBJ(method);+ if (p->class_id == JS_CLASS_C_FUNCTION &&+ p->u.cfunc.cproto == JS_CFUNC_iterator_next) {+ JSCFunctionType func;+ JSValueConst args[1];++ /* in case the function expects one argument */+ if (argc == 0) {+ args[0] = JS_UNDEFINED;+ argv = args;+ }+ func = p->u.cfunc.c_function;+ return func.iterator_next(ctx, enum_obj, argc, argv,+ pdone, p->u.cfunc.magic);+ }+ }+ obj = JS_Call(ctx, method, enum_obj, argc, argv);+ if (JS_IsException(obj))+ goto fail;+ if (!JS_IsObject(obj)) {+ JS_FreeValue(ctx, obj);+ JS_ThrowTypeError(ctx, "iterator must return an object");+ goto fail;+ }+ *pdone = 2;+ return obj;+ fail:+ *pdone = FALSE;+ return JS_EXCEPTION;+}++static JSValue JS_IteratorNext(JSContext *ctx, JSValueConst enum_obj,+ JSValueConst method,+ int argc, JSValueConst *argv, BOOL *pdone)+{+ JSValue obj, value, done_val;+ int done;++ obj = JS_IteratorNext2(ctx, enum_obj, method, argc, argv, &done);+ if (JS_IsException(obj))+ goto fail;+ if (done != 2) {+ *pdone = done;+ return obj;+ } else {+ done_val = JS_GetProperty(ctx, obj, JS_ATOM_done);+ if (JS_IsException(done_val))+ goto fail;+ *pdone = JS_ToBoolFree(ctx, done_val);+ value = JS_UNDEFINED;+ if (!*pdone) {+ value = JS_GetProperty(ctx, obj, JS_ATOM_value);+ }+ JS_FreeValue(ctx, obj);+ return value;+ }+ fail:+ JS_FreeValue(ctx, obj);+ *pdone = FALSE;+ return JS_EXCEPTION;+}++/* return < 0 in case of exception */+static int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj,+ BOOL is_exception_pending)+{+ JSValue method, ret, ex_obj;+ int res;++ if (is_exception_pending) {+ ex_obj = ctx->rt->current_exception;+ ctx->rt->current_exception = JS_NULL;+ res = -1;+ } else {+ ex_obj = JS_UNDEFINED;+ res = 0;+ }+ method = JS_GetProperty(ctx, enum_obj, JS_ATOM_return);+ if (JS_IsException(method)) {+ res = -1;+ goto done;+ }+ if (JS_IsUndefined(method) || JS_IsNull(method)) {+ goto done;+ }+ ret = JS_CallFree(ctx, method, enum_obj, 0, NULL);+ if (!is_exception_pending) {+ if (JS_IsException(ret)) {+ res = -1;+ } else if (!JS_IsObject(ret)) {+ JS_ThrowTypeErrorNotAnObject(ctx);+ res = -1;+ }+ }+ JS_FreeValue(ctx, ret);+ done:+ if (is_exception_pending) {+ JS_Throw(ctx, ex_obj);+ }+ return res;+}++/* obj -> enum_rec (3 slots) */+static __exception int js_for_of_start(JSContext *ctx, JSValue *sp,+ BOOL is_async)+{+ JSValue op1, obj, method;+ op1 = sp[-1];+ obj = JS_GetIterator(ctx, op1, is_async);+ if (JS_IsException(obj))+ return -1;+ JS_FreeValue(ctx, op1);+ sp[-1] = obj;+ method = JS_GetProperty(ctx, obj, JS_ATOM_next);+ if (JS_IsException(method))+ return -1;+ sp[0] = method;+ return 0;+}++/* enum_rec [objs] -> enum_rec [objs] value done. There are 'offset'+ objs. If 'done' is true or in case of exception, 'enum_rec' is set+ to undefined. If 'done' is true, 'value' is always set to+ undefined. */+static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset)+{+ JSValue value = JS_UNDEFINED;+ int done = 1;++ if (likely(!JS_IsUndefined(sp[offset]))) {+ value = JS_IteratorNext(ctx, sp[offset], sp[offset + 1], 0, NULL, &done);+ if (JS_IsException(value))+ done = -1;+ if (done) {+ /* value is JS_UNDEFINED or JS_EXCEPTION */+ /* replace the iteration object with undefined */+ JS_FreeValue(ctx, sp[offset]);+ sp[offset] = JS_UNDEFINED;+ if (done < 0) {+ return -1;+ } else {+ JS_FreeValue(ctx, value);+ value = JS_UNDEFINED;+ }+ }+ }+ sp[0] = value;+ sp[1] = JS_NewBool(ctx, done);+ return 0;+}++static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValueConst obj,+ BOOL *pdone)+{+ JSValue done_val, value;+ BOOL done;+ done_val = JS_GetProperty(ctx, obj, JS_ATOM_done);+ if (JS_IsException(done_val))+ goto fail;+ done = JS_ToBoolFree(ctx, done_val);+ value = JS_GetProperty(ctx, obj, JS_ATOM_value);+ if (JS_IsException(value))+ goto fail;+ *pdone = done;+ return value;+ fail:+ *pdone = FALSE;+ return JS_EXCEPTION;+}++static __exception int js_iterator_get_value_done(JSContext *ctx, JSValue *sp)+{+ JSValue obj, value;+ BOOL done;+ obj = sp[-1];+ if (!JS_IsObject(obj)) {+ JS_ThrowTypeError(ctx, "iterator must return an object");+ return -1;+ }+ value = JS_IteratorGetCompleteValue(ctx, obj, &done);+ if (JS_IsException(value))+ return -1;+ JS_FreeValue(ctx, obj);+ sp[-1] = value;+ sp[0] = JS_NewBool(ctx, done);+ return 0;+}++static JSValue js_create_iterator_result(JSContext *ctx,+ JSValue val,+ BOOL done)+{+ JSValue obj;+ obj = JS_NewObject(ctx);+ if (JS_IsException(obj)) {+ JS_FreeValue(ctx, val);+ return obj;+ }+ if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_value,+ val, JS_PROP_C_W_E) < 0) {+ goto fail;+ }+ if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_done,+ JS_NewBool(ctx, done), JS_PROP_C_W_E) < 0) {+ fail:+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+ }+ return obj;+}++static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv,+ BOOL *pdone, int magic);++static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic);++static BOOL js_is_fast_array(JSContext *ctx, JSValueConst obj)+{+ /* Try and handle fast arrays explicitly */+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {+ JSObject *p = JS_VALUE_GET_OBJ(obj);+ if (p->class_id == JS_CLASS_ARRAY && p->fast_array) {+ return TRUE;+ }+ }+ return FALSE;+}++/* Access an Array's internal JSValue array if available */+static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj,+ JSValue **arrpp, uint32_t *countp)+{+ /* Try and handle fast arrays explicitly */+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {+ JSObject *p = JS_VALUE_GET_OBJ(obj);+ if (p->class_id == JS_CLASS_ARRAY && p->fast_array) {+ *countp = p->u.array.count;+ *arrpp = p->u.array.u.values;+ return TRUE;+ }+ }+ return FALSE;+}++static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp)+{+ JSValue iterator, enumobj, method, value;+ int is_array_iterator;+ JSValue *arrp;+ uint32_t i, count32, pos;+ + if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) {+ JS_ThrowInternalError(ctx, "invalid index for append");+ return -1;+ }++ pos = JS_VALUE_GET_INT(sp[-2]);++ /* XXX: further optimisations:+ - use ctx->array_proto_values?+ - check if array_iterator_prototype next method is built-in and+ avoid constructing actual iterator object?+ - build this into js_for_of_start and use in all `for (x of o)` loops+ */+ iterator = JS_GetProperty(ctx, sp[-1], JS_ATOM_Symbol_iterator);+ if (JS_IsException(iterator))+ return -1;+ is_array_iterator = JS_IsCFunction(ctx, iterator,+ (JSCFunction *)js_create_array_iterator,+ JS_ITERATOR_KIND_VALUE);+ JS_FreeValue(ctx, iterator);++ enumobj = JS_GetIterator(ctx, sp[-1], FALSE);+ if (JS_IsException(enumobj))+ return -1;+ method = JS_GetProperty(ctx, enumobj, JS_ATOM_next);+ if (JS_IsException(method)) {+ JS_FreeValue(ctx, enumobj);+ return -1;+ }+ if (is_array_iterator+ && JS_IsCFunction(ctx, method, (JSCFunction *)js_array_iterator_next, 0)+ && js_get_fast_array(ctx, sp[-1], &arrp, &count32)) {+ uint32_t len;+ if (js_get_length32(ctx, &len, sp[-1]))+ goto exception;+ /* if len > count32, the elements >= count32 might be read in+ the prototypes and might have side effects */+ if (len != count32)+ goto general_case;+ /* Handle fast arrays explicitly */+ for (i = 0; i < count32; i++) {+ if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++,+ JS_DupValue(ctx, arrp[i]), JS_PROP_C_W_E) < 0)+ goto exception;+ }+ } else {+ general_case:+ for (;;) {+ BOOL done;+ value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done);+ if (JS_IsException(value))+ goto exception;+ if (done) {+ /* value is JS_UNDEFINED */+ break;+ }+ if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, value, JS_PROP_C_W_E) < 0)+ goto exception;+ }+ }+ /* Note: could raise an error if too many elements */+ sp[-2] = JS_NewInt32(ctx, pos);+ JS_FreeValue(ctx, enumobj);+ JS_FreeValue(ctx, method);+ return 0;++exception:+ JS_IteratorClose(ctx, enumobj, TRUE);+ JS_FreeValue(ctx, enumobj);+ JS_FreeValue(ctx, method);+ return -1;+}++static __exception int JS_CopyDataProperties(JSContext *ctx,+ JSValueConst target,+ JSValueConst source,+ JSValueConst excluded,+ BOOL setprop)+{+ JSPropertyEnum *tab_atom;+ JSValue val;+ uint32_t i, tab_atom_count;+ JSObject *p;+ JSObject *pexcl = NULL;+ int ret = 0, flags;++ if (JS_VALUE_GET_TAG(source) != JS_TAG_OBJECT)+ return 0;++ if (JS_VALUE_GET_TAG(excluded) == JS_TAG_OBJECT)+ pexcl = JS_VALUE_GET_OBJ(excluded);++ p = JS_VALUE_GET_OBJ(source);+ if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,+ JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK |+ JS_GPN_ENUM_ONLY))+ return -1;++ flags = JS_PROP_C_W_E;++ for (i = 0; i < tab_atom_count; i++) {+ if (pexcl) {+ ret = JS_GetOwnPropertyInternal(ctx, NULL, pexcl, tab_atom[i].atom);+ if (ret) {+ if (ret < 0)+ break;+ ret = 0;+ continue;+ }+ }+ ret = -1;+ val = JS_GetProperty(ctx, source, tab_atom[i].atom);+ if (JS_IsException(val))+ break;+ if (setprop)+ ret = JS_SetProperty(ctx, target, tab_atom[i].atom, val);+ else+ ret = JS_DefinePropertyValue(ctx, target, tab_atom[i].atom, val, flags);+ if (ret < 0)+ break;+ ret = 0;+ }+ js_free_prop_enum(ctx, tab_atom, tab_atom_count);+ return ret;+}++/* only valid inside C functions */+static JSValueConst JS_GetActiveFunction(JSContext *ctx)+{+ return ctx->rt->current_stack_frame->cur_func;+}++static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf,+ int var_idx, BOOL is_arg)+{+ JSVarRef *var_ref;+ struct list_head *el;++ list_for_each(el, &sf->var_ref_list) {+ var_ref = list_entry(el, JSVarRef, header.link);+ if (var_ref->var_idx == var_idx && var_ref->is_arg == is_arg) {+ var_ref->header.ref_count++;+ return var_ref;+ }+ }+ /* create a new one */+ var_ref = js_malloc(ctx, sizeof(JSVarRef));+ if (!var_ref)+ return NULL;+ var_ref->header.ref_count = 1;+ var_ref->is_detached = FALSE;+ var_ref->is_arg = is_arg;+ var_ref->var_idx = var_idx;+ list_add_tail(&var_ref->header.link, &sf->var_ref_list);+ if (is_arg)+ var_ref->pvalue = &sf->arg_buf[var_idx];+ else+ var_ref->pvalue = &sf->var_buf[var_idx];+ var_ref->value = JS_UNDEFINED;+ return var_ref;+}++static JSValue js_closure2(JSContext *ctx, JSValue func_obj,+ JSFunctionBytecode *b,+ JSVarRef **cur_var_refs,+ JSStackFrame *sf)+{+ JSObject *p;+ JSVarRef **var_refs;+ int i;++ p = JS_VALUE_GET_OBJ(func_obj);+ p->u.func.function_bytecode = b;+ p->u.func.home_object = NULL;+ p->u.func.var_refs = NULL;+ if (b->closure_var_count) {+ var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count);+ if (!var_refs)+ goto fail;+ p->u.func.var_refs = var_refs;+ for(i = 0; i < b->closure_var_count; i++) {+ JSClosureVar *cv = &b->closure_var[i];+ JSVarRef *var_ref;+ if (cv->is_local) {+ /* reuse the existing variable reference if it already exists */+ var_ref = get_var_ref(ctx, sf, cv->var_idx, cv->is_arg);+ if (!var_ref)+ goto fail;+ } else {+ var_ref = cur_var_refs[cv->var_idx];+ var_ref->header.ref_count++;+ }+ var_refs[i] = var_ref;+ }+ }+ return func_obj;+ fail:+ /* bfunc is freed when func_obj is freed */+ JS_FreeValue(ctx, func_obj);+ return JS_EXCEPTION;+}++static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque)+{+ JSValue obj, this_val;+ int ret;++ this_val = JS_MKPTR(JS_TAG_OBJECT, p);+ obj = JS_NewObject(ctx);+ if (JS_IsException(obj))+ return JS_EXCEPTION;+ set_cycle_flag(ctx, obj);+ set_cycle_flag(ctx, this_val);+ ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_constructor,+ JS_DupValue(ctx, this_val),+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+ if (ret < 0) {+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+ }+ return obj;+}++static const uint16_t func_kind_to_class_id[] = {+ [JS_FUNC_NORMAL] = JS_CLASS_BYTECODE_FUNCTION,+ [JS_FUNC_GENERATOR] = JS_CLASS_GENERATOR_FUNCTION,+ [JS_FUNC_ASYNC] = JS_CLASS_ASYNC_FUNCTION,+ [JS_FUNC_ASYNC_GENERATOR] = JS_CLASS_ASYNC_GENERATOR_FUNCTION,+};++static JSValue js_closure(JSContext *ctx, JSValue bfunc,+ JSVarRef **cur_var_refs,+ JSStackFrame *sf)+{+ JSFunctionBytecode *b;+ JSValue func_obj;+ JSAtom name_atom;++ b = JS_VALUE_GET_PTR(bfunc);+ func_obj = JS_NewObjectClass(ctx, func_kind_to_class_id[b->func_kind]);+ if (JS_IsException(func_obj)) {+ JS_FreeValue(ctx, bfunc);+ return JS_EXCEPTION;+ }+ func_obj = js_closure2(ctx, func_obj, b, cur_var_refs, sf);+ if (JS_IsException(func_obj)) {+ /* bfunc has been freed */+ goto fail;+ }+ name_atom = b->func_name;+ if (name_atom == JS_ATOM_NULL)+ name_atom = JS_ATOM_empty_string;+ js_function_set_properties(ctx, func_obj, name_atom,+ b->defined_arg_count);++ if (b->func_kind & JS_FUNC_GENERATOR) {+ JSValue proto;+ int proto_class_id;+ /* generators have a prototype field which is used as+ prototype for the generator object */+ if (b->func_kind == JS_FUNC_ASYNC_GENERATOR)+ proto_class_id = JS_CLASS_ASYNC_GENERATOR;+ else+ proto_class_id = JS_CLASS_GENERATOR;+ proto = JS_NewObjectProto(ctx, ctx->class_proto[proto_class_id]);+ if (JS_IsException(proto))+ goto fail;+ JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, proto,+ JS_PROP_WRITABLE);+ } else if (b->has_prototype) {+ /* add the 'prototype' property: delay instantiation to avoid+ creating cycles for every javascript function. The prototype+ object is created on the fly when first accessed */+ JS_SetConstructorBit(ctx, func_obj, TRUE);+ JS_DefineAutoInitProperty(ctx, func_obj, JS_ATOM_prototype,+ JS_AUTOINIT_ID_PROTOTYPE, NULL,+ JS_PROP_WRITABLE);+ }+ return func_obj;+ fail:+ /* bfunc is freed when func_obj is freed */+ JS_FreeValue(ctx, func_obj);+ return JS_EXCEPTION;+}++#define JS_DEFINE_CLASS_HAS_HERITAGE (1 << 0)++static int js_op_define_class(JSContext *ctx, JSValue *sp,+ JSAtom class_name, int class_flags,+ JSVarRef **cur_var_refs,+ JSStackFrame *sf, BOOL is_computed_name)+{+ JSValue bfunc, parent_class, proto = JS_UNDEFINED;+ JSValue ctor = JS_UNDEFINED, parent_proto = JS_UNDEFINED;+ JSFunctionBytecode *b;++ parent_class = sp[-2];+ bfunc = sp[-1];++ if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) {+ if (JS_IsNull(parent_class)) {+ parent_proto = JS_NULL;+ parent_class = JS_DupValue(ctx, ctx->function_proto);+ } else {+ if (!JS_IsConstructor(ctx, parent_class)) {+ JS_ThrowTypeError(ctx, "parent class must be constructor");+ goto fail;+ }+ parent_proto = JS_GetProperty(ctx, parent_class, JS_ATOM_prototype);+ if (JS_IsException(parent_proto))+ goto fail;+ if (!JS_IsNull(parent_proto) && !JS_IsObject(parent_proto)) {+ JS_ThrowTypeError(ctx, "parent prototype must be an object or null");+ goto fail;+ }+ }+ } else {+ /* parent_class is JS_UNDEFINED in this case */+ parent_proto = JS_DupValue(ctx, ctx->class_proto[JS_CLASS_OBJECT]);+ parent_class = JS_DupValue(ctx, ctx->function_proto);+ }+ proto = JS_NewObjectProto(ctx, parent_proto);+ if (JS_IsException(proto))+ goto fail;++ b = JS_VALUE_GET_PTR(bfunc);+ assert(b->func_kind == JS_FUNC_NORMAL);+ ctor = JS_NewObjectProtoClass(ctx, parent_class,+ JS_CLASS_BYTECODE_FUNCTION);+ if (JS_IsException(ctor))+ goto fail;+ ctor = js_closure2(ctx, ctor, b, cur_var_refs, sf);+ bfunc = JS_UNDEFINED;+ if (JS_IsException(ctor))+ goto fail;+ js_method_set_home_object(ctx, ctor, proto);+ JS_SetConstructorBit(ctx, ctor, TRUE);++ JS_DefinePropertyValue(ctx, ctor, JS_ATOM_length,+ JS_NewInt32(ctx, b->defined_arg_count),+ JS_PROP_CONFIGURABLE);++ if (is_computed_name) {+ if (JS_DefineObjectNameComputed(ctx, ctor, sp[-3],+ JS_PROP_CONFIGURABLE) < 0)+ goto fail;+ } else {+ if (JS_DefineObjectName(ctx, ctor, class_name, JS_PROP_CONFIGURABLE) < 0)+ goto fail;+ }++ /* the constructor property must be first. It can be overriden by+ computed property names */+ if (JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor,+ JS_DupValue(ctx, ctor),+ JS_PROP_CONFIGURABLE |+ JS_PROP_WRITABLE | JS_PROP_THROW) < 0)+ goto fail;+ /* set the prototype property */+ if (JS_DefinePropertyValue(ctx, ctor, JS_ATOM_prototype,+ JS_DupValue(ctx, proto), JS_PROP_THROW) < 0)+ goto fail;+ set_cycle_flag(ctx, ctor);+ set_cycle_flag(ctx, proto);++ JS_FreeValue(ctx, parent_proto);+ JS_FreeValue(ctx, parent_class);++ sp[-2] = ctor;+ sp[-1] = proto;+ return 0;+ fail:+ JS_FreeValue(ctx, parent_class);+ JS_FreeValue(ctx, parent_proto);+ JS_FreeValue(ctx, bfunc);+ JS_FreeValue(ctx, proto);+ JS_FreeValue(ctx, ctor);+ sp[-2] = JS_UNDEFINED;+ sp[-1] = JS_UNDEFINED;+ return -1;+}++static void close_var_refs(JSRuntime *rt, JSStackFrame *sf)+{+ struct list_head *el, *el1;+ JSVarRef *var_ref;+ int var_idx;++ list_for_each_safe(el, el1, &sf->var_ref_list) {+ var_ref = list_entry(el, JSVarRef, header.link);+ var_idx = var_ref->var_idx;+ if (var_ref->is_arg)+ var_ref->value = JS_DupValueRT(rt, sf->arg_buf[var_idx]);+ else+ var_ref->value = JS_DupValueRT(rt, sf->var_buf[var_idx]);+ var_ref->pvalue = &var_ref->value;+ /* the reference is no longer to a local variable */+ var_ref->is_detached = TRUE;+ add_gc_object(rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);+ }+}++static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int idx, int is_arg)+{+ struct list_head *el, *el1;+ JSVarRef *var_ref;+ int var_idx = idx;++ list_for_each_safe(el, el1, &sf->var_ref_list) {+ var_ref = list_entry(el, JSVarRef, header.link);+ if (var_idx == var_ref->var_idx && var_ref->is_arg == is_arg) {+ var_ref->value = JS_DupValue(ctx, sf->var_buf[var_idx]);+ var_ref->pvalue = &var_ref->value;+ list_del(&var_ref->header.link);+ /* the reference is no longer to a local variable */+ var_ref->is_detached = TRUE;+ add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);+ }+ }+}++#define JS_CALL_FLAG_COPY_ARGV (1 << 1)+#define JS_CALL_FLAG_GENERATOR (1 << 2)++static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,+ JSValueConst this_obj,+ int argc, JSValueConst *argv, int flags)+{+ JSRuntime *rt = ctx->rt;+ JSCFunctionType func;+ JSObject *p;+ JSStackFrame sf_s, *sf = &sf_s, *prev_sf;+ JSValue ret_val;+ JSValueConst *arg_buf;+ int arg_count, i;+ JSCFunctionEnum cproto;++ p = JS_VALUE_GET_OBJ(func_obj);+ cproto = p->u.cfunc.cproto;+ arg_count = p->u.cfunc.length;++ /* better to always check stack overflow */+ if (js_check_stack_overflow(rt, sizeof(arg_buf[0]) * arg_count))+ return JS_ThrowStackOverflow(ctx);++ prev_sf = rt->current_stack_frame;+ sf->prev_frame = prev_sf;+ rt->current_stack_frame = sf;+ ctx = p->u.cfunc.realm; /* change the current realm */+ +#ifdef CONFIG_BIGNUM+ /* we only propagate the bignum mode as some runtime functions+ test it */+ if (prev_sf)+ sf->js_mode = prev_sf->js_mode & JS_MODE_MATH;+ else+ sf->js_mode = 0;+#else+ sf->js_mode = 0;+#endif+ sf->cur_func = (JSValue)func_obj;+ sf->arg_count = argc;+ arg_buf = argv;++ if (unlikely(argc < arg_count)) {+ /* ensure that at least argc_count arguments are readable */+ arg_buf = alloca(sizeof(arg_buf[0]) * arg_count);+ for(i = 0; i < argc; i++)+ arg_buf[i] = argv[i];+ for(i = argc; i < arg_count; i++)+ arg_buf[i] = JS_UNDEFINED;+ sf->arg_count = arg_count;+ }+ sf->arg_buf = (JSValue*)arg_buf;++ func = p->u.cfunc.c_function;+ switch(cproto) {+ case JS_CFUNC_constructor:+ case JS_CFUNC_constructor_or_func:+ if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) {+ if (cproto == JS_CFUNC_constructor) {+ not_a_constructor:+ ret_val = JS_ThrowTypeError(ctx, "must be called with new");+ break;+ } else {+ this_obj = JS_UNDEFINED;+ }+ }+ /* here this_obj is new_target */+ /* fall thru */+ case JS_CFUNC_generic:+ ret_val = func.generic(ctx, this_obj, argc, arg_buf);+ break;+ case JS_CFUNC_constructor_magic:+ case JS_CFUNC_constructor_or_func_magic:+ if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) {+ if (cproto == JS_CFUNC_constructor_magic) {+ goto not_a_constructor;+ } else {+ this_obj = JS_UNDEFINED;+ }+ }+ /* fall thru */+ case JS_CFUNC_generic_magic:+ ret_val = func.generic_magic(ctx, this_obj, argc, arg_buf,+ p->u.cfunc.magic);+ break;+ case JS_CFUNC_getter:+ ret_val = func.getter(ctx, this_obj);+ break;+ case JS_CFUNC_setter:+ ret_val = func.setter(ctx, this_obj, arg_buf[0]);+ break;+ case JS_CFUNC_getter_magic:+ ret_val = func.getter_magic(ctx, this_obj, p->u.cfunc.magic);+ break;+ case JS_CFUNC_setter_magic:+ ret_val = func.setter_magic(ctx, this_obj, arg_buf[0], p->u.cfunc.magic);+ break;+ case JS_CFUNC_f_f:+ {+ double d1;++ if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) {+ ret_val = JS_EXCEPTION;+ break;+ }+ ret_val = JS_NewFloat64(ctx, func.f_f(d1));+ }+ break;+ case JS_CFUNC_f_f_f:+ {+ double d1, d2;++ if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) {+ ret_val = JS_EXCEPTION;+ break;+ }+ if (unlikely(JS_ToFloat64(ctx, &d2, arg_buf[1]))) {+ ret_val = JS_EXCEPTION;+ break;+ }+ ret_val = JS_NewFloat64(ctx, func.f_f_f(d1, d2));+ }+ break;+ case JS_CFUNC_iterator_next:+ {+ int done;+ ret_val = func.iterator_next(ctx, this_obj, argc, arg_buf,+ &done, p->u.cfunc.magic);+ if (!JS_IsException(ret_val) && done != 2) {+ ret_val = js_create_iterator_result(ctx, ret_val, done);+ }+ }+ break;+ default:+ abort();+ }++ rt->current_stack_frame = sf->prev_frame;+ return ret_val;+}++static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj,+ JSValueConst this_obj,+ int argc, JSValueConst *argv, int flags)+{+ JSObject *p;+ JSBoundFunction *bf;+ JSValueConst *arg_buf, new_target;+ int arg_count, i;++ p = JS_VALUE_GET_OBJ(func_obj);+ bf = p->u.bound_function;+ arg_count = bf->argc + argc;+ if (js_check_stack_overflow(ctx->rt, sizeof(JSValue) * arg_count))+ return JS_ThrowStackOverflow(ctx);+ arg_buf = alloca(sizeof(JSValue) * arg_count);+ for(i = 0; i < bf->argc; i++) {+ arg_buf[i] = bf->argv[i];+ }+ for(i = 0; i < argc; i++) {+ arg_buf[bf->argc + i] = argv[i];+ }+ if (flags & JS_CALL_FLAG_CONSTRUCTOR) {+ new_target = this_obj;+ if (js_same_value(ctx, func_obj, new_target))+ new_target = bf->func_obj;+ return JS_CallConstructor2(ctx, bf->func_obj, new_target,+ arg_count, arg_buf);+ } else {+ return JS_Call(ctx, bf->func_obj, bf->this_val,+ arg_count, arg_buf);+ }+}++/* argument of OP_special_object */+typedef enum {+ OP_SPECIAL_OBJECT_ARGUMENTS,+ OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS,+ OP_SPECIAL_OBJECT_THIS_FUNC,+ OP_SPECIAL_OBJECT_NEW_TARGET,+ OP_SPECIAL_OBJECT_HOME_OBJECT,+ OP_SPECIAL_OBJECT_VAR_OBJECT,+ OP_SPECIAL_OBJECT_IMPORT_META,+} OPSpecialObjectEnum;++#define FUNC_RET_AWAIT 0+#define FUNC_RET_YIELD 1+#define FUNC_RET_YIELD_STAR 2++/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */+static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,+ JSValueConst this_obj, JSValueConst new_target,+ int argc, JSValue *argv, int flags)+{+ JSRuntime *rt = caller_ctx->rt;+ JSContext *ctx;+ JSObject *p;+ JSFunctionBytecode *b;+ JSStackFrame sf_s, *sf = &sf_s;+ const uint8_t *pc;+ int opcode, arg_allocated_size, i;+ JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, *sp, ret_val, *pval;+ JSVarRef **var_refs;+ size_t alloca_size;++#if !DIRECT_DISPATCH+#define SWITCH(pc) switch (opcode = *pc++)+#define CASE(op) case op+#define DEFAULT default+#define BREAK break+#else+ static const void * const dispatch_table[256] = {+#define DEF(id, size, n_pop, n_push, f) && case_OP_ ## id,+#if SHORT_OPCODES+#define def(id, size, n_pop, n_push, f)+#else +#define def(id, size, n_pop, n_push, f) && case_default,+#endif+#include "quickjs-opcode.h"+ [ OP_COUNT ... 255 ] = &&case_default+ };+#define SWITCH(pc) goto *dispatch_table[opcode = *pc++];+#define CASE(op) case_ ## op+#define DEFAULT case_default+#define BREAK SWITCH(pc)+#endif++ if (js_poll_interrupts(caller_ctx))+ return JS_EXCEPTION;+ if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) {+ if (flags & JS_CALL_FLAG_GENERATOR) {+ JSAsyncFunctionState *s = JS_VALUE_GET_PTR(func_obj);+ /* func_obj get contains a pointer to JSFuncAsyncState */+ /* the stack frame is already allocated */+ sf = &s->frame;+ p = JS_VALUE_GET_OBJ(sf->cur_func);+ b = p->u.func.function_bytecode;+ ctx = b->realm;+ var_refs = p->u.func.var_refs;+ local_buf = arg_buf = sf->arg_buf;+ var_buf = sf->var_buf;+ stack_buf = sf->var_buf + b->var_count;+ sp = sf->cur_sp;+ sf->cur_sp = NULL; /* cur_sp is NULL if the function is running */+ pc = sf->cur_pc;+ sf->prev_frame = rt->current_stack_frame;+ rt->current_stack_frame = sf;+ if (s->throw_flag)+ goto exception;+ else+ goto restart;+ } else {+ goto not_a_function;+ }+ }+ p = JS_VALUE_GET_OBJ(func_obj);+ if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) {+ JSClassCall *call_func;+ call_func = rt->class_array[p->class_id].call;+ if (!call_func) {+ not_a_function:+ return JS_ThrowTypeError(caller_ctx, "not a function");+ }+ return call_func(caller_ctx, func_obj, this_obj, argc,+ (JSValueConst *)argv, flags);+ }+ b = p->u.func.function_bytecode;++ if (unlikely(argc < b->arg_count || (flags & JS_CALL_FLAG_COPY_ARGV))) {+ arg_allocated_size = b->arg_count;+ } else {+ arg_allocated_size = 0;+ }++ alloca_size = sizeof(JSValue) * (arg_allocated_size + b->var_count ++ b->stack_size);+ if (js_check_stack_overflow(rt, alloca_size))+ return JS_ThrowStackOverflow(caller_ctx);++ sf->js_mode = b->js_mode;+ arg_buf = argv;+ sf->arg_count = argc;+ sf->cur_func = (JSValue)func_obj;+ init_list_head(&sf->var_ref_list);+ var_refs = p->u.func.var_refs;++ local_buf = alloca(alloca_size);+ if (unlikely(arg_allocated_size)) {+ int n = min_int(argc, b->arg_count);+ arg_buf = local_buf;+ for(i = 0; i < n; i++)+ arg_buf[i] = JS_DupValue(caller_ctx, argv[i]);+ for(; i < b->arg_count; i++)+ arg_buf[i] = JS_UNDEFINED;+ sf->arg_count = b->arg_count;+ }+ var_buf = local_buf + arg_allocated_size;+ sf->var_buf = var_buf;+ sf->arg_buf = arg_buf;++ for(i = 0; i < b->var_count; i++)+ var_buf[i] = JS_UNDEFINED;++ stack_buf = var_buf + b->var_count;+ sp = stack_buf;+ pc = b->byte_code_buf;+ sf->prev_frame = rt->current_stack_frame;+ rt->current_stack_frame = sf;+ ctx = b->realm; /* set the current realm */+ + restart:+ for(;;) {+ int call_argc;+ JSValue *call_argv;++ SWITCH(pc) {+ CASE(OP_push_i32):+ *sp++ = JS_NewInt32(ctx, get_u32(pc));+ pc += 4;+ BREAK;+ CASE(OP_push_const):+ *sp++ = JS_DupValue(ctx, b->cpool[get_u32(pc)]);+ pc += 4;+ BREAK;+#if SHORT_OPCODES+ CASE(OP_push_minus1):+ CASE(OP_push_0):+ CASE(OP_push_1):+ CASE(OP_push_2):+ CASE(OP_push_3):+ CASE(OP_push_4):+ CASE(OP_push_5):+ CASE(OP_push_6):+ CASE(OP_push_7):+ *sp++ = JS_NewInt32(ctx, opcode - OP_push_0);+ BREAK;+ CASE(OP_push_i8):+ *sp++ = JS_NewInt32(ctx, get_i8(pc));+ pc += 1;+ BREAK;+ CASE(OP_push_i16):+ *sp++ = JS_NewInt32(ctx, get_i16(pc));+ pc += 2;+ BREAK;+ CASE(OP_push_const8):+ *sp++ = JS_DupValue(ctx, b->cpool[*pc++]);+ BREAK;+ CASE(OP_fclosure8):+ *sp++ = js_closure(ctx, JS_DupValue(ctx, b->cpool[*pc++]), var_refs, sf);+ if (unlikely(JS_IsException(sp[-1])))+ goto exception;+ BREAK;+ CASE(OP_push_empty_string):+ *sp++ = JS_AtomToString(ctx, JS_ATOM_empty_string);+ BREAK;+ CASE(OP_get_length):+ {+ JSValue val;++ val = JS_GetProperty(ctx, sp[-1], JS_ATOM_length);+ if (unlikely(JS_IsException(val)))+ goto exception;+ JS_FreeValue(ctx, sp[-1]);+ sp[-1] = val;+ }+ BREAK;+#endif+ CASE(OP_push_atom_value):+ *sp++ = JS_AtomToValue(ctx, get_u32(pc));+ pc += 4;+ BREAK;+ CASE(OP_undefined):+ *sp++ = JS_UNDEFINED;+ BREAK;+ CASE(OP_null):+ *sp++ = JS_NULL;+ BREAK;+ CASE(OP_push_this):+ /* OP_push_this is only called at the start of a function */+ {+ JSValue val;+ if (!(b->js_mode & JS_MODE_STRICT)) {+ uint32_t tag = JS_VALUE_GET_TAG(this_obj);+ if (likely(tag == JS_TAG_OBJECT))+ goto normal_this;+ if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) {+ val = JS_DupValue(ctx, ctx->global_obj);+ } else {+ val = JS_ToObject(ctx, this_obj);+ if (JS_IsException(val))+ goto exception;+ }+ } else {+ normal_this:+ val = JS_DupValue(ctx, this_obj);+ }+ *sp++ = val;+ }+ BREAK;+ CASE(OP_push_false):+ *sp++ = JS_FALSE;+ BREAK;+ CASE(OP_push_true):+ *sp++ = JS_TRUE;+ BREAK;+ CASE(OP_object):+ *sp++ = JS_NewObject(ctx);+ if (unlikely(JS_IsException(sp[-1])))+ goto exception;+ BREAK;+ CASE(OP_special_object):+ {+ int arg = *pc++;+ switch(arg) {+ case OP_SPECIAL_OBJECT_ARGUMENTS:+ *sp++ = js_build_arguments(ctx, argc, (JSValueConst *)argv);+ if (unlikely(JS_IsException(sp[-1])))+ goto exception;+ break;+ case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS:+ *sp++ = js_build_mapped_arguments(ctx, argc, (JSValueConst *)argv,+ sf, min_int(argc, b->arg_count));+ if (unlikely(JS_IsException(sp[-1])))+ goto exception;+ break;+ case OP_SPECIAL_OBJECT_THIS_FUNC:+ *sp++ = JS_DupValue(ctx, sf->cur_func);+ break;+ case OP_SPECIAL_OBJECT_NEW_TARGET:+ *sp++ = JS_DupValue(ctx, new_target);+ break;+ case OP_SPECIAL_OBJECT_HOME_OBJECT:+ {+ JSObject *p1;+ p1 = p->u.func.home_object;+ if (unlikely(!p1))+ *sp++ = JS_UNDEFINED;+ else+ *sp++ = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));+ }+ break;+ case OP_SPECIAL_OBJECT_VAR_OBJECT:+ *sp++ = JS_NewObjectProto(ctx, JS_NULL);+ if (unlikely(JS_IsException(sp[-1])))+ goto exception;+ break;+ case OP_SPECIAL_OBJECT_IMPORT_META:+ *sp++ = js_import_meta(ctx);+ if (unlikely(JS_IsException(sp[-1])))+ goto exception;+ break;+ default:+ abort();+ }+ }+ BREAK;+ CASE(OP_rest):+ {+ int first = get_u16(pc);+ pc += 2;+ *sp++ = js_build_rest(ctx, first, argc, (JSValueConst *)argv);+ if (unlikely(JS_IsException(sp[-1])))+ goto exception;+ }+ BREAK;++ CASE(OP_drop):+ JS_FreeValue(ctx, sp[-1]);+ sp--;+ BREAK;+ CASE(OP_nip):+ JS_FreeValue(ctx, sp[-2]);+ sp[-2] = sp[-1];+ sp--;+ BREAK;+ CASE(OP_nip1): /* a b c -> b c */+ JS_FreeValue(ctx, sp[-3]);+ sp[-3] = sp[-2];+ sp[-2] = sp[-1];+ sp--;+ BREAK;+ CASE(OP_dup):+ sp[0] = JS_DupValue(ctx, sp[-1]);+ sp++;+ BREAK;+ CASE(OP_dup2): /* a b -> a b a b */+ sp[0] = JS_DupValue(ctx, sp[-2]);+ sp[1] = JS_DupValue(ctx, sp[-1]);+ sp += 2;+ BREAK;+ CASE(OP_dup3): /* a b c -> a b c a b c */+ sp[0] = JS_DupValue(ctx, sp[-3]);+ sp[1] = JS_DupValue(ctx, sp[-2]);+ sp[2] = JS_DupValue(ctx, sp[-1]);+ sp += 3;+ BREAK;+ CASE(OP_dup1): /* a b -> a a b */+ sp[0] = sp[-1];+ sp[-1] = JS_DupValue(ctx, sp[-2]);+ sp++;+ BREAK;+ CASE(OP_insert2): /* obj a -> a obj a (dup_x1) */+ sp[0] = sp[-1];+ sp[-1] = sp[-2];+ sp[-2] = JS_DupValue(ctx, sp[0]);+ sp++;+ BREAK;+ CASE(OP_insert3): /* obj prop a -> a obj prop a (dup_x2) */+ sp[0] = sp[-1];+ sp[-1] = sp[-2];+ sp[-2] = sp[-3];+ sp[-3] = JS_DupValue(ctx, sp[0]);+ sp++;+ BREAK;+ CASE(OP_insert4): /* this obj prop a -> a this obj prop a */+ sp[0] = sp[-1];+ sp[-1] = sp[-2];+ sp[-2] = sp[-3];+ sp[-3] = sp[-4];+ sp[-4] = JS_DupValue(ctx, sp[0]);+ sp++;+ BREAK;+ CASE(OP_perm3): /* obj a b -> a obj b (213) */+ {+ JSValue tmp;+ tmp = sp[-2];+ sp[-2] = sp[-3];+ sp[-3] = tmp;+ }+ BREAK;+ CASE(OP_rot3l): /* x a b -> a b x (231) */+ {+ JSValue tmp;+ tmp = sp[-3];+ sp[-3] = sp[-2];+ sp[-2] = sp[-1];+ sp[-1] = tmp;+ }+ BREAK;+ CASE(OP_rot4l): /* x a b c -> a b c x */+ {+ JSValue tmp;+ tmp = sp[-4];+ sp[-4] = sp[-3];+ sp[-3] = sp[-2];+ sp[-2] = sp[-1];+ sp[-1] = tmp;+ }+ BREAK;+ CASE(OP_rot5l): /* x a b c d -> a b c d x */+ {+ JSValue tmp;+ tmp = sp[-5];+ sp[-5] = sp[-4];+ sp[-4] = sp[-3];+ sp[-3] = sp[-2];+ sp[-2] = sp[-1];+ sp[-1] = tmp;+ }+ BREAK;+ CASE(OP_rot3r): /* a b x -> x a b (312) */+ {+ JSValue tmp;+ tmp = sp[-1];+ sp[-1] = sp[-2];+ sp[-2] = sp[-3];+ sp[-3] = tmp;+ }+ BREAK;+ CASE(OP_perm4): /* obj prop a b -> a obj prop b */+ {+ JSValue tmp;+ tmp = sp[-2];+ sp[-2] = sp[-3];+ sp[-3] = sp[-4];+ sp[-4] = tmp;+ }+ BREAK;+ CASE(OP_perm5): /* this obj prop a b -> a this obj prop b */+ {+ JSValue tmp;+ tmp = sp[-2];+ sp[-2] = sp[-3];+ sp[-3] = sp[-4];+ sp[-4] = sp[-5];+ sp[-5] = tmp;+ }+ BREAK;+ CASE(OP_swap): /* a b -> b a */+ {+ JSValue tmp;+ tmp = sp[-2];+ sp[-2] = sp[-1];+ sp[-1] = tmp;+ }+ BREAK;+ CASE(OP_swap2): /* a b c d -> c d a b */+ {+ JSValue tmp1, tmp2;+ tmp1 = sp[-4];+ tmp2 = sp[-3];+ sp[-4] = sp[-2];+ sp[-3] = sp[-1];+ sp[-2] = tmp1;+ sp[-1] = tmp2;+ }+ BREAK;++ CASE(OP_fclosure):+ {+ JSValue bfunc = JS_DupValue(ctx, b->cpool[get_u32(pc)]);+ pc += 4;+ *sp++ = js_closure(ctx, bfunc, var_refs, sf);+ if (unlikely(JS_IsException(sp[-1])))+ goto exception;+ }+ BREAK;+#if SHORT_OPCODES+ CASE(OP_call0):+ CASE(OP_call1):+ CASE(OP_call2):+ CASE(OP_call3):+ call_argc = opcode - OP_call0;+ goto has_call_argc;+#endif+ CASE(OP_call):+ CASE(OP_tail_call):+ {+ call_argc = get_u16(pc);+ pc += 2;+ goto has_call_argc;+ has_call_argc:+ call_argv = sp - call_argc;+ sf->cur_pc = pc;+ ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED,+ JS_UNDEFINED, call_argc, call_argv, 0);+ if (unlikely(JS_IsException(ret_val)))+ goto exception;+ if (opcode == OP_tail_call)+ goto done;+ for(i = -1; i < call_argc; i++)+ JS_FreeValue(ctx, call_argv[i]);+ sp -= call_argc + 1;+ *sp++ = ret_val;+ }+ BREAK;+ CASE(OP_call_constructor):+ {+ call_argc = get_u16(pc);+ pc += 2;+ call_argv = sp - call_argc;+ sf->cur_pc = pc;+ ret_val = JS_CallConstructorInternal(ctx, call_argv[-2],+ call_argv[-1],+ call_argc, call_argv, 0);+ if (unlikely(JS_IsException(ret_val)))+ goto exception;+ for(i = -2; i < call_argc; i++)+ JS_FreeValue(ctx, call_argv[i]);+ sp -= call_argc + 2;+ *sp++ = ret_val;+ }+ BREAK;+ CASE(OP_call_method):+ CASE(OP_tail_call_method):+ {+ call_argc = get_u16(pc);+ pc += 2;+ call_argv = sp - call_argc;+ sf->cur_pc = pc;+ ret_val = JS_CallInternal(ctx, call_argv[-1], call_argv[-2],+ JS_UNDEFINED, call_argc, call_argv, 0);+ if (unlikely(JS_IsException(ret_val)))+ goto exception;+ if (opcode == OP_tail_call_method)+ goto done;+ for(i = -2; i < call_argc; i++)+ JS_FreeValue(ctx, call_argv[i]);+ sp -= call_argc + 2;+ *sp++ = ret_val;+ }+ BREAK;+ CASE(OP_array_from):+ {+ int i, ret;++ call_argc = get_u16(pc);+ pc += 2;+ ret_val = JS_NewArray(ctx);+ if (unlikely(JS_IsException(ret_val)))+ goto exception;+ call_argv = sp - call_argc;+ for(i = 0; i < call_argc; i++) {+ ret = JS_DefinePropertyValue(ctx, ret_val, __JS_AtomFromUInt32(i), call_argv[i],+ JS_PROP_C_W_E | JS_PROP_THROW);+ call_argv[i] = JS_UNDEFINED;+ if (ret < 0) {+ JS_FreeValue(ctx, ret_val);+ goto exception;+ }+ }+ sp -= call_argc;+ *sp++ = ret_val;+ }+ BREAK;++ CASE(OP_apply):+ {+ int magic;+ magic = get_u16(pc);+ pc += 2;++ ret_val = js_function_apply(ctx, sp[-3], 2, (JSValueConst *)&sp[-2], magic);+ if (unlikely(JS_IsException(ret_val)))+ goto exception;+ JS_FreeValue(ctx, sp[-3]);+ JS_FreeValue(ctx, sp[-2]);+ JS_FreeValue(ctx, sp[-1]);+ sp -= 3;+ *sp++ = ret_val;+ }+ BREAK;+ CASE(OP_return):+ ret_val = *--sp;+ goto done;+ CASE(OP_return_undef):+ ret_val = JS_UNDEFINED;+ goto done;++ CASE(OP_check_ctor_return):+ /* return TRUE if 'this' should be returned */+ if (!JS_IsObject(sp[-1])) {+ if (!JS_IsUndefined(sp[-1])) {+ JS_ThrowTypeError(caller_ctx, "derived class constructor must return an object or undefined");+ goto exception;+ }+ sp[0] = JS_TRUE;+ } else {+ sp[0] = JS_FALSE;+ }+ sp++;+ BREAK;+ CASE(OP_check_ctor):+ if (JS_IsUndefined(new_target)) {+ JS_ThrowTypeError(caller_ctx, "class constructors must be invoked with 'new'");+ goto exception;+ }+ BREAK;+ CASE(OP_check_brand):+ if (JS_CheckBrand(ctx, sp[-2], sp[-1]) < 0)+ goto exception;+ BREAK;+ CASE(OP_add_brand):+ if (JS_AddBrand(ctx, sp[-2], sp[-1]) < 0)+ goto exception;+ JS_FreeValue(ctx, sp[-2]);+ JS_FreeValue(ctx, sp[-1]);+ sp -= 2;+ BREAK;+ + CASE(OP_throw):+ JS_Throw(ctx, *--sp);+ goto exception;++ CASE(OP_throw_error):+#define JS_THROW_VAR_RO 0+#define JS_THROW_VAR_REDECL 1+#define JS_THROW_VAR_UNINITIALIZED 2+#define JS_THROW_ERROR_DELETE_SUPER 3+#define JS_THROW_ERROR_ITERATOR_THROW 4+ {+ JSAtom atom;+ int type;+ atom = get_u32(pc);+ type = pc[4];+ pc += 5;+ if (type == JS_THROW_VAR_RO)+ JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, atom);+ else+ if (type == JS_THROW_VAR_REDECL)+ JS_ThrowSyntaxErrorVarRedeclaration(ctx, atom);+ else+ if (type == JS_THROW_VAR_UNINITIALIZED)+ JS_ThrowReferenceErrorUninitialized(ctx, atom);+ else+ if (type == JS_THROW_ERROR_DELETE_SUPER)+ JS_ThrowReferenceError(ctx, "unsupported reference to 'super'");+ else+ if (type == JS_THROW_ERROR_ITERATOR_THROW)+ JS_ThrowTypeError(ctx, "iterator does not have a throw method");+ else+ JS_ThrowInternalError(ctx, "invalid throw var type %d", type);+ }+ goto exception;++ CASE(OP_eval):+ {+ JSValueConst obj;+ int scope_idx;+ call_argc = get_u16(pc);+ scope_idx = get_u16(pc + 2) - 1;+ pc += 4;+ call_argv = sp - call_argc;+ sf->cur_pc = pc;+ if (js_same_value(ctx, call_argv[-1], ctx->eval_obj)) {+ if (call_argc >= 1)+ obj = call_argv[0];+ else+ obj = JS_UNDEFINED;+ ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj,+ JS_EVAL_TYPE_DIRECT, scope_idx);+ } else {+ ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED,+ JS_UNDEFINED, call_argc, call_argv, 0);+ }+ if (unlikely(JS_IsException(ret_val)))+ goto exception;+ for(i = -1; i < call_argc; i++)+ JS_FreeValue(ctx, call_argv[i]);+ sp -= call_argc + 1;+ *sp++ = ret_val;+ }+ BREAK;+ /* could merge with OP_apply */+ CASE(OP_apply_eval):+ {+ int scope_idx;+ uint32_t len;+ JSValue *tab;+ JSValueConst obj;++ scope_idx = get_u16(pc) - 1;+ pc += 2;+ tab = build_arg_list(ctx, &len, sp[-1]);+ if (!tab)+ goto exception;+ if (js_same_value(ctx, sp[-2], ctx->eval_obj)) {+ if (len >= 1)+ obj = tab[0];+ else+ obj = JS_UNDEFINED;+ ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj,+ JS_EVAL_TYPE_DIRECT, scope_idx);+ } else {+ ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len,+ (JSValueConst *)tab);+ }+ free_arg_list(ctx, tab, len);+ if (unlikely(JS_IsException(ret_val)))+ goto exception;+ JS_FreeValue(ctx, sp[-2]);+ JS_FreeValue(ctx, sp[-1]);+ sp -= 2;+ *sp++ = ret_val;+ }+ BREAK;++ CASE(OP_regexp):+ {+ sp[-2] = js_regexp_constructor_internal(ctx, JS_UNDEFINED,+ sp[-2], sp[-1]);+ sp--;+ }+ BREAK;++ CASE(OP_get_super):+ {+ JSValue proto;+ proto = JS_GetPrototype(ctx, sp[-1]);+ if (JS_IsException(proto))+ goto exception;+ JS_FreeValue(ctx, sp[-1]);+ sp[-1] = proto;+ }+ BREAK;++ CASE(OP_import):+ {+ JSValue val;+ val = js_dynamic_import(ctx, sp[-1]);+ if (JS_IsException(val))+ goto exception;+ JS_FreeValue(ctx, sp[-1]);+ sp[-1] = val;+ }+ BREAK;++ CASE(OP_check_var):+ {+ int ret;+ JSAtom atom;+ atom = get_u32(pc);+ pc += 4;++ ret = JS_CheckGlobalVar(ctx, atom);+ if (ret < 0)+ goto exception;+ *sp++ = JS_NewBool(ctx, ret);+ }+ BREAK;++ CASE(OP_get_var_undef):+ CASE(OP_get_var):+ {+ JSValue val;+ JSAtom atom;+ atom = get_u32(pc);+ pc += 4;++ val = JS_GetGlobalVar(ctx, atom, opcode - OP_get_var_undef);+ if (unlikely(JS_IsException(val)))+ goto exception;+ *sp++ = val;+ }+ BREAK;++ CASE(OP_put_var):+ CASE(OP_put_var_init):+ {+ int ret;+ JSAtom atom;+ atom = get_u32(pc);+ pc += 4;++ ret = JS_SetGlobalVar(ctx, atom, sp[-1], opcode - OP_put_var);+ sp--;+ if (unlikely(ret < 0))+ goto exception;+ }+ BREAK;++ CASE(OP_put_var_strict):+ {+ int ret;+ JSAtom atom;+ atom = get_u32(pc);+ pc += 4;++ /* sp[-2] is JS_TRUE or JS_FALSE */+ if (unlikely(!JS_VALUE_GET_INT(sp[-2]))) {+ JS_ThrowReferenceErrorNotDefined(ctx, atom);+ goto exception;+ }+ ret = JS_SetGlobalVar(ctx, atom, sp[-1], 2);+ sp -= 2;+ if (unlikely(ret < 0))+ goto exception;+ }+ BREAK;++ CASE(OP_check_define_var):+ {+ JSAtom atom;+ int flags;+ atom = get_u32(pc);+ flags = pc[4];+ pc += 5;+ if (JS_CheckDefineGlobalVar(ctx, atom, flags))+ goto exception;+ }+ BREAK;+ CASE(OP_define_var):+ {+ JSAtom atom;+ int flags;+ atom = get_u32(pc);+ flags = pc[4];+ pc += 5;+ if (JS_DefineGlobalVar(ctx, atom, flags))+ goto exception;+ }+ BREAK;+ CASE(OP_define_func):+ {+ JSAtom atom;+ int flags;+ atom = get_u32(pc);+ flags = pc[4];+ pc += 5;+ if (JS_DefineGlobalFunction(ctx, atom, sp[-1], flags))+ goto exception;+ JS_FreeValue(ctx, sp[-1]);+ sp--;+ }+ BREAK;++ CASE(OP_get_loc):+ {+ int idx;+ idx = get_u16(pc);+ pc += 2;+ sp[0] = JS_DupValue(ctx, var_buf[idx]);+ sp++;+ }+ BREAK;+ CASE(OP_put_loc):+ {+ int idx;+ idx = get_u16(pc);+ pc += 2;+ set_value(ctx, &var_buf[idx], sp[-1]);+ sp--;+ }+ BREAK;+ CASE(OP_set_loc):+ {+ int idx;+ idx = get_u16(pc);+ pc += 2;+ set_value(ctx, &var_buf[idx], JS_DupValue(ctx, sp[-1]));+ }+ BREAK;+ CASE(OP_get_arg):+ {+ int idx;+ idx = get_u16(pc);+ pc += 2;+ sp[0] = JS_DupValue(ctx, arg_buf[idx]);+ sp++;+ }+ BREAK;+ CASE(OP_put_arg):+ {+ int idx;+ idx = get_u16(pc);+ pc += 2;+ set_value(ctx, &arg_buf[idx], sp[-1]);+ sp--;+ }+ BREAK;+ CASE(OP_set_arg):+ {+ int idx;+ idx = get_u16(pc);+ pc += 2;+ set_value(ctx, &arg_buf[idx], JS_DupValue(ctx, sp[-1]));+ }+ BREAK;++#if SHORT_OPCODES+ CASE(OP_get_loc8): *sp++ = JS_DupValue(ctx, var_buf[*pc++]); BREAK;+ CASE(OP_put_loc8): set_value(ctx, &var_buf[*pc++], *--sp); BREAK;+ CASE(OP_set_loc8): set_value(ctx, &var_buf[*pc++], JS_DupValue(ctx, sp[-1])); BREAK;++ CASE(OP_get_loc0): *sp++ = JS_DupValue(ctx, var_buf[0]); BREAK;+ CASE(OP_get_loc1): *sp++ = JS_DupValue(ctx, var_buf[1]); BREAK;+ CASE(OP_get_loc2): *sp++ = JS_DupValue(ctx, var_buf[2]); BREAK;+ CASE(OP_get_loc3): *sp++ = JS_DupValue(ctx, var_buf[3]); BREAK;+ CASE(OP_put_loc0): set_value(ctx, &var_buf[0], *--sp); BREAK;+ CASE(OP_put_loc1): set_value(ctx, &var_buf[1], *--sp); BREAK;+ CASE(OP_put_loc2): set_value(ctx, &var_buf[2], *--sp); BREAK;+ CASE(OP_put_loc3): set_value(ctx, &var_buf[3], *--sp); BREAK;+ CASE(OP_set_loc0): set_value(ctx, &var_buf[0], JS_DupValue(ctx, sp[-1])); BREAK;+ CASE(OP_set_loc1): set_value(ctx, &var_buf[1], JS_DupValue(ctx, sp[-1])); BREAK;+ CASE(OP_set_loc2): set_value(ctx, &var_buf[2], JS_DupValue(ctx, sp[-1])); BREAK;+ CASE(OP_set_loc3): set_value(ctx, &var_buf[3], JS_DupValue(ctx, sp[-1])); BREAK;+ CASE(OP_get_arg0): *sp++ = JS_DupValue(ctx, arg_buf[0]); BREAK;+ CASE(OP_get_arg1): *sp++ = JS_DupValue(ctx, arg_buf[1]); BREAK;+ CASE(OP_get_arg2): *sp++ = JS_DupValue(ctx, arg_buf[2]); BREAK;+ CASE(OP_get_arg3): *sp++ = JS_DupValue(ctx, arg_buf[3]); BREAK;+ CASE(OP_put_arg0): set_value(ctx, &arg_buf[0], *--sp); BREAK;+ CASE(OP_put_arg1): set_value(ctx, &arg_buf[1], *--sp); BREAK;+ CASE(OP_put_arg2): set_value(ctx, &arg_buf[2], *--sp); BREAK;+ CASE(OP_put_arg3): set_value(ctx, &arg_buf[3], *--sp); BREAK;+ CASE(OP_set_arg0): set_value(ctx, &arg_buf[0], JS_DupValue(ctx, sp[-1])); BREAK;+ CASE(OP_set_arg1): set_value(ctx, &arg_buf[1], JS_DupValue(ctx, sp[-1])); BREAK;+ CASE(OP_set_arg2): set_value(ctx, &arg_buf[2], JS_DupValue(ctx, sp[-1])); BREAK;+ CASE(OP_set_arg3): set_value(ctx, &arg_buf[3], JS_DupValue(ctx, sp[-1])); BREAK;+ CASE(OP_get_var_ref0): *sp++ = JS_DupValue(ctx, *var_refs[0]->pvalue); BREAK;+ CASE(OP_get_var_ref1): *sp++ = JS_DupValue(ctx, *var_refs[1]->pvalue); BREAK;+ CASE(OP_get_var_ref2): *sp++ = JS_DupValue(ctx, *var_refs[2]->pvalue); BREAK;+ CASE(OP_get_var_ref3): *sp++ = JS_DupValue(ctx, *var_refs[3]->pvalue); BREAK;+ CASE(OP_put_var_ref0): set_value(ctx, var_refs[0]->pvalue, *--sp); BREAK;+ CASE(OP_put_var_ref1): set_value(ctx, var_refs[1]->pvalue, *--sp); BREAK;+ CASE(OP_put_var_ref2): set_value(ctx, var_refs[2]->pvalue, *--sp); BREAK;+ CASE(OP_put_var_ref3): set_value(ctx, var_refs[3]->pvalue, *--sp); BREAK;+ CASE(OP_set_var_ref0): set_value(ctx, var_refs[0]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;+ CASE(OP_set_var_ref1): set_value(ctx, var_refs[1]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;+ CASE(OP_set_var_ref2): set_value(ctx, var_refs[2]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;+ CASE(OP_set_var_ref3): set_value(ctx, var_refs[3]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;+#endif++ CASE(OP_get_var_ref):+ {+ int idx;+ JSValue val;+ idx = get_u16(pc);+ pc += 2;+ val = *var_refs[idx]->pvalue;+ sp[0] = JS_DupValue(ctx, val);+ sp++;+ }+ BREAK;+ CASE(OP_put_var_ref):+ {+ int idx;+ idx = get_u16(pc);+ pc += 2;+ set_value(ctx, var_refs[idx]->pvalue, sp[-1]);+ sp--;+ }+ BREAK;+ CASE(OP_set_var_ref):+ {+ int idx;+ idx = get_u16(pc);+ pc += 2;+ set_value(ctx, var_refs[idx]->pvalue, JS_DupValue(ctx, sp[-1]));+ }+ BREAK;+ CASE(OP_get_var_ref_check):+ {+ int idx;+ JSValue val;+ idx = get_u16(pc);+ pc += 2;+ val = *var_refs[idx]->pvalue;+ if (unlikely(JS_IsUninitialized(val))) {+ JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);+ goto exception;+ }+ sp[0] = JS_DupValue(ctx, val);+ sp++;+ }+ BREAK;+ CASE(OP_put_var_ref_check):+ {+ int idx;+ idx = get_u16(pc);+ pc += 2;+ if (unlikely(JS_IsUninitialized(*var_refs[idx]->pvalue))) {+ JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);+ goto exception;+ }+ set_value(ctx, var_refs[idx]->pvalue, sp[-1]);+ sp--;+ }+ BREAK;+ CASE(OP_put_var_ref_check_init):+ {+ int idx;+ idx = get_u16(pc);+ pc += 2;+ if (unlikely(!JS_IsUninitialized(*var_refs[idx]->pvalue))) {+ JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);+ goto exception;+ }+ set_value(ctx, var_refs[idx]->pvalue, sp[-1]);+ sp--;+ }+ BREAK;+ CASE(OP_set_loc_uninitialized):+ {+ int idx;+ idx = get_u16(pc);+ pc += 2;+ set_value(ctx, &var_buf[idx], JS_UNINITIALIZED);+ }+ BREAK;+ CASE(OP_get_loc_check):+ {+ int idx;+ idx = get_u16(pc);+ pc += 2;+ if (unlikely(JS_IsUninitialized(var_buf[idx]))) {+ JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE);+ goto exception;+ }+ sp[0] = JS_DupValue(ctx, var_buf[idx]);+ sp++;+ }+ BREAK;+ CASE(OP_put_loc_check):+ {+ int idx;+ idx = get_u16(pc);+ pc += 2;+ if (unlikely(JS_IsUninitialized(var_buf[idx]))) {+ JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE);+ goto exception;+ }+ set_value(ctx, &var_buf[idx], sp[-1]);+ sp--;+ }+ BREAK;+ CASE(OP_put_loc_check_init):+ {+ int idx;+ idx = get_u16(pc);+ pc += 2;+ if (unlikely(!JS_IsUninitialized(var_buf[idx]))) {+ JS_ThrowReferenceError(ctx, "'this' can be initialized only once");+ goto exception;+ }+ set_value(ctx, &var_buf[idx], sp[-1]);+ sp--;+ }+ BREAK;+ CASE(OP_close_loc):+ {+ int idx;+ idx = get_u16(pc);+ pc += 2;+ close_lexical_var(ctx, sf, idx, FALSE);+ }+ BREAK;++ CASE(OP_make_loc_ref):+ CASE(OP_make_arg_ref):+ CASE(OP_make_var_ref_ref):+ {+ JSVarRef *var_ref;+ JSProperty *pr;+ JSAtom atom;+ int idx;+ atom = get_u32(pc);+ idx = get_u16(pc + 4);+ pc += 6;+ *sp++ = JS_NewObjectProto(ctx, JS_NULL);+ if (unlikely(JS_IsException(sp[-1])))+ goto exception;+ if (opcode == OP_make_var_ref_ref) {+ var_ref = var_refs[idx];+ var_ref->header.ref_count++;+ } else {+ var_ref = get_var_ref(ctx, sf, idx, opcode == OP_make_arg_ref);+ if (!var_ref)+ goto exception;+ }+ pr = add_property(ctx, JS_VALUE_GET_OBJ(sp[-1]), atom,+ JS_PROP_WRITABLE | JS_PROP_VARREF);+ if (!pr) {+ free_var_ref(rt, var_ref);+ goto exception;+ }+ pr->u.var_ref = var_ref;+ *sp++ = JS_AtomToValue(ctx, atom);+ }+ BREAK;+ CASE(OP_make_var_ref):+ {+ JSAtom atom;+ atom = get_u32(pc);+ pc += 4;++ if (JS_GetGlobalVarRef(ctx, atom, sp))+ goto exception;+ sp += 2;+ }+ BREAK;++ CASE(OP_goto):+ pc += (int32_t)get_u32(pc);+ if (unlikely(js_poll_interrupts(ctx)))+ goto exception;+ BREAK;+#if SHORT_OPCODES+ CASE(OP_goto16):+ pc += (int16_t)get_u16(pc);+ if (unlikely(js_poll_interrupts(ctx)))+ goto exception;+ BREAK;+ CASE(OP_goto8):+ pc += (int8_t)pc[0];+ if (unlikely(js_poll_interrupts(ctx)))+ goto exception;+ BREAK;+#endif+ CASE(OP_if_true):+ {+ int res;+ JSValue op1;++ op1 = sp[-1];+ pc += 4;+ if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {+ res = JS_VALUE_GET_INT(op1);+ } else {+ res = JS_ToBoolFree(ctx, op1);+ }+ sp--;+ if (res) {+ pc += (int32_t)get_u32(pc - 4) - 4;+ }+ if (unlikely(js_poll_interrupts(ctx)))+ goto exception;+ }+ BREAK;+ CASE(OP_if_false):+ {+ int res;+ JSValue op1;++ op1 = sp[-1];+ pc += 4;+ if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {+ res = JS_VALUE_GET_INT(op1);+ } else {+ res = JS_ToBoolFree(ctx, op1);+ }+ sp--;+ if (!res) {+ pc += (int32_t)get_u32(pc - 4) - 4;+ }+ if (unlikely(js_poll_interrupts(ctx)))+ goto exception;+ }+ BREAK;+#if SHORT_OPCODES+ CASE(OP_if_true8):+ {+ int res;+ JSValue op1;++ op1 = sp[-1];+ pc += 1;+ if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {+ res = JS_VALUE_GET_INT(op1);+ } else {+ res = JS_ToBoolFree(ctx, op1);+ }+ sp--;+ if (res) {+ pc += (int8_t)pc[-1] - 1;+ }+ if (unlikely(js_poll_interrupts(ctx)))+ goto exception;+ }+ BREAK;+ CASE(OP_if_false8):+ {+ int res;+ JSValue op1;++ op1 = sp[-1];+ pc += 1;+ if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {+ res = JS_VALUE_GET_INT(op1);+ } else {+ res = JS_ToBoolFree(ctx, op1);+ }+ sp--;+ if (!res) {+ pc += (int8_t)pc[-1] - 1;+ }+ if (unlikely(js_poll_interrupts(ctx)))+ goto exception;+ }+ BREAK;+#endif+ CASE(OP_catch):+ {+ int32_t diff;+ diff = get_u32(pc);+ sp[0] = JS_NewCatchOffset(ctx, pc + diff - b->byte_code_buf);+ sp++;+ pc += 4;+ }+ BREAK;+ CASE(OP_gosub):+ {+ int32_t diff;+ diff = get_u32(pc);+ /* XXX: should have a different tag to avoid security flaw */+ sp[0] = JS_NewInt32(ctx, pc + 4 - b->byte_code_buf);+ sp++;+ pc += diff;+ }+ BREAK;+ CASE(OP_ret):+ {+ JSValue op1;+ uint32_t pos;+ op1 = sp[-1];+ if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_INT))+ goto ret_fail;+ pos = JS_VALUE_GET_INT(op1);+ if (unlikely(pos >= b->byte_code_len)) {+ ret_fail:+ JS_ThrowInternalError(ctx, "invalid ret value");+ goto exception;+ }+ sp--;+ pc = b->byte_code_buf + pos;+ }+ BREAK;++ CASE(OP_for_in_start):+ if (js_for_in_start(ctx, sp))+ goto exception;+ BREAK;+ CASE(OP_for_in_next):+ if (js_for_in_next(ctx, sp))+ goto exception;+ sp += 2;+ BREAK;+ CASE(OP_for_of_start):+ if (js_for_of_start(ctx, sp, FALSE))+ goto exception;+ sp += 1;+ *sp++ = JS_NewCatchOffset(ctx, 0);+ BREAK;+ CASE(OP_for_of_next):+ {+ int offset = -3 - pc[0];+ pc += 1;+ if (js_for_of_next(ctx, sp, offset))+ goto exception;+ sp += 2;+ }+ BREAK;+ CASE(OP_for_await_of_start):+ if (js_for_of_start(ctx, sp, TRUE))+ goto exception;+ sp += 1;+ *sp++ = JS_NewCatchOffset(ctx, 0);+ BREAK;+ CASE(OP_iterator_get_value_done):+ if (js_iterator_get_value_done(ctx, sp))+ goto exception;+ sp += 1;+ BREAK;+ CASE(OP_iterator_check_object):+ if (unlikely(!JS_IsObject(sp[-1]))) {+ JS_ThrowTypeError(ctx, "iterator must return an object");+ goto exception;+ }+ BREAK;++ CASE(OP_iterator_close):+ /* iter_obj next catch_offset -> */+ sp--; /* drop the catch offset to avoid getting caught by exception */+ JS_FreeValue(ctx, sp[-1]); /* drop the next method */+ sp--;+ if (!JS_IsUndefined(sp[-1])) {+ if (JS_IteratorClose(ctx, sp[-1], FALSE))+ goto exception;+ JS_FreeValue(ctx, sp[-1]);+ }+ sp--;+ BREAK;+ CASE(OP_iterator_close_return):+ {+ JSValue ret_val;+ /* iter_obj next catch_offset ... ret_val ->+ ret_eval iter_obj next catch_offset */+ ret_val = *--sp;+ while (sp > stack_buf &&+ JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_CATCH_OFFSET) {+ JS_FreeValue(ctx, *--sp);+ }+ if (unlikely(sp < stack_buf + 3)) {+ JS_ThrowInternalError(ctx, "iterator_close_return");+ JS_FreeValue(ctx, ret_val);+ goto exception;+ }+ sp[0] = sp[-1];+ sp[-1] = sp[-2];+ sp[-2] = sp[-3];+ sp[-3] = ret_val;+ sp++;+ }+ BREAK;++ CASE(OP_iterator_next):+ /* stack: iter_obj next catch_offset val */+ {+ JSValue ret;+ ret = JS_Call(ctx, sp[-3], sp[-4],+ 1, (JSValueConst *)(sp - 1));+ if (JS_IsException(ret))+ goto exception;+ JS_FreeValue(ctx, sp[-1]);+ sp[-1] = ret;+ }+ BREAK;++ CASE(OP_iterator_call):+ /* stack: iter_obj next catch_offset val */+ {+ JSValue method, ret;+ BOOL ret_flag;+ int flags;+ flags = *pc++;+ method = JS_GetProperty(ctx, sp[-4], (flags & 1) ?+ JS_ATOM_throw : JS_ATOM_return);+ if (JS_IsException(method))+ goto exception;+ if (JS_IsUndefined(method) || JS_IsNull(method)) {+ ret_flag = TRUE;+ } else {+ if (flags & 2) {+ /* no argument */+ ret = JS_CallFree(ctx, method, sp[-4],+ 0, NULL);+ } else {+ ret = JS_CallFree(ctx, method, sp[-4],+ 1, (JSValueConst *)(sp - 1));+ }+ if (JS_IsException(ret))+ goto exception;+ JS_FreeValue(ctx, sp[-1]);+ sp[-1] = ret;+ ret_flag = FALSE;+ }+ sp[0] = JS_NewBool(ctx, ret_flag);+ sp += 1;+ }+ BREAK;++ CASE(OP_lnot):+ {+ int res;+ JSValue op1;++ op1 = sp[-1];+ if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {+ res = JS_VALUE_GET_INT(op1) != 0;+ } else {+ res = JS_ToBoolFree(ctx, op1);+ }+ sp[-1] = JS_NewBool(ctx, !res);+ }+ BREAK;++ CASE(OP_get_field):+ {+ JSValue val;+ JSAtom atom;+ atom = get_u32(pc);+ pc += 4;++ val = JS_GetProperty(ctx, sp[-1], atom);+ if (unlikely(JS_IsException(val)))+ goto exception;+ JS_FreeValue(ctx, sp[-1]);+ sp[-1] = val;+ }+ BREAK;++ CASE(OP_get_field2):+ {+ JSValue val;+ JSAtom atom;+ atom = get_u32(pc);+ pc += 4;++ val = JS_GetProperty(ctx, sp[-1], atom);+ if (unlikely(JS_IsException(val)))+ goto exception;+ *sp++ = val;+ }+ BREAK;++ CASE(OP_put_field):+ {+ int ret;+ JSAtom atom;+ atom = get_u32(pc);+ pc += 4;++ ret = JS_SetPropertyInternal(ctx, sp[-2], atom, sp[-1],+ JS_PROP_THROW_STRICT);+ JS_FreeValue(ctx, sp[-2]);+ sp -= 2;+ if (unlikely(ret < 0))+ goto exception;+ }+ BREAK;++ CASE(OP_private_symbol):+ {+ JSAtom atom;+ JSValue val;+ + atom = get_u32(pc);+ pc += 4;+ val = JS_NewSymbolFromAtom(ctx, atom, JS_ATOM_TYPE_PRIVATE);+ if (JS_IsException(val))+ goto exception;+ *sp++ = val;+ }+ BREAK;+ + CASE(OP_get_private_field):+ {+ JSValue val;++ val = JS_GetPrivateField(ctx, sp[-2], sp[-1]);+ JS_FreeValue(ctx, sp[-1]);+ JS_FreeValue(ctx, sp[-2]);+ sp[-2] = val;+ sp--;+ if (unlikely(JS_IsException(val)))+ goto exception;+ }+ BREAK;++ CASE(OP_put_private_field):+ {+ int ret;+ ret = JS_SetPrivateField(ctx, sp[-3], sp[-1], sp[-2]);+ JS_FreeValue(ctx, sp[-3]);+ JS_FreeValue(ctx, sp[-1]);+ sp -= 3;+ if (unlikely(ret < 0))+ goto exception;+ }+ BREAK;++ CASE(OP_define_private_field):+ {+ int ret;+ ret = JS_DefinePrivateField(ctx, sp[-3], sp[-2], sp[-1]);+ JS_FreeValue(ctx, sp[-2]);+ sp -= 2;+ if (unlikely(ret < 0))+ goto exception;+ }+ BREAK;++ CASE(OP_define_field):+ {+ int ret;+ JSAtom atom;+ atom = get_u32(pc);+ pc += 4;++ ret = JS_DefinePropertyValue(ctx, sp[-2], atom, sp[-1],+ JS_PROP_C_W_E | JS_PROP_THROW);+ sp--;+ if (unlikely(ret < 0))+ goto exception;+ }+ BREAK;++ CASE(OP_set_name):+ {+ int ret;+ JSAtom atom;+ atom = get_u32(pc);+ pc += 4;++ ret = JS_DefineObjectName(ctx, sp[-1], atom, JS_PROP_CONFIGURABLE);+ if (unlikely(ret < 0))+ goto exception;+ }+ BREAK;+ CASE(OP_set_name_computed):+ {+ int ret;+ ret = JS_DefineObjectNameComputed(ctx, sp[-1], sp[-2], JS_PROP_CONFIGURABLE);+ if (unlikely(ret < 0))+ goto exception;+ }+ BREAK;+ CASE(OP_set_proto):+ {+ JSValue proto;+ proto = sp[-1];+ if (JS_IsObject(proto) || JS_IsNull(proto)) {+ if (JS_SetPrototypeInternal(ctx, sp[-2], proto, TRUE) < 0)+ goto exception;+ }+ JS_FreeValue(ctx, proto);+ sp--;+ }+ BREAK;+ CASE(OP_set_home_object):+ js_method_set_home_object(ctx, sp[-1], sp[-2]);+ BREAK;+ CASE(OP_define_method):+ CASE(OP_define_method_computed):+ {+ JSValue getter, setter, value;+ JSValueConst obj;+ JSAtom atom;+ int flags, ret, op_flags;+ BOOL is_computed;+#define OP_DEFINE_METHOD_METHOD 0+#define OP_DEFINE_METHOD_GETTER 1+#define OP_DEFINE_METHOD_SETTER 2+#define OP_DEFINE_METHOD_ENUMERABLE 4++ is_computed = (opcode == OP_define_method_computed);+ if (is_computed) {+ atom = JS_ValueToAtom(ctx, sp[-2]);+ if (unlikely(atom == JS_ATOM_NULL))+ goto exception;+ opcode += OP_define_method - OP_define_method_computed;+ } else {+ atom = get_u32(pc);+ pc += 4;+ }+ op_flags = *pc++;++ obj = sp[-2 - is_computed];+ flags = JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE |+ JS_PROP_HAS_ENUMERABLE | JS_PROP_THROW;+ if (op_flags & OP_DEFINE_METHOD_ENUMERABLE)+ flags |= JS_PROP_ENUMERABLE;+ op_flags &= 3;+ value = JS_UNDEFINED;+ getter = JS_UNDEFINED;+ setter = JS_UNDEFINED;+ if (op_flags == OP_DEFINE_METHOD_METHOD) {+ value = sp[-1];+ flags |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE;+ } else if (op_flags == OP_DEFINE_METHOD_GETTER) {+ getter = sp[-1];+ flags |= JS_PROP_HAS_GET;+ } else {+ setter = sp[-1];+ flags |= JS_PROP_HAS_SET;+ }+ ret = js_method_set_properties(ctx, sp[-1], atom, flags, obj);+ if (ret >= 0) {+ ret = JS_DefineProperty(ctx, obj, atom, value,+ getter, setter, flags);+ }+ JS_FreeValue(ctx, sp[-1]);+ if (is_computed) {+ JS_FreeAtom(ctx, atom);+ JS_FreeValue(ctx, sp[-2]);+ }+ sp -= 1 + is_computed;+ if (unlikely(ret < 0))+ goto exception;+ }+ BREAK;++ CASE(OP_define_class):+ CASE(OP_define_class_computed):+ {+ int class_flags;+ JSAtom atom;+ + atom = get_u32(pc);+ class_flags = pc[4];+ pc += 5;+ if (js_op_define_class(ctx, sp, atom, class_flags,+ var_refs, sf,+ (opcode == OP_define_class_computed)) < 0)+ goto exception;+ }+ BREAK;++ CASE(OP_get_array_el):+ {+ JSValue val;++ val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]);+ JS_FreeValue(ctx, sp[-2]);+ sp[-2] = val;+ sp--;+ if (unlikely(JS_IsException(val)))+ goto exception;+ }+ BREAK;++ CASE(OP_get_array_el2):+ {+ JSValue val;++ val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]);+ sp[-1] = val;+ if (unlikely(JS_IsException(val)))+ goto exception;+ }+ BREAK;++ CASE(OP_get_ref_value):+ {+ JSValue val;+ if (unlikely(JS_IsUndefined(sp[-2]))) {+ JSAtom atom = JS_ValueToAtom(ctx, sp[-1]);+ if (atom != JS_ATOM_NULL) {+ JS_ThrowReferenceErrorNotDefined(ctx, atom);+ JS_FreeAtom(ctx, atom);+ }+ goto exception;+ }+ val = JS_GetPropertyValue(ctx, sp[-2],+ JS_DupValue(ctx, sp[-1]));+ if (unlikely(JS_IsException(val)))+ goto exception;+ sp[0] = val;+ sp++;+ }+ BREAK;++ CASE(OP_get_super_value):+ {+ JSValue val;+ JSAtom atom;+ atom = JS_ValueToAtom(ctx, sp[-1]);+ if (unlikely(atom == JS_ATOM_NULL))+ goto exception;+ val = JS_GetPropertyInternal(ctx, sp[-2], atom, sp[-3], FALSE);+ JS_FreeAtom(ctx, atom);+ if (unlikely(JS_IsException(val)))+ goto exception;+ JS_FreeValue(ctx, sp[-1]);+ JS_FreeValue(ctx, sp[-2]);+ JS_FreeValue(ctx, sp[-3]);+ sp[-3] = val;+ sp -= 2;+ }+ BREAK;++ CASE(OP_put_array_el):+ {+ int ret;++ ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT);+ JS_FreeValue(ctx, sp[-3]);+ sp -= 3;+ if (unlikely(ret < 0))+ goto exception;+ }+ BREAK;++ CASE(OP_put_ref_value):+ {+ int ret, flags;+ flags = JS_PROP_THROW_STRICT;+ if (unlikely(JS_IsUndefined(sp[-3]))) {+ if (is_strict_mode(ctx)) {+ JSAtom atom = JS_ValueToAtom(ctx, sp[-2]);+ if (atom != JS_ATOM_NULL) {+ JS_ThrowReferenceErrorNotDefined(ctx, atom);+ JS_FreeAtom(ctx, atom);+ }+ goto exception;+ } else {+ sp[-3] = JS_DupValue(ctx, ctx->global_obj);+ }+ } else {+ if (is_strict_mode(ctx))+ flags |= JS_PROP_NO_ADD;+ }+ ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], flags);+ JS_FreeValue(ctx, sp[-3]);+ sp -= 3;+ if (unlikely(ret < 0))+ goto exception;+ }+ BREAK;++ CASE(OP_put_super_value):+ {+ int ret;+ JSAtom atom;+ if (JS_VALUE_GET_TAG(sp[-3]) != JS_TAG_OBJECT) {+ JS_ThrowTypeErrorNotAnObject(ctx);+ goto exception;+ }+ atom = JS_ValueToAtom(ctx, sp[-2]);+ if (unlikely(atom == JS_ATOM_NULL))+ goto exception;+ ret = JS_SetPropertyGeneric(ctx, JS_VALUE_GET_OBJ(sp[-3]),+ atom, sp[-1], sp[-4],+ JS_PROP_THROW_STRICT);+ JS_FreeAtom(ctx, atom);+ JS_FreeValue(ctx, sp[-4]);+ JS_FreeValue(ctx, sp[-3]);+ JS_FreeValue(ctx, sp[-2]);+ sp -= 4;+ if (ret < 0)+ goto exception;+ }+ BREAK;++ CASE(OP_define_array_el):+ {+ int ret;+ ret = JS_DefinePropertyValueValue(ctx, sp[-3], JS_DupValue(ctx, sp[-2]), sp[-1],+ JS_PROP_C_W_E | JS_PROP_THROW);+ sp -= 1;+ if (unlikely(ret < 0))+ goto exception;+ }+ BREAK;++ CASE(OP_append): /* array pos enumobj -- array pos */+ {+ if (js_append_enumerate(ctx, sp))+ goto exception;+ JS_FreeValue(ctx, *--sp);+ }+ BREAK;++ CASE(OP_copy_data_properties): /* target source excludeList */+ {+ /* stack offsets (-1 based):+ 2 bits for target,+ 3 bits for source,+ 2 bits for exclusionList */+ int mask;++ mask = *pc++;+ if (JS_CopyDataProperties(ctx, sp[-1 - (mask & 3)],+ sp[-1 - ((mask >> 2) & 7)],+ sp[-1 - ((mask >> 5) & 7)], 0))+ goto exception;+ }+ BREAK;++ CASE(OP_add):+ {+ JSValue op1, op2;+ op1 = sp[-2];+ op2 = sp[-1];+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+ int64_t r;+ r = (int64_t)JS_VALUE_GET_INT(op1) + JS_VALUE_GET_INT(op2);+ if (unlikely((int)r != r))+ goto add_slow;+ sp[-2] = JS_NewInt32(ctx, r);+ sp--;+ } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {+ sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) ++ JS_VALUE_GET_FLOAT64(op2));+ sp--;+ } else {+ add_slow:+ if (js_add_slow(ctx, sp))+ goto exception;+ sp--;+ }+ }+ BREAK;+ CASE(OP_add_loc):+ {+ JSValue *pv;+ int idx;+ idx = *pc;+ pc += 1;++ pv = &var_buf[idx];+ if (likely(JS_VALUE_IS_BOTH_INT(*pv, sp[-1]))) {+ int64_t r;+ r = (int64_t)JS_VALUE_GET_INT(*pv) ++ JS_VALUE_GET_INT(sp[-1]);+ if (unlikely((int)r != r))+ goto add_loc_slow;+ *pv = JS_NewInt32(ctx, r);+ sp--;+ } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING) {+ JSValue op1;+ op1 = sp[-1];+ sp--;+ op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);+ if (JS_IsException(op1))+ goto exception;+ op1 = JS_ConcatString(ctx, JS_DupValue(ctx, *pv), op1);+ if (JS_IsException(op1))+ goto exception;+ set_value(ctx, pv, op1);+ } else {+ JSValue ops[2];+ add_loc_slow:+ /* In case of exception, js_add_slow frees ops[0]+ and ops[1], so we must duplicate *pv */+ ops[0] = JS_DupValue(ctx, *pv);+ ops[1] = sp[-1];+ sp--;+ if (js_add_slow(ctx, ops + 2))+ goto exception;+ set_value(ctx, pv, ops[0]);+ }+ }+ BREAK;+ CASE(OP_sub):+ {+ JSValue op1, op2;+ op1 = sp[-2];+ op2 = sp[-1];+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+ int64_t r;+ r = (int64_t)JS_VALUE_GET_INT(op1) - JS_VALUE_GET_INT(op2);+ if (unlikely((int)r != r))+ goto binary_arith_slow;+ sp[-2] = JS_NewInt32(ctx, r);+ sp--;+ } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {+ sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) -+ JS_VALUE_GET_FLOAT64(op2));+ sp--;+ } else {+ goto binary_arith_slow;+ }+ }+ BREAK;+ CASE(OP_mul):+ {+ JSValue op1, op2;+ double d;+ op1 = sp[-2];+ op2 = sp[-1];+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+ int32_t v1, v2;+ int64_t r;+ v1 = JS_VALUE_GET_INT(op1);+ v2 = JS_VALUE_GET_INT(op2);+ r = (int64_t)v1 * v2;+ if (unlikely((int)r != r)) {+#ifdef CONFIG_BIGNUM+ if (unlikely(sf->js_mode & JS_MODE_MATH) &&+ (r < -MAX_SAFE_INTEGER || r > MAX_SAFE_INTEGER))+ goto binary_arith_slow;+#endif+ d = (double)r;+ goto mul_fp_res;+ }+ /* need to test zero case for -0 result */+ if (unlikely(r == 0 && (v1 | v2) < 0)) {+ d = -0.0;+ goto mul_fp_res;+ }+ sp[-2] = JS_NewInt32(ctx, r);+ sp--;+ } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {+#ifdef CONFIG_BIGNUM+ if (unlikely(sf->js_mode & JS_MODE_MATH))+ goto binary_arith_slow;+#endif+ d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2);+ mul_fp_res:+ sp[-2] = __JS_NewFloat64(ctx, d);+ sp--;+ } else {+ goto binary_arith_slow;+ }+ }+ BREAK;+ CASE(OP_div):+ {+ JSValue op1, op2;+ op1 = sp[-2];+ op2 = sp[-1];+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+ int v1, v2;+ if (unlikely(sf->js_mode & JS_MODE_MATH))+ goto binary_arith_slow;+ v1 = JS_VALUE_GET_INT(op1);+ v2 = JS_VALUE_GET_INT(op2);+ sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2);+ sp--;+ } else {+ goto binary_arith_slow;+ }+ }+ BREAK;+ CASE(OP_mod):+#ifdef CONFIG_BIGNUM+ CASE(OP_math_mod):+#endif+ {+ JSValue op1, op2;+ op1 = sp[-2];+ op2 = sp[-1];+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+ int v1, v2, r;+ v1 = JS_VALUE_GET_INT(op1);+ v2 = JS_VALUE_GET_INT(op2);+ /* We must avoid v2 = 0, v1 = INT32_MIN and v2 =+ -1 and the cases where the result is -0. */+ if (unlikely(v1 < 0 || v2 <= 0))+ goto binary_arith_slow;+ r = v1 % v2;+ sp[-2] = JS_NewInt32(ctx, r);+ sp--;+ } else {+ goto binary_arith_slow;+ }+ }+ BREAK;+ CASE(OP_pow):+ binary_arith_slow:+ if (js_binary_arith_slow(ctx, sp, opcode))+ goto exception;+ sp--;+ BREAK;++ CASE(OP_plus):+ {+ JSValue op1;+ uint32_t tag;+ op1 = sp[-1];+ tag = JS_VALUE_GET_TAG(op1);+ if (tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag)) {+ } else {+ if (js_unary_arith_slow(ctx, sp, opcode))+ goto exception;+ }+ }+ BREAK;+ CASE(OP_neg):+ {+ JSValue op1;+ uint32_t tag;+ int val;+ double d;+ op1 = sp[-1];+ tag = JS_VALUE_GET_TAG(op1);+ if (tag == JS_TAG_INT) {+ val = JS_VALUE_GET_INT(op1);+ /* Note: -0 cannot be expressed as integer */+ if (unlikely(val == 0)) {+ d = -0.0;+ goto neg_fp_res;+ }+ if (unlikely(val == INT32_MIN)) {+ d = -(double)val;+ goto neg_fp_res;+ }+ sp[-1] = JS_NewInt32(ctx, -val);+ } else if (JS_TAG_IS_FLOAT64(tag)) {+ d = -JS_VALUE_GET_FLOAT64(op1);+ neg_fp_res:+ sp[-1] = __JS_NewFloat64(ctx, d);+ } else {+ if (js_unary_arith_slow(ctx, sp, opcode))+ goto exception;+ }+ }+ BREAK;+ CASE(OP_inc):+ {+ JSValue op1;+ int val;+ op1 = sp[-1];+ if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {+ val = JS_VALUE_GET_INT(op1);+ if (unlikely(val == INT32_MAX))+ goto inc_slow;+ sp[-1] = JS_NewInt32(ctx, val + 1);+ } else {+ inc_slow:+ if (js_unary_arith_slow(ctx, sp, opcode))+ goto exception;+ }+ }+ BREAK;+ CASE(OP_dec):+ {+ JSValue op1;+ int val;+ op1 = sp[-1];+ if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {+ val = JS_VALUE_GET_INT(op1);+ if (unlikely(val == INT32_MIN))+ goto dec_slow;+ sp[-1] = JS_NewInt32(ctx, val - 1);+ } else {+ dec_slow:+ if (js_unary_arith_slow(ctx, sp, opcode))+ goto exception;+ }+ }+ BREAK;+ CASE(OP_post_inc):+ CASE(OP_post_dec):+ if (js_post_inc_slow(ctx, sp, opcode))+ goto exception;+ sp++;+ BREAK;+ CASE(OP_inc_loc):+ {+ JSValue op1;+ int val;+ int idx;+ idx = *pc;+ pc += 1;++ op1 = var_buf[idx];+ if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {+ val = JS_VALUE_GET_INT(op1);+ if (unlikely(val == INT32_MAX))+ goto inc_loc_slow;+ var_buf[idx] = JS_NewInt32(ctx, val + 1);+ } else {+ inc_loc_slow:+ /* must duplicate otherwise the variable value may+ be destroyed before JS code accesses it */+ op1 = JS_DupValue(ctx, op1);+ if (js_unary_arith_slow(ctx, &op1 + 1, OP_inc))+ goto exception;+ set_value(ctx, &var_buf[idx], op1);+ }+ }+ BREAK;+ CASE(OP_dec_loc):+ {+ JSValue op1;+ int val;+ int idx;+ idx = *pc;+ pc += 1;++ op1 = var_buf[idx];+ if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {+ val = JS_VALUE_GET_INT(op1);+ if (unlikely(val == INT32_MIN))+ goto dec_loc_slow;+ var_buf[idx] = JS_NewInt32(ctx, val - 1);+ } else {+ dec_loc_slow:+ /* must duplicate otherwise the variable value may+ be destroyed before JS code accesses it */+ op1 = JS_DupValue(ctx, op1);+ if (js_unary_arith_slow(ctx, &op1 + 1, OP_dec))+ goto exception;+ set_value(ctx, &var_buf[idx], op1);+ }+ }+ BREAK;+ CASE(OP_not):+ {+ JSValue op1;+ op1 = sp[-1];+ if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {+ sp[-1] = JS_NewInt32(ctx, ~JS_VALUE_GET_INT(op1));+ } else {+ if (js_not_slow(ctx, sp))+ goto exception;+ }+ }+ BREAK;++ CASE(OP_shl):+ {+ JSValue op1, op2;+ op1 = sp[-2];+ op2 = sp[-1];+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+ uint32_t v1, v2;+ v1 = JS_VALUE_GET_INT(op1);+ v2 = JS_VALUE_GET_INT(op2);+#ifdef CONFIG_BIGNUM+ {+ int64_t r;+ if (unlikely(sf->js_mode & JS_MODE_MATH)) {+ if (v2 > 0x1f)+ goto shl_slow;+ r = (int64_t)v1 << v2;+ if ((int)r != r)+ goto shl_slow;+ } else {+ v2 &= 0x1f;+ }+ }+#else+ v2 &= 0x1f;+#endif+ sp[-2] = JS_NewInt32(ctx, v1 << v2);+ sp--;+ } else {+#ifdef CONFIG_BIGNUM+ shl_slow:+#endif+ if (js_binary_logic_slow(ctx, sp, opcode))+ goto exception;+ sp--;+ }+ }+ BREAK;+ CASE(OP_shr):+ {+ JSValue op1, op2;+ op1 = sp[-2];+ op2 = sp[-1];+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+ uint32_t v2;+ v2 = JS_VALUE_GET_INT(op2);+ /* v1 >>> v2 retains its JS semantics if CONFIG_BIGNUM */+ v2 &= 0x1f;+ sp[-2] = JS_NewUint32(ctx,+ (uint32_t)JS_VALUE_GET_INT(op1) >>+ v2);+ sp--;+ } else {+ if (js_shr_slow(ctx, sp))+ goto exception;+ sp--;+ }+ }+ BREAK;+ CASE(OP_sar):+ {+ JSValue op1, op2;+ op1 = sp[-2];+ op2 = sp[-1];+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+ uint32_t v2;+ v2 = JS_VALUE_GET_INT(op2);+#ifdef CONFIG_BIGNUM+ if (unlikely(v2 > 0x1f)) {+ if (unlikely(sf->js_mode & JS_MODE_MATH))+ goto sar_slow;+ else+ v2 &= 0x1f;+ }+#else+ v2 &= 0x1f;+#endif+ sp[-2] = JS_NewInt32(ctx,+ (int)JS_VALUE_GET_INT(op1) >> v2);+ sp--;+ } else {+#ifdef CONFIG_BIGNUM+ sar_slow:+#endif+ if (js_binary_logic_slow(ctx, sp, opcode))+ goto exception;+ sp--;+ }+ }+ BREAK;+ CASE(OP_and):+ {+ JSValue op1, op2;+ op1 = sp[-2];+ op2 = sp[-1];+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+ sp[-2] = JS_NewInt32(ctx,+ JS_VALUE_GET_INT(op1) &+ JS_VALUE_GET_INT(op2));+ sp--;+ } else {+ if (js_binary_logic_slow(ctx, sp, opcode))+ goto exception;+ sp--;+ }+ }+ BREAK;+ CASE(OP_or):+ {+ JSValue op1, op2;+ op1 = sp[-2];+ op2 = sp[-1];+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+ sp[-2] = JS_NewInt32(ctx,+ JS_VALUE_GET_INT(op1) |+ JS_VALUE_GET_INT(op2));+ sp--;+ } else {+ if (js_binary_logic_slow(ctx, sp, opcode))+ goto exception;+ sp--;+ }+ }+ BREAK;+ CASE(OP_xor):+ {+ JSValue op1, op2;+ op1 = sp[-2];+ op2 = sp[-1];+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {+ sp[-2] = JS_NewInt32(ctx,+ JS_VALUE_GET_INT(op1) ^+ JS_VALUE_GET_INT(op2));+ sp--;+ } else {+ if (js_binary_logic_slow(ctx, sp, opcode))+ goto exception;+ sp--;+ }+ }+ BREAK;+++#define OP_CMP(opcode, binary_op, slow_call) \+ CASE(opcode): \+ { \+ JSValue op1, op2; \+ op1 = sp[-2]; \+ op2 = sp[-1]; \+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { \+ sp[-2] = JS_NewBool(ctx, JS_VALUE_GET_INT(op1) binary_op JS_VALUE_GET_INT(op2)); \+ sp--; \+ } else { \+ if (slow_call) \+ goto exception; \+ sp--; \+ } \+ } \+ BREAK++ OP_CMP(OP_lt, <, js_relational_slow(ctx, sp, opcode));+ OP_CMP(OP_lte, <=, js_relational_slow(ctx, sp, opcode));+ OP_CMP(OP_gt, >, js_relational_slow(ctx, sp, opcode));+ OP_CMP(OP_gte, >=, js_relational_slow(ctx, sp, opcode));+ OP_CMP(OP_eq, ==, js_eq_slow(ctx, sp, 0));+ OP_CMP(OP_neq, !=, js_eq_slow(ctx, sp, 1));+ OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, sp, 0));+ OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, sp, 1));++#ifdef CONFIG_BIGNUM+ CASE(OP_mul_pow10):+ if (rt->bigfloat_ops.mul_pow10(ctx, sp))+ goto exception;+ sp--;+ BREAK;+#endif+ CASE(OP_in):+ if (js_operator_in(ctx, sp))+ goto exception;+ sp--;+ BREAK;+ CASE(OP_instanceof):+ if (js_operator_instanceof(ctx, sp))+ goto exception;+ sp--;+ BREAK;+ CASE(OP_typeof):+ {+ JSValue op1;+ JSAtom atom;++ op1 = sp[-1];+ atom = js_operator_typeof(ctx, op1);+ JS_FreeValue(ctx, op1);+ sp[-1] = JS_AtomToString(ctx, atom);+ }+ BREAK;+ CASE(OP_delete):+ if (js_operator_delete(ctx, sp))+ goto exception;+ sp--;+ BREAK;+ CASE(OP_delete_var):+ {+ JSAtom atom;+ int ret;++ atom = get_u32(pc);+ pc += 4;++ ret = JS_DeleteProperty(ctx, ctx->global_obj, atom, 0);+ if (unlikely(ret < 0))+ goto exception;+ *sp++ = JS_NewBool(ctx, ret);+ }+ BREAK;++ CASE(OP_to_object):+ if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_OBJECT) {+ ret_val = JS_ToObject(ctx, sp[-1]);+ if (JS_IsException(ret_val))+ goto exception;+ JS_FreeValue(ctx, sp[-1]);+ sp[-1] = ret_val;+ }+ BREAK;++ CASE(OP_to_propkey):+ switch (JS_VALUE_GET_TAG(sp[-1])) {+ case JS_TAG_INT:+ case JS_TAG_STRING:+ case JS_TAG_SYMBOL:+ break;+ default:+ ret_val = JS_ToPropertyKey(ctx, sp[-1]);+ if (JS_IsException(ret_val))+ goto exception;+ JS_FreeValue(ctx, sp[-1]);+ sp[-1] = ret_val;+ break;+ }+ BREAK;++ CASE(OP_to_propkey2):+ /* must be tested first */+ if (unlikely(JS_IsUndefined(sp[-2]) || JS_IsNull(sp[-2]))) {+ JS_ThrowTypeError(ctx, "value has no property");+ goto exception;+ }+ switch (JS_VALUE_GET_TAG(sp[-1])) {+ case JS_TAG_INT:+ case JS_TAG_STRING:+ case JS_TAG_SYMBOL:+ break;+ default:+ ret_val = JS_ToPropertyKey(ctx, sp[-1]);+ if (JS_IsException(ret_val))+ goto exception;+ JS_FreeValue(ctx, sp[-1]);+ sp[-1] = ret_val;+ break;+ }+ BREAK;+#if 0+ CASE(OP_to_string):+ if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_STRING) {+ ret_val = JS_ToString(ctx, sp[-1]);+ if (JS_IsException(ret_val))+ goto exception;+ JS_FreeValue(ctx, sp[-1]);+ sp[-1] = ret_val;+ }+ BREAK;+#endif+ CASE(OP_with_get_var):+ CASE(OP_with_put_var):+ CASE(OP_with_delete_var):+ CASE(OP_with_make_ref):+ CASE(OP_with_get_ref):+ CASE(OP_with_get_ref_undef):+ {+ JSAtom atom;+ int32_t diff;+ JSValue obj, val;+ int ret, is_with;+ atom = get_u32(pc);+ diff = get_u32(pc + 4);+ is_with = pc[8];+ pc += 9;++ obj = sp[-1];+ ret = JS_HasProperty(ctx, obj, atom);+ if (unlikely(ret < 0))+ goto exception;+ if (ret) {+ if (is_with) {+ ret = js_has_unscopable(ctx, obj, atom);+ if (unlikely(ret < 0))+ goto exception;+ if (ret)+ goto no_with;+ }+ switch (opcode) {+ case OP_with_get_var:+ val = JS_GetProperty(ctx, obj, atom);+ if (unlikely(JS_IsException(val)))+ goto exception;+ set_value(ctx, &sp[-1], val);+ break;+ case OP_with_put_var:+ /* XXX: check if strict mode */+ ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2],+ JS_PROP_THROW_STRICT);+ JS_FreeValue(ctx, sp[-1]);+ sp -= 2;+ if (unlikely(ret < 0))+ goto exception;+ break;+ case OP_with_delete_var:+ ret = JS_DeleteProperty(ctx, obj, atom, 0);+ if (unlikely(ret < 0))+ goto exception;+ JS_FreeValue(ctx, sp[-1]);+ sp[-1] = JS_NewBool(ctx, ret);+ break;+ case OP_with_make_ref:+ /* produce a pair object/propname on the stack */+ *sp++ = JS_AtomToValue(ctx, atom);+ break;+ case OP_with_get_ref:+ /* produce a pair object/method on the stack */+ val = JS_GetProperty(ctx, obj, atom);+ if (unlikely(JS_IsException(val)))+ goto exception;+ *sp++ = val;+ break;+ case OP_with_get_ref_undef:+ /* produce a pair undefined/function on the stack */+ val = JS_GetProperty(ctx, obj, atom);+ if (unlikely(JS_IsException(val)))+ goto exception;+ JS_FreeValue(ctx, sp[-1]);+ sp[-1] = JS_UNDEFINED;+ *sp++ = val;+ break;+ }+ pc += diff - 5;+ } else {+ no_with:+ /* if not jumping, drop the object argument */+ JS_FreeValue(ctx, sp[-1]);+ sp--;+ }+ }+ BREAK;++ CASE(OP_await):+ ret_val = JS_NewInt32(ctx, FUNC_RET_AWAIT);+ goto done_generator;+ CASE(OP_yield):+ ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD);+ goto done_generator;+ CASE(OP_yield_star):+ CASE(OP_async_yield_star):+ ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD_STAR);+ goto done_generator;+ CASE(OP_return_async):+ CASE(OP_initial_yield):+ ret_val = JS_UNDEFINED;+ goto done_generator;++ CASE(OP_nop):+ BREAK;+ CASE(OP_is_undefined_or_null):+ if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED ||+ JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) {+ goto set_true;+ } else {+ goto free_and_set_false;+ }+#if SHORT_OPCODES+ CASE(OP_is_undefined):+ if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED) {+ goto set_true;+ } else {+ goto free_and_set_false;+ }+ CASE(OP_is_null):+ if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) {+ goto set_true;+ } else {+ goto free_and_set_false;+ }+ /* XXX: could merge to a single opcode */+ CASE(OP_typeof_is_undefined):+ /* different from OP_is_undefined because of isHTMLDDA */+ if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_undefined) {+ goto free_and_set_true;+ } else {+ goto free_and_set_false;+ }+ CASE(OP_typeof_is_function):+ if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) {+ goto free_and_set_true;+ } else {+ goto free_and_set_false;+ }+ free_and_set_true:+ JS_FreeValue(ctx, sp[-1]);+#endif+ set_true:+ sp[-1] = JS_TRUE;+ BREAK;+ free_and_set_false:+ JS_FreeValue(ctx, sp[-1]);+ sp[-1] = JS_FALSE;+ BREAK;+ CASE(OP_invalid):+ DEFAULT:+ JS_ThrowInternalError(ctx, "invalid opcode: pc=%u opcode=0x%02x",+ (int)(pc - b->byte_code_buf - 1), opcode);+ goto exception;+ }+ }+ exception:+ if (is_backtrace_needed(ctx, rt->current_exception)) {+ /* add the backtrace information now (it is not done+ before if the exception happens in a bytecode+ operation */+ sf->cur_pc = pc;+ build_backtrace(ctx, rt->current_exception, NULL, 0, 0);+ }+ if (!JS_IsUncatchableError(ctx, rt->current_exception)) {+ while (sp > stack_buf) {+ JSValue val = *--sp;+ JS_FreeValue(ctx, val);+ if (JS_VALUE_GET_TAG(val) == JS_TAG_CATCH_OFFSET) {+ int pos = JS_VALUE_GET_INT(val);+ if (pos == 0) {+ /* enumerator: close it with a throw */+ JS_FreeValue(ctx, sp[-1]); /* drop the next method */+ sp--;+ JS_IteratorClose(ctx, sp[-1], TRUE);+ } else {+ *sp++ = rt->current_exception;+ rt->current_exception = JS_NULL;+ pc = b->byte_code_buf + pos;+ goto restart;+ }+ }+ }+ }+ ret_val = JS_EXCEPTION;+ /* the local variables are freed by the caller in the generator+ case. Hence the label 'done' should never be reached in a+ generator function. */+ if (b->func_kind != JS_FUNC_NORMAL) {+ done_generator:+ sf->cur_pc = pc;+ sf->cur_sp = sp;+ } else {+ done:+ if (unlikely(!list_empty(&sf->var_ref_list))) {+ /* variable references reference the stack: must close them */+ close_var_refs(rt, sf);+ }+ /* free the local variables and stack */+ for(pval = local_buf; pval < sp; pval++) {+ JS_FreeValue(ctx, *pval);+ }+ }+ rt->current_stack_frame = sf->prev_frame;+ return ret_val;+}++JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj,+ int argc, JSValueConst *argv)+{+ return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED,+ argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV);+}++static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj,+ int argc, JSValueConst *argv)+{+ JSValue res = JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED,+ argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV);+ JS_FreeValue(ctx, func_obj);+ return res;+}++/* warning: the refcount of the context is not incremented. Return+ NULL in case of exception (case of revoked proxy only) */+static JSContext *JS_GetFunctionRealm(JSContext *ctx, JSValueConst func_obj)+{+ JSObject *p;+ JSContext *realm;+ + if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)+ return ctx;+ p = JS_VALUE_GET_OBJ(func_obj);+ switch(p->class_id) {+ case JS_CLASS_C_FUNCTION:+ realm = p->u.cfunc.realm;+ break;+ case JS_CLASS_BYTECODE_FUNCTION:+ case JS_CLASS_GENERATOR_FUNCTION:+ case JS_CLASS_ASYNC_FUNCTION:+ case JS_CLASS_ASYNC_GENERATOR_FUNCTION:+ {+ JSFunctionBytecode *b;+ b = p->u.func.function_bytecode;+ realm = b->realm;+ }+ break;+ case JS_CLASS_PROXY:+ {+ JSProxyData *s = p->u.opaque;+ if (!s)+ return ctx;+ if (s->is_revoked) {+ JS_ThrowTypeErrorRevokedProxy(ctx);+ return NULL;+ } else {+ realm = JS_GetFunctionRealm(ctx, s->target);+ }+ }+ break;+ case JS_CLASS_BOUND_FUNCTION:+ {+ JSBoundFunction *bf = p->u.bound_function;+ realm = JS_GetFunctionRealm(ctx, bf->func_obj);+ }+ break;+ default:+ realm = ctx;+ break;+ }+ return realm;+}++static JSValue js_create_from_ctor(JSContext *ctx, JSValueConst ctor,+ int class_id)+{+ JSValue proto, obj;+ JSContext *realm;+ + if (JS_IsUndefined(ctor)) {+ proto = JS_DupValue(ctx, ctx->class_proto[class_id]);+ } else {+ proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype);+ if (JS_IsException(proto))+ return proto;+ if (!JS_IsObject(proto)) {+ JS_FreeValue(ctx, proto);+ realm = JS_GetFunctionRealm(ctx, ctor);+ if (!realm)+ return JS_EXCEPTION;+ proto = JS_DupValue(ctx, realm->class_proto[class_id]);+ }+ }+ obj = JS_NewObjectProtoClass(ctx, proto, class_id);+ JS_FreeValue(ctx, proto);+ return obj;+}++/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */+static JSValue JS_CallConstructorInternal(JSContext *ctx,+ JSValueConst func_obj,+ JSValueConst new_target,+ int argc, JSValue *argv, int flags)+{+ JSObject *p;+ JSFunctionBytecode *b;++ if (js_poll_interrupts(ctx))+ return JS_EXCEPTION;+ flags |= JS_CALL_FLAG_CONSTRUCTOR;+ if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT))+ goto not_a_function;+ p = JS_VALUE_GET_OBJ(func_obj);+ if (unlikely(!p->is_constructor))+ return JS_ThrowTypeError(ctx, "not a constructor");+ if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) {+ JSClassCall *call_func;+ call_func = ctx->rt->class_array[p->class_id].call;+ if (!call_func) {+ not_a_function:+ return JS_ThrowTypeError(ctx, "not a function");+ }+ return call_func(ctx, func_obj, new_target, argc,+ (JSValueConst *)argv, flags);+ }++ b = p->u.func.function_bytecode;+ if (b->is_derived_class_constructor) {+ return JS_CallInternal(ctx, func_obj, JS_UNDEFINED, new_target, argc, argv, flags);+ } else {+ JSValue obj, ret;+ /* legacy constructor behavior */+ obj = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);+ if (JS_IsException(obj))+ return JS_EXCEPTION;+ ret = JS_CallInternal(ctx, func_obj, obj, new_target, argc, argv, flags);+ if (JS_VALUE_GET_TAG(ret) == JS_TAG_OBJECT ||+ JS_IsException(ret)) {+ JS_FreeValue(ctx, obj);+ return ret;+ } else {+ JS_FreeValue(ctx, ret);+ return obj;+ }+ }+}++JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj,+ JSValueConst new_target,+ int argc, JSValueConst *argv)+{+ return JS_CallConstructorInternal(ctx, func_obj, new_target,+ argc, (JSValue *)argv,+ JS_CALL_FLAG_COPY_ARGV);+}++JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj,+ int argc, JSValueConst *argv)+{+ return JS_CallConstructorInternal(ctx, func_obj, func_obj,+ argc, (JSValue *)argv,+ JS_CALL_FLAG_COPY_ARGV);+}++JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom,+ int argc, JSValueConst *argv)+{+ JSValue func_obj;+ func_obj = JS_GetProperty(ctx, this_val, atom);+ if (JS_IsException(func_obj))+ return func_obj;+ return JS_CallFree(ctx, func_obj, this_val, argc, argv);+}++static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,+ int argc, JSValueConst *argv)+{+ JSValue res = JS_Invoke(ctx, this_val, atom, argc, argv);+ JS_FreeValue(ctx, this_val);+ return res;+}++/* JSAsyncFunctionState (used by generator and async functions) */+static __exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s,+ JSValueConst func_obj, JSValueConst this_obj,+ int argc, JSValueConst *argv)+{+ JSObject *p;+ JSFunctionBytecode *b;+ JSStackFrame *sf;+ int local_count, i, arg_buf_len, n;++ sf = &s->frame;+ init_list_head(&sf->var_ref_list);+ p = JS_VALUE_GET_OBJ(func_obj);+ b = p->u.func.function_bytecode;+ sf->js_mode = b->js_mode;+ sf->cur_pc = b->byte_code_buf;+ arg_buf_len = max_int(b->arg_count, argc);+ local_count = arg_buf_len + b->var_count + b->stack_size;+ sf->arg_buf = js_malloc(ctx, sizeof(JSValue) * max_int(local_count, 1));+ if (!sf->arg_buf)+ return -1;+ sf->cur_func = JS_DupValue(ctx, func_obj);+ s->this_val = JS_DupValue(ctx, this_obj);+ s->argc = argc;+ sf->arg_count = arg_buf_len;+ sf->var_buf = sf->arg_buf + arg_buf_len;+ sf->cur_sp = sf->var_buf + b->var_count;+ for(i = 0; i < argc; i++)+ sf->arg_buf[i] = JS_DupValue(ctx, argv[i]);+ n = arg_buf_len + b->var_count;+ for(i = argc; i < n; i++)+ sf->arg_buf[i] = JS_UNDEFINED;+ return 0;+}++static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s,+ JS_MarkFunc *mark_func)+{+ JSStackFrame *sf;+ JSValue *sp;++ sf = &s->frame;+ JS_MarkValue(rt, sf->cur_func, mark_func);+ JS_MarkValue(rt, s->this_val, mark_func);+ if (sf->cur_sp) {+ /* if the function is running, cur_sp is not known so we+ cannot mark the stack. Marking the variables is not needed+ because a running function cannot be part of a removable+ cycle */+ for(sp = sf->arg_buf; sp < sf->cur_sp; sp++)+ JS_MarkValue(rt, *sp, mark_func);+ }+}++static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)+{+ JSStackFrame *sf;+ JSValue *sp;++ sf = &s->frame;++ /* close the closure variables. */+ close_var_refs(rt, sf);+ + if (sf->arg_buf) {+ /* cannot free the function if it is running */+ assert(sf->cur_sp != NULL);+ for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) {+ JS_FreeValueRT(rt, *sp);+ }+ js_free_rt(rt, sf->arg_buf);+ }+ JS_FreeValueRT(rt, sf->cur_func);+ JS_FreeValueRT(rt, s->this_val);+}++static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s)+{+ JSValue func_obj;++ if (js_check_stack_overflow(ctx->rt, 0))+ return JS_ThrowStackOverflow(ctx);++ /* the tag does not matter provided it is not an object */+ func_obj = JS_MKPTR(JS_TAG_INT, s);+ return JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED,+ s->argc, s->frame.arg_buf, JS_CALL_FLAG_GENERATOR);+}+++/* Generators */++typedef enum JSGeneratorStateEnum {+ JS_GENERATOR_STATE_SUSPENDED_START,+ JS_GENERATOR_STATE_SUSPENDED_YIELD,+ JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR,+ JS_GENERATOR_STATE_EXECUTING,+ JS_GENERATOR_STATE_COMPLETED,+} JSGeneratorStateEnum;++typedef struct JSGeneratorData {+ JSGeneratorStateEnum state;+ JSAsyncFunctionState func_state;+} JSGeneratorData;++static void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s)+{+ if (s->state == JS_GENERATOR_STATE_COMPLETED)+ return;+ async_func_free(rt, &s->func_state);+ s->state = JS_GENERATOR_STATE_COMPLETED;+}++static void js_generator_finalizer(JSRuntime *rt, JSValue obj)+{+ JSGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_GENERATOR);++ if (s) {+ free_generator_stack_rt(rt, s);+ js_free_rt(rt, s);+ }+}++static void free_generator_stack(JSContext *ctx, JSGeneratorData *s)+{+ free_generator_stack_rt(ctx->rt, s);+}++static void js_generator_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func)+{+ JSObject *p = JS_VALUE_GET_OBJ(val);+ JSGeneratorData *s = p->u.generator_data;++ if (!s || s->state == JS_GENERATOR_STATE_COMPLETED)+ return;+ async_func_mark(rt, &s->func_state, mark_func);+}++/* XXX: use enum */+#define GEN_MAGIC_NEXT 0+#define GEN_MAGIC_RETURN 1+#define GEN_MAGIC_THROW 2++static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv,+ BOOL *pdone, int magic)+{+ JSGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_GENERATOR);+ JSStackFrame *sf;+ JSValue ret, func_ret;++ *pdone = TRUE;+ if (!s)+ return JS_ThrowTypeError(ctx, "not a generator");+ sf = &s->func_state.frame;+ switch(s->state) {+ default:+ case JS_GENERATOR_STATE_SUSPENDED_START:+ if (magic == GEN_MAGIC_NEXT) {+ goto exec_no_arg;+ } else {+ free_generator_stack(ctx, s);+ goto done;+ }+ break;+ case JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR:+ case JS_GENERATOR_STATE_SUSPENDED_YIELD:+ /* cur_sp[-1] was set to JS_UNDEFINED in the previous call */+ ret = JS_DupValue(ctx, argv[0]);+ if (magic == GEN_MAGIC_THROW &&+ s->state == JS_GENERATOR_STATE_SUSPENDED_YIELD) {+ JS_Throw(ctx, ret);+ s->func_state.throw_flag = TRUE;+ } else {+ sf->cur_sp[-1] = ret;+ sf->cur_sp[0] = JS_NewInt32(ctx, magic);+ sf->cur_sp++;+ exec_no_arg:+ s->func_state.throw_flag = FALSE;+ }+ s->state = JS_GENERATOR_STATE_EXECUTING;+ func_ret = async_func_resume(ctx, &s->func_state);+ s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD;+ if (JS_IsException(func_ret)) {+ /* finalize the execution in case of exception */+ free_generator_stack(ctx, s);+ return func_ret;+ }+ if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) {+ /* get the returned yield value at the top of the stack */+ ret = sf->cur_sp[-1];+ sf->cur_sp[-1] = JS_UNDEFINED;+ if (JS_VALUE_GET_INT(func_ret) == FUNC_RET_YIELD_STAR) {+ s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR;+ /* return (value, done) object */+ *pdone = 2;+ } else {+ *pdone = FALSE;+ }+ } else {+ /* end of iterator */+ ret = sf->cur_sp[-1];+ sf->cur_sp[-1] = JS_UNDEFINED;+ JS_FreeValue(ctx, func_ret);+ free_generator_stack(ctx, s);+ }+ break;+ case JS_GENERATOR_STATE_COMPLETED:+ done:+ /* execution is finished */+ switch(magic) {+ default:+ case GEN_MAGIC_NEXT:+ ret = JS_UNDEFINED;+ break;+ case GEN_MAGIC_RETURN:+ ret = JS_DupValue(ctx, argv[0]);+ break;+ case GEN_MAGIC_THROW:+ ret = JS_Throw(ctx, JS_DupValue(ctx, argv[0]));+ break;+ }+ break;+ case JS_GENERATOR_STATE_EXECUTING:+ ret = JS_ThrowTypeError(ctx, "cannot invoke a running generator");+ break;+ }+ return ret;+}++static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,+ JSValueConst this_obj,+ int argc, JSValueConst *argv,+ int flags)+{+ JSValue obj, func_ret;+ JSGeneratorData *s;++ s = js_mallocz(ctx, sizeof(*s));+ if (!s)+ return JS_EXCEPTION;+ s->state = JS_GENERATOR_STATE_SUSPENDED_START;+ if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {+ s->state = JS_GENERATOR_STATE_COMPLETED;+ goto fail;+ }++ /* execute the function up to 'OP_initial_yield' */+ func_ret = async_func_resume(ctx, &s->func_state);+ if (JS_IsException(func_ret))+ goto fail;+ JS_FreeValue(ctx, func_ret);++ obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_GENERATOR);+ if (JS_IsException(obj))+ goto fail;+ JS_SetOpaque(obj, s);+ return obj;+ fail:+ free_generator_stack_rt(ctx->rt, s);+ js_free(ctx, s);+ return JS_EXCEPTION;+}++/* AsyncFunction */++static void js_async_function_terminate(JSRuntime *rt, JSAsyncFunctionData *s)+{+ if (s->is_active) {+ async_func_free(rt, &s->func_state);+ s->is_active = FALSE;+ }+}++static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s)+{+ js_async_function_terminate(rt, s);+ JS_FreeValueRT(rt, s->resolving_funcs[0]);+ JS_FreeValueRT(rt, s->resolving_funcs[1]);+ remove_gc_object(&s->header);+ js_free_rt(rt, s);+}++static void js_async_function_free(JSRuntime *rt, JSAsyncFunctionData *s)+{+ if (--s->header.ref_count == 0) {+ js_async_function_free0(rt, s);+ }+}++static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val)+{+ JSObject *p = JS_VALUE_GET_OBJ(val);+ JSAsyncFunctionData *s = p->u.async_function_data;+ if (s) {+ js_async_function_free(rt, s);+ }+}++static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func)+{+ JSObject *p = JS_VALUE_GET_OBJ(val);+ JSAsyncFunctionData *s = p->u.async_function_data;+ if (s) {+ mark_func(rt, &s->header);+ }+}++static int js_async_function_resolve_create(JSContext *ctx,+ JSAsyncFunctionData *s,+ JSValue *resolving_funcs)+{+ int i;+ JSObject *p;++ for(i = 0; i < 2; i++) {+ resolving_funcs[i] =+ JS_NewObjectProtoClass(ctx, ctx->function_proto,+ JS_CLASS_ASYNC_FUNCTION_RESOLVE + i);+ if (JS_IsException(resolving_funcs[i])) {+ if (i == 1)+ JS_FreeValue(ctx, resolving_funcs[0]);+ return -1;+ }+ p = JS_VALUE_GET_OBJ(resolving_funcs[i]);+ s->header.ref_count++;+ p->u.async_function_data = s;+ }+ return 0;+}++static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s)+{+ JSValue func_ret, ret2;++ func_ret = async_func_resume(ctx, &s->func_state);+ if (JS_IsException(func_ret)) {+ JSValue error;+ fail:+ error = JS_GetException(ctx);+ ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED,+ 1, (JSValueConst *)&error);+ JS_FreeValue(ctx, error);+ js_async_function_terminate(ctx->rt, s);+ JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */+ } else {+ JSValue value;+ value = s->func_state.frame.cur_sp[-1];+ s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;+ if (JS_IsUndefined(func_ret)) {+ /* function returned */+ ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED,+ 1, (JSValueConst *)&value);+ JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */+ JS_FreeValue(ctx, value);+ js_async_function_terminate(ctx->rt, s);+ } else {+ JSValue promise, resolving_funcs[2], resolving_funcs1[2];+ int i, res;++ /* await */+ JS_FreeValue(ctx, func_ret); /* not used */+ promise = js_promise_resolve(ctx, ctx->promise_ctor,+ 1, (JSValueConst *)&value, 0);+ JS_FreeValue(ctx, value);+ if (JS_IsException(promise))+ goto fail;+ if (js_async_function_resolve_create(ctx, s, resolving_funcs)) {+ JS_FreeValue(ctx, promise);+ goto fail;+ }++ /* Note: no need to create 'thrownawayCapability' as in+ the spec */+ for(i = 0; i < 2; i++)+ resolving_funcs1[i] = JS_UNDEFINED;+ res = perform_promise_then(ctx, promise,+ (JSValueConst *)resolving_funcs,+ (JSValueConst *)resolving_funcs1);+ JS_FreeValue(ctx, promise);+ for(i = 0; i < 2; i++)+ JS_FreeValue(ctx, resolving_funcs[i]);+ if (res)+ goto fail;+ }+ }+}++static JSValue js_async_function_resolve_call(JSContext *ctx,+ JSValueConst func_obj,+ JSValueConst this_obj,+ int argc, JSValueConst *argv,+ int flags)+{+ JSObject *p = JS_VALUE_GET_OBJ(func_obj);+ JSAsyncFunctionData *s = p->u.async_function_data;+ BOOL is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE;+ JSValueConst arg;++ if (argc > 0)+ arg = argv[0];+ else+ arg = JS_UNDEFINED;+ s->func_state.throw_flag = is_reject;+ if (is_reject) {+ JS_Throw(ctx, JS_DupValue(ctx, arg));+ } else {+ /* return value of await */+ s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg);+ }+ js_async_function_resume(ctx, s);+ return JS_UNDEFINED;+}++static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj,+ JSValueConst this_obj,+ int argc, JSValueConst *argv, int flags)+{+ JSValue promise;+ JSAsyncFunctionData *s;++ s = js_mallocz(ctx, sizeof(*s));+ if (!s)+ return JS_EXCEPTION;+ s->header.ref_count = 1;+ add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION);+ s->is_active = FALSE;+ s->resolving_funcs[0] = JS_UNDEFINED;+ s->resolving_funcs[1] = JS_UNDEFINED;++ promise = JS_NewPromiseCapability(ctx, s->resolving_funcs);+ if (JS_IsException(promise))+ goto fail;++ if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {+ fail:+ JS_FreeValue(ctx, promise);+ js_async_function_free(ctx->rt, s);+ return JS_EXCEPTION;+ }+ s->is_active = TRUE;++ js_async_function_resume(ctx, s);++ js_async_function_free(ctx->rt, s);++ return promise;+}++/* AsyncGenerator */++typedef enum JSAsyncGeneratorStateEnum {+ JS_ASYNC_GENERATOR_STATE_SUSPENDED_START,+ JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD,+ JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR,+ JS_ASYNC_GENERATOR_STATE_EXECUTING,+ JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN,+ JS_ASYNC_GENERATOR_STATE_COMPLETED,+} JSAsyncGeneratorStateEnum;++typedef struct JSAsyncGeneratorRequest {+ struct list_head link;+ /* completion */+ int completion_type; /* GEN_MAGIC_x */+ JSValue result;+ /* promise capability */+ JSValue promise;+ JSValue resolving_funcs[2];+} JSAsyncGeneratorRequest;++typedef struct JSAsyncGeneratorData {+ JSObject *generator; /* back pointer to the object (const) */+ JSAsyncGeneratorStateEnum state;+ JSAsyncFunctionState func_state;+ struct list_head queue; /* list of JSAsyncGeneratorRequest.link */+} JSAsyncGeneratorData;++static void js_async_generator_free(JSRuntime *rt,+ JSAsyncGeneratorData *s)+{+ struct list_head *el, *el1;+ JSAsyncGeneratorRequest *req;++ list_for_each_safe(el, el1, &s->queue) {+ req = list_entry(el, JSAsyncGeneratorRequest, link);+ JS_FreeValueRT(rt, req->result);+ JS_FreeValueRT(rt, req->promise);+ JS_FreeValueRT(rt, req->resolving_funcs[0]);+ JS_FreeValueRT(rt, req->resolving_funcs[1]);+ js_free_rt(rt, req);+ }+ if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED &&+ s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) {+ async_func_free(rt, &s->func_state);+ }+ js_free_rt(rt, s);+}++static void js_async_generator_finalizer(JSRuntime *rt, JSValue obj)+{+ JSAsyncGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_ASYNC_GENERATOR);++ if (s) {+ js_async_generator_free(rt, s);+ }+}++static void js_async_generator_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func)+{+ JSAsyncGeneratorData *s = JS_GetOpaque(val, JS_CLASS_ASYNC_GENERATOR);+ struct list_head *el;+ JSAsyncGeneratorRequest *req;+ if (s) {+ list_for_each(el, &s->queue) {+ req = list_entry(el, JSAsyncGeneratorRequest, link);+ JS_MarkValue(rt, req->result, mark_func);+ JS_MarkValue(rt, req->promise, mark_func);+ JS_MarkValue(rt, req->resolving_funcs[0], mark_func);+ JS_MarkValue(rt, req->resolving_funcs[1], mark_func);+ }+ if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED &&+ s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) {+ async_func_mark(rt, &s->func_state, mark_func);+ }+ }+}++static JSValue js_async_generator_resolve_function(JSContext *ctx,+ JSValueConst this_obj,+ int argc, JSValueConst *argv,+ int magic, JSValue *func_data);++static int js_async_generator_resolve_function_create(JSContext *ctx,+ JSValueConst generator,+ JSValue *resolving_funcs,+ BOOL is_resume_next)+{+ int i;+ JSValue func;++ for(i = 0; i < 2; i++) {+ func = JS_NewCFunctionData(ctx, js_async_generator_resolve_function, 1,+ i + is_resume_next * 2, 1, &generator);+ if (JS_IsException(func)) {+ if (i == 1)+ JS_FreeValue(ctx, resolving_funcs[0]);+ return -1;+ }+ resolving_funcs[i] = func;+ }+ return 0;+}++static int js_async_generator_await(JSContext *ctx,+ JSAsyncGeneratorData *s,+ JSValueConst value)+{+ JSValue promise, resolving_funcs[2], resolving_funcs1[2];+ int i, res;++ promise = js_promise_resolve(ctx, ctx->promise_ctor,+ 1, &value, 0);+ if (JS_IsException(promise))+ goto fail;++ if (js_async_generator_resolve_function_create(ctx, JS_MKPTR(JS_TAG_OBJECT, s->generator),+ resolving_funcs, FALSE)) {+ JS_FreeValue(ctx, promise);+ goto fail;+ }++ /* Note: no need to create 'thrownawayCapability' as in+ the spec */+ for(i = 0; i < 2; i++)+ resolving_funcs1[i] = JS_UNDEFINED;+ res = perform_promise_then(ctx, promise,+ (JSValueConst *)resolving_funcs,+ (JSValueConst *)resolving_funcs1);+ JS_FreeValue(ctx, promise);+ for(i = 0; i < 2; i++)+ JS_FreeValue(ctx, resolving_funcs[i]);+ if (res)+ goto fail;+ return 0;+ fail:+ return -1;+}++static void js_async_generator_resolve_or_reject(JSContext *ctx,+ JSAsyncGeneratorData *s,+ JSValueConst result,+ int is_reject)+{+ JSAsyncGeneratorRequest *next;+ JSValue ret;++ next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link);+ list_del(&next->link);+ ret = JS_Call(ctx, next->resolving_funcs[is_reject], JS_UNDEFINED, 1,+ &result);+ JS_FreeValue(ctx, ret);+ JS_FreeValue(ctx, next->result);+ JS_FreeValue(ctx, next->promise);+ JS_FreeValue(ctx, next->resolving_funcs[0]);+ JS_FreeValue(ctx, next->resolving_funcs[1]);+ js_free(ctx, next);+}++static void js_async_generator_resolve(JSContext *ctx,+ JSAsyncGeneratorData *s,+ JSValueConst value,+ BOOL done)+{+ JSValue result;+ result = js_create_iterator_result(ctx, JS_DupValue(ctx, value), done);+ /* XXX: better exception handling ? */+ js_async_generator_resolve_or_reject(ctx, s, result, 0);+ JS_FreeValue(ctx, result);+ }++static void js_async_generator_reject(JSContext *ctx,+ JSAsyncGeneratorData *s,+ JSValueConst exception)+{+ js_async_generator_resolve_or_reject(ctx, s, exception, 1);+}++static void js_async_generator_complete(JSContext *ctx,+ JSAsyncGeneratorData *s)+{+ if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) {+ s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;+ async_func_free(ctx->rt, &s->func_state);+ }+}++static int js_async_generator_completed_return(JSContext *ctx,+ JSAsyncGeneratorData *s,+ JSValueConst value)+{+ JSValue promise, resolving_funcs[2], resolving_funcs1[2];+ int res;++ promise = js_promise_resolve(ctx, ctx->promise_ctor,+ 1, (JSValueConst *)&value, 0);+ if (JS_IsException(promise))+ return -1;+ if (js_async_generator_resolve_function_create(ctx,+ JS_MKPTR(JS_TAG_OBJECT, s->generator),+ resolving_funcs1,+ TRUE)) {+ JS_FreeValue(ctx, promise);+ return -1;+ }+ resolving_funcs[0] = JS_UNDEFINED;+ resolving_funcs[1] = JS_UNDEFINED;+ res = perform_promise_then(ctx, promise,+ (JSValueConst *)resolving_funcs1,+ (JSValueConst *)resolving_funcs);+ JS_FreeValue(ctx, resolving_funcs1[0]);+ JS_FreeValue(ctx, resolving_funcs1[1]);+ JS_FreeValue(ctx, promise);+ return res;+}++static void js_async_generator_resume_next(JSContext *ctx,+ JSAsyncGeneratorData *s)+{+ JSAsyncGeneratorRequest *next;+ JSValue func_ret, value;++ for(;;) {+ if (list_empty(&s->queue))+ break;+ next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link);+ switch(s->state) {+ case JS_ASYNC_GENERATOR_STATE_EXECUTING:+ /* only happens when restarting execution after await() */+ goto resume_exec;+ case JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN:+ goto done;+ case JS_ASYNC_GENERATOR_STATE_SUSPENDED_START:+ if (next->completion_type == GEN_MAGIC_NEXT) {+ goto exec_no_arg;+ } else {+ js_async_generator_complete(ctx, s);+ }+ break;+ case JS_ASYNC_GENERATOR_STATE_COMPLETED:+ if (next->completion_type == GEN_MAGIC_NEXT) {+ js_async_generator_resolve(ctx, s, JS_UNDEFINED, TRUE);+ } else if (next->completion_type == GEN_MAGIC_RETURN) {+ s->state = JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN;+ js_async_generator_completed_return(ctx, s, next->result);+ goto done;+ } else {+ js_async_generator_reject(ctx, s, next->result);+ }+ goto done;+ case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD:+ case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR:+ value = JS_DupValue(ctx, next->result);+ if (next->completion_type == GEN_MAGIC_THROW &&+ s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) {+ JS_Throw(ctx, value);+ s->func_state.throw_flag = TRUE;+ } else {+ /* 'yield' returns a value. 'yield *' also returns a value+ in case the 'throw' method is called */+ s->func_state.frame.cur_sp[-1] = value;+ s->func_state.frame.cur_sp[0] =+ JS_NewInt32(ctx, next->completion_type);+ s->func_state.frame.cur_sp++;+ exec_no_arg:+ s->func_state.throw_flag = FALSE;+ }+ s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING;+ resume_exec:+ func_ret = async_func_resume(ctx, &s->func_state);+ if (JS_IsException(func_ret)) {+ value = JS_GetException(ctx);+ js_async_generator_complete(ctx, s);+ js_async_generator_reject(ctx, s, value);+ JS_FreeValue(ctx, value);+ } else if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) {+ int func_ret_code;+ value = s->func_state.frame.cur_sp[-1];+ s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;+ func_ret_code = JS_VALUE_GET_INT(func_ret);+ switch(func_ret_code) {+ case FUNC_RET_YIELD:+ case FUNC_RET_YIELD_STAR:+ if (func_ret_code == FUNC_RET_YIELD_STAR)+ s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR;+ else+ s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD;+ js_async_generator_resolve(ctx, s, value, FALSE);+ JS_FreeValue(ctx, value);+ break;+ case FUNC_RET_AWAIT:+ js_async_generator_await(ctx, s, value);+ JS_FreeValue(ctx, value);+ goto done;+ default:+ abort();+ }+ } else {+ assert(JS_IsUndefined(func_ret));+ /* end of function */+ value = s->func_state.frame.cur_sp[-1];+ s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;+ js_async_generator_complete(ctx, s);+ js_async_generator_resolve(ctx, s, value, TRUE);+ JS_FreeValue(ctx, value);+ }+ break;+ default:+ abort();+ }+ }+ done: ;+}++static JSValue js_async_generator_resolve_function(JSContext *ctx,+ JSValueConst this_obj,+ int argc, JSValueConst *argv,+ int magic, JSValue *func_data)+{+ BOOL is_reject = magic & 1;+ JSAsyncGeneratorData *s = JS_GetOpaque(func_data[0], JS_CLASS_ASYNC_GENERATOR);+ JSValueConst arg = argv[0];++ /* XXX: what if s == NULL */++ if (magic >= 2) {+ /* resume next case in AWAITING_RETURN state */+ assert(s->state == JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN ||+ s->state == JS_ASYNC_GENERATOR_STATE_COMPLETED);+ s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;+ if (is_reject) {+ js_async_generator_reject(ctx, s, arg);+ } else {+ js_async_generator_resolve(ctx, s, arg, TRUE);+ }+ } else {+ /* restart function execution after await() */+ assert(s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING);+ s->func_state.throw_flag = is_reject;+ if (is_reject) {+ JS_Throw(ctx, JS_DupValue(ctx, arg));+ } else {+ /* return value of await */+ s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg);+ }+ js_async_generator_resume_next(ctx, s);+ }+ return JS_UNDEFINED;+}++/* magic = GEN_MAGIC_x */+static JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv,+ int magic)+{+ JSAsyncGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_GENERATOR);+ JSValue promise, resolving_funcs[2];+ JSAsyncGeneratorRequest *req;++ promise = JS_NewPromiseCapability(ctx, resolving_funcs);+ if (JS_IsException(promise))+ return JS_EXCEPTION;+ if (!s) {+ JSValue err, res2;+ JS_ThrowTypeError(ctx, "not an AsyncGenerator object");+ err = JS_GetException(ctx);+ res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,+ 1, (JSValueConst *)&err);+ JS_FreeValue(ctx, err);+ JS_FreeValue(ctx, res2);+ JS_FreeValue(ctx, resolving_funcs[0]);+ JS_FreeValue(ctx, resolving_funcs[1]);+ return promise;+ }+ req = js_mallocz(ctx, sizeof(*req));+ if (!req)+ goto fail;+ req->completion_type = magic;+ req->result = JS_DupValue(ctx, argv[0]);+ req->promise = JS_DupValue(ctx, promise);+ req->resolving_funcs[0] = resolving_funcs[0];+ req->resolving_funcs[1] = resolving_funcs[1];+ list_add_tail(&req->link, &s->queue);+ if (s->state != JS_ASYNC_GENERATOR_STATE_EXECUTING) {+ js_async_generator_resume_next(ctx, s);+ }+ return promise;+ fail:+ JS_FreeValue(ctx, resolving_funcs[0]);+ JS_FreeValue(ctx, resolving_funcs[1]);+ JS_FreeValue(ctx, promise);+ return JS_EXCEPTION;+}++static JSValue js_async_generator_function_call(JSContext *ctx, JSValueConst func_obj,+ JSValueConst this_obj,+ int argc, JSValueConst *argv,+ int flags)+{+ JSValue obj, func_ret;+ JSAsyncGeneratorData *s;++ s = js_mallocz(ctx, sizeof(*s));+ if (!s)+ return JS_EXCEPTION;+ s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START;+ init_list_head(&s->queue);+ if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {+ s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;+ goto fail;+ }++ /* execute the function up to 'OP_initial_yield' (no yield nor+ await are possible) */+ func_ret = async_func_resume(ctx, &s->func_state);+ if (JS_IsException(func_ret))+ goto fail;+ JS_FreeValue(ctx, func_ret);++ obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_ASYNC_GENERATOR);+ if (JS_IsException(obj))+ goto fail;+ s->generator = JS_VALUE_GET_OBJ(obj);+ JS_SetOpaque(obj, s);+ return obj;+ fail:+ js_async_generator_free(ctx->rt, s);+ return JS_EXCEPTION;+}++/* JS parser */++enum {+ TOK_NUMBER = -128,+ TOK_STRING,+ TOK_TEMPLATE,+ TOK_IDENT,+ TOK_REGEXP,+ /* warning: order matters (see js_parse_assign_expr) */+ TOK_MUL_ASSIGN,+ TOK_DIV_ASSIGN,+ TOK_MOD_ASSIGN,+ TOK_PLUS_ASSIGN,+ TOK_MINUS_ASSIGN,+ TOK_SHL_ASSIGN,+ TOK_SAR_ASSIGN,+ TOK_SHR_ASSIGN,+ TOK_AND_ASSIGN,+ TOK_XOR_ASSIGN,+ TOK_OR_ASSIGN,+#ifdef CONFIG_BIGNUM+ TOK_MATH_POW_ASSIGN,+#endif+ TOK_POW_ASSIGN,+ TOK_LAND_ASSIGN,+ TOK_LOR_ASSIGN,+ TOK_DOUBLE_QUESTION_MARK_ASSIGN,+ TOK_DEC,+ TOK_INC,+ TOK_SHL,+ TOK_SAR,+ TOK_SHR,+ TOK_LT,+ TOK_LTE,+ TOK_GT,+ TOK_GTE,+ TOK_EQ,+ TOK_STRICT_EQ,+ TOK_NEQ,+ TOK_STRICT_NEQ,+ TOK_LAND,+ TOK_LOR,+#ifdef CONFIG_BIGNUM+ TOK_MATH_POW,+#endif+ TOK_POW,+ TOK_ARROW,+ TOK_ELLIPSIS,+ TOK_DOUBLE_QUESTION_MARK,+ TOK_QUESTION_MARK_DOT,+ TOK_ERROR,+ TOK_PRIVATE_NAME,+ TOK_EOF,+ /* keywords: WARNING: same order as atoms */+ TOK_NULL, /* must be first */+ TOK_FALSE,+ TOK_TRUE,+ TOK_IF,+ TOK_ELSE,+ TOK_RETURN,+ TOK_VAR,+ TOK_THIS,+ TOK_DELETE,+ TOK_VOID,+ TOK_TYPEOF,+ TOK_NEW,+ TOK_IN,+ TOK_INSTANCEOF,+ TOK_DO,+ TOK_WHILE,+ TOK_FOR,+ TOK_BREAK,+ TOK_CONTINUE,+ TOK_SWITCH,+ TOK_CASE,+ TOK_DEFAULT,+ TOK_THROW,+ TOK_TRY,+ TOK_CATCH,+ TOK_FINALLY,+ TOK_FUNCTION,+ TOK_DEBUGGER,+ TOK_WITH,+ /* FutureReservedWord */+ TOK_CLASS,+ TOK_CONST,+ TOK_ENUM,+ TOK_EXPORT,+ TOK_EXTENDS,+ TOK_IMPORT,+ TOK_SUPER,+ /* FutureReservedWords when parsing strict mode code */+ TOK_IMPLEMENTS,+ TOK_INTERFACE,+ TOK_LET,+ TOK_PACKAGE,+ TOK_PRIVATE,+ TOK_PROTECTED,+ TOK_PUBLIC,+ TOK_STATIC,+ TOK_YIELD,+ TOK_AWAIT, /* must be last */+ TOK_OF, /* only used for js_parse_skip_parens_token() */+};++#define TOK_FIRST_KEYWORD TOK_NULL+#define TOK_LAST_KEYWORD TOK_AWAIT++/* unicode code points */+#define CP_NBSP 0x00a0+#define CP_BOM 0xfeff++#define CP_LS 0x2028+#define CP_PS 0x2029++typedef struct BlockEnv {+ struct BlockEnv *prev;+ JSAtom label_name; /* JS_ATOM_NULL if none */+ int label_break; /* -1 if none */+ int label_cont; /* -1 if none */+ int drop_count; /* number of stack elements to drop */+ int label_finally; /* -1 if none */+ int scope_level;+ int has_iterator;+} BlockEnv;++typedef struct JSGlobalVar {+ int cpool_idx; /* if >= 0, index in the constant pool for hoisted+ function defintion*/+ uint8_t force_init : 1; /* force initialization to undefined */+ uint8_t is_lexical : 1; /* global let/const definition */+ uint8_t is_const : 1; /* const definition */+ int scope_level; /* scope of definition */+ JSAtom var_name; /* variable name */+} JSGlobalVar;++typedef struct RelocEntry {+ struct RelocEntry *next;+ uint32_t addr; /* address to patch */+ int size; /* address size: 1, 2 or 4 bytes */+} RelocEntry;++typedef struct JumpSlot {+ int op;+ int size;+ int pos;+ int label;+} JumpSlot;++typedef struct LabelSlot {+ int ref_count;+ int pos; /* phase 1 address, -1 means not resolved yet */+ int pos2; /* phase 2 address, -1 means not resolved yet */+ int addr; /* phase 3 address, -1 means not resolved yet */+ RelocEntry *first_reloc;+} LabelSlot;++typedef struct LineNumberSlot {+ uint32_t pc;+ int line_num;+} LineNumberSlot;++typedef enum JSParseFunctionEnum {+ JS_PARSE_FUNC_STATEMENT,+ JS_PARSE_FUNC_VAR,+ JS_PARSE_FUNC_EXPR,+ JS_PARSE_FUNC_ARROW,+ JS_PARSE_FUNC_GETTER,+ JS_PARSE_FUNC_SETTER,+ JS_PARSE_FUNC_METHOD,+ JS_PARSE_FUNC_CLASS_CONSTRUCTOR,+ JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR,+} JSParseFunctionEnum;++typedef enum JSParseExportEnum {+ JS_PARSE_EXPORT_NONE,+ JS_PARSE_EXPORT_NAMED,+ JS_PARSE_EXPORT_DEFAULT,+} JSParseExportEnum;++typedef struct JSFunctionDef {+ JSContext *ctx;+ struct JSFunctionDef *parent;+ int parent_cpool_idx; /* index in the constant pool of the parent+ or -1 if none */+ int parent_scope_level; /* scope level in parent at point of definition */+ struct list_head child_list; /* list of JSFunctionDef.link */+ struct list_head link;++ BOOL is_eval; /* TRUE if eval code */+ int eval_type; /* only valid if is_eval = TRUE */+ BOOL is_global_var; /* TRUE if variables are not defined locally:+ eval global, eval module or non strict eval */+ BOOL is_func_expr; /* TRUE if function expression */+ BOOL has_home_object; /* TRUE if the home object is available */+ BOOL has_prototype; /* true if a prototype field is necessary */+ BOOL has_simple_parameter_list;+ BOOL has_parameter_expressions; /* if true, an argument scope is created */+ BOOL has_use_strict; /* to reject directive in special cases */+ BOOL has_eval_call; /* true if the function contains a call to eval() */+ BOOL has_arguments_binding; /* true if the 'arguments' binding is+ available in the function */+ BOOL has_this_binding; /* true if the 'this' and new.target binding are+ available in the function */+ BOOL new_target_allowed; /* true if the 'new.target' does not+ throw a syntax error */+ BOOL super_call_allowed; /* true if super() is allowed */+ BOOL super_allowed; /* true if super. or super[] is allowed */+ BOOL arguments_allowed; /* true if the 'arguments' identifier is allowed */+ BOOL is_derived_class_constructor;+ BOOL in_function_body;+ BOOL backtrace_barrier;+ JSFunctionKindEnum func_kind : 8;+ JSParseFunctionEnum func_type : 8;+ uint8_t js_mode; /* bitmap of JS_MODE_x */+ JSAtom func_name; /* JS_ATOM_NULL if no name */++ JSVarDef *vars;+ int var_size; /* allocated size for vars[] */+ int var_count;+ JSVarDef *args;+ int arg_size; /* allocated size for args[] */+ int arg_count; /* number of arguments */+ int defined_arg_count;+ int var_object_idx; /* -1 if none */+ int arg_var_object_idx; /* -1 if none (var object for the argument scope) */+ int arguments_var_idx; /* -1 if none */+ int arguments_arg_idx; /* argument variable definition in argument scope, + -1 if none */+ int func_var_idx; /* variable containing the current function (-1+ if none, only used if is_func_expr is true) */+ int eval_ret_idx; /* variable containing the return value of the eval, -1 if none */+ int this_var_idx; /* variable containg the 'this' value, -1 if none */+ int new_target_var_idx; /* variable containg the 'new.target' value, -1 if none */+ int this_active_func_var_idx; /* variable containg the 'this.active_func' value, -1 if none */+ int home_object_var_idx;+ BOOL need_home_object;+ + int scope_level; /* index into fd->scopes if the current lexical scope */+ int scope_first; /* index into vd->vars of first lexically scoped variable */+ int scope_size; /* allocated size of fd->scopes array */+ int scope_count; /* number of entries used in the fd->scopes array */+ JSVarScope *scopes;+ JSVarScope def_scope_array[4];+ int body_scope; /* scope of the body of the function or eval */++ int global_var_count;+ int global_var_size;+ JSGlobalVar *global_vars;++ DynBuf byte_code;+ int last_opcode_pos; /* -1 if no last opcode */+ int last_opcode_line_num;+ BOOL use_short_opcodes; /* true if short opcodes are used in byte_code */+ + LabelSlot *label_slots;+ int label_size; /* allocated size for label_slots[] */+ int label_count;+ BlockEnv *top_break; /* break/continue label stack */++ /* constant pool (strings, functions, numbers) */+ JSValue *cpool;+ int cpool_count;+ int cpool_size;++ /* list of variables in the closure */+ int closure_var_count;+ int closure_var_size;+ JSClosureVar *closure_var;++ JumpSlot *jump_slots;+ int jump_size;+ int jump_count;++ LineNumberSlot *line_number_slots;+ int line_number_size;+ int line_number_count;+ int line_number_last;+ int line_number_last_pc;++ /* pc2line table */+ JSAtom filename;+ int line_num;+ DynBuf pc2line;++ char *source; /* raw source, utf-8 encoded */+ int source_len;++ JSModuleDef *module; /* != NULL when parsing a module */+} JSFunctionDef;++typedef struct JSToken {+ int val;+ int line_num; /* line number of token start */+ const uint8_t *ptr;+ union {+ struct {+ JSValue str;+ int sep;+ } str;+ struct {+ JSValue val;+#ifdef CONFIG_BIGNUM+ slimb_t exponent; /* may be != 0 only if val is a float */+#endif+ } num;+ struct {+ JSAtom atom;+ BOOL has_escape;+ BOOL is_reserved;+ } ident;+ struct {+ JSValue body;+ JSValue flags;+ } regexp;+ } u;+} JSToken;++typedef struct JSParseState {+ JSContext *ctx;+ int last_line_num; /* line number of last token */+ int line_num; /* line number of current offset */+ const char *filename;+ JSToken token;+ BOOL got_lf; /* true if got line feed before the current token */+ const uint8_t *last_ptr;+ const uint8_t *buf_ptr;+ const uint8_t *buf_end;++ /* current function code */+ JSFunctionDef *cur_func;+ BOOL is_module; /* parsing a module */+ BOOL allow_html_comments;+ BOOL ext_json; /* true if accepting JSON superset */+} JSParseState;++typedef struct JSOpCode {+#ifdef DUMP_BYTECODE+ const char *name;+#endif+ uint8_t size; /* in bytes */+ /* the opcodes remove n_pop items from the top of the stack, then+ pushes n_push items */+ uint8_t n_pop;+ uint8_t n_push;+ uint8_t fmt;+} JSOpCode;++static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = {+#define FMT(f)+#ifdef DUMP_BYTECODE+#define DEF(id, size, n_pop, n_push, f) { #id, size, n_pop, n_push, OP_FMT_ ## f },+#else+#define DEF(id, size, n_pop, n_push, f) { size, n_pop, n_push, OP_FMT_ ## f },+#endif+#include "quickjs-opcode.h"+#undef DEF+#undef FMT+};++#if SHORT_OPCODES+/* After the final compilation pass, short opcodes are used. Their+ opcodes overlap with the temporary opcodes which cannot appear in+ the final bytecode. Their description is after the temporary+ opcodes in opcode_info[]. */+#define short_opcode_info(op) \+ opcode_info[(op) >= OP_TEMP_START ? \+ (op) + (OP_TEMP_END - OP_TEMP_START) : (op)]+#else+#define short_opcode_info(op) opcode_info[op]+#endif++static __exception int next_token(JSParseState *s);++static void free_token(JSParseState *s, JSToken *token)+{+ switch(token->val) {+#ifdef CONFIG_BIGNUM+ case TOK_NUMBER:+ JS_FreeValue(s->ctx, token->u.num.val);+ break;+#endif+ case TOK_STRING:+ case TOK_TEMPLATE:+ JS_FreeValue(s->ctx, token->u.str.str);+ break;+ case TOK_REGEXP:+ JS_FreeValue(s->ctx, token->u.regexp.body);+ JS_FreeValue(s->ctx, token->u.regexp.flags);+ break;+ case TOK_IDENT:+ case TOK_PRIVATE_NAME:+ JS_FreeAtom(s->ctx, token->u.ident.atom);+ break;+ default:+ if (token->val >= TOK_FIRST_KEYWORD &&+ token->val <= TOK_LAST_KEYWORD) {+ JS_FreeAtom(s->ctx, token->u.ident.atom);+ }+ break;+ }+}++static void __attribute((unused)) dump_token(JSParseState *s,+ const JSToken *token)+{+ switch(token->val) {+ case TOK_NUMBER:+ {+ double d;+ JS_ToFloat64(s->ctx, &d, token->u.num.val); /* no exception possible */+ printf("number: %.14g\n", d);+ }+ break;+ case TOK_IDENT:+ dump_atom:+ {+ char buf[ATOM_GET_STR_BUF_SIZE];+ printf("ident: '%s'\n",+ JS_AtomGetStr(s->ctx, buf, sizeof(buf), token->u.ident.atom));+ }+ break;+ case TOK_STRING:+ {+ const char *str;+ /* XXX: quote the string */+ str = JS_ToCString(s->ctx, token->u.str.str);+ printf("string: '%s'\n", str);+ JS_FreeCString(s->ctx, str);+ }+ break;+ case TOK_TEMPLATE:+ {+ const char *str;+ str = JS_ToCString(s->ctx, token->u.str.str);+ printf("template: `%s`\n", str);+ JS_FreeCString(s->ctx, str);+ }+ break;+ case TOK_REGEXP:+ {+ const char *str, *str2;+ str = JS_ToCString(s->ctx, token->u.regexp.body);+ str2 = JS_ToCString(s->ctx, token->u.regexp.flags);+ printf("regexp: '%s' '%s'\n", str, str2);+ JS_FreeCString(s->ctx, str);+ JS_FreeCString(s->ctx, str2);+ }+ break;+ case TOK_EOF:+ printf("eof\n");+ break;+ default:+ if (s->token.val >= TOK_NULL && s->token.val <= TOK_LAST_KEYWORD) {+ goto dump_atom;+ } else if (s->token.val >= 256) {+ printf("token: %d\n", token->val);+ } else {+ printf("token: '%c'\n", token->val);+ }+ break;+ }+}++int __attribute__((format(printf, 2, 3))) js_parse_error(JSParseState *s, const char *fmt, ...)+{+ JSContext *ctx = s->ctx;+ va_list ap;+ int backtrace_flags;+ + va_start(ap, fmt);+ JS_ThrowError2(ctx, JS_SYNTAX_ERROR, fmt, ap, FALSE);+ va_end(ap);+ backtrace_flags = 0;+ if (s->cur_func && s->cur_func->backtrace_barrier)+ backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL;+ build_backtrace(ctx, ctx->rt->current_exception, s->filename, s->line_num,+ backtrace_flags);+ return -1;+}++static int js_parse_expect(JSParseState *s, int tok)+{+ if (s->token.val != tok) {+ /* XXX: dump token correctly in all cases */+ return js_parse_error(s, "expecting '%c'", tok);+ }+ return next_token(s);+}++static int js_parse_expect_semi(JSParseState *s)+{+ if (s->token.val != ';') {+ /* automatic insertion of ';' */+ if (s->token.val == TOK_EOF || s->token.val == '}' || s->got_lf) {+ return 0;+ }+ return js_parse_error(s, "expecting '%c'", ';');+ }+ return next_token(s);+}++static int js_parse_error_reserved_identifier(JSParseState *s)+{+ char buf1[ATOM_GET_STR_BUF_SIZE];+ return js_parse_error(s, "'%s' is a reserved identifier",+ JS_AtomGetStr(s->ctx, buf1, sizeof(buf1),+ s->token.u.ident.atom));+}++static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p)+{+ uint32_t c;+ StringBuffer b_s, *b = &b_s;++ /* p points to the first byte of the template part */+ if (string_buffer_init(s->ctx, b, 32))+ goto fail;+ for(;;) {+ if (p >= s->buf_end)+ goto unexpected_eof;+ c = *p++;+ if (c == '`') {+ /* template end part */+ break;+ }+ if (c == '$' && *p == '{') {+ /* template start or middle part */+ p++;+ break;+ }+ if (c == '\\') {+ if (string_buffer_putc8(b, c))+ goto fail;+ if (p >= s->buf_end)+ goto unexpected_eof;+ c = *p++;+ }+ /* newline sequences are normalized as single '\n' bytes */+ if (c == '\r') {+ if (*p == '\n')+ p++;+ c = '\n';+ }+ if (c == '\n') {+ s->line_num++;+ } else if (c >= 0x80) {+ const uint8_t *p_next;+ c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);+ if (c > 0x10FFFF) {+ js_parse_error(s, "invalid UTF-8 sequence");+ goto fail;+ }+ p = p_next;+ }+ if (string_buffer_putc(b, c))+ goto fail;+ }+ s->token.val = TOK_TEMPLATE;+ s->token.u.str.sep = c;+ s->token.u.str.str = string_buffer_end(b);+ s->buf_ptr = p;+ return 0;++ unexpected_eof:+ js_parse_error(s, "unexpected end of string");+ fail:+ string_buffer_free(b);+ return -1;+}++static __exception int js_parse_string(JSParseState *s, int sep,+ BOOL do_throw, const uint8_t *p,+ JSToken *token, const uint8_t **pp)+{+ int ret;+ uint32_t c;+ StringBuffer b_s, *b = &b_s;++ /* string */+ if (string_buffer_init(s->ctx, b, 32))+ goto fail;+ for(;;) {+ if (p >= s->buf_end)+ goto invalid_char;+ c = *p;+ if (c < 0x20) {+ if (!s->cur_func) {+ if (do_throw)+ js_parse_error(s, "invalid character in a JSON string");+ goto fail;+ }+ if (sep == '`') {+ if (c == '\r') {+ if (p[1] == '\n')+ p++;+ c = '\n';+ }+ /* do not update s->line_num */+ } else if (c == '\n' || c == '\r')+ goto invalid_char;+ }+ p++;+ if (c == sep)+ break;+ if (c == '$' && *p == '{' && sep == '`') {+ /* template start or middle part */+ p++;+ break;+ }+ if (c == '\\') {+ c = *p;+ /* XXX: need a specific JSON case to avoid+ accepting invalid escapes */+ switch(c) {+ case '\0':+ if (p >= s->buf_end)+ goto invalid_char;+ p++;+ break;+ case '\'':+ case '\"':+ case '\\':+ p++;+ break;+ case '\r': /* accept DOS and MAC newline sequences */+ if (p[1] == '\n') {+ p++;+ }+ /* fall thru */+ case '\n':+ /* ignore escaped newline sequence */+ p++;+ if (sep != '`')+ s->line_num++;+ continue;+ default:+ if (c >= '0' && c <= '9') {+ if (!s->cur_func)+ goto invalid_escape; /* JSON case */+ if (!(s->cur_func->js_mode & JS_MODE_STRICT) && sep != '`')+ goto parse_escape;+ if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) {+ p++;+ c = '\0';+ } else {+ if (c >= '8' || sep == '`') {+ /* Note: according to ES2021, \8 and \9 are not+ accepted in strict mode or in templates. */+ goto invalid_escape;+ } else {+ if (do_throw)+ js_parse_error(s, "octal escape sequences are not allowed in strict mode");+ }+ goto fail;+ }+ } else if (c >= 0x80) {+ const uint8_t *p_next;+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);+ if (c > 0x10FFFF) {+ goto invalid_utf8;+ }+ p = p_next;+ /* LS or PS are skipped */+ if (c == CP_LS || c == CP_PS)+ continue;+ } else {+ parse_escape:+ ret = lre_parse_escape(&p, TRUE);+ if (ret == -1) {+ invalid_escape:+ if (do_throw)+ js_parse_error(s, "malformed escape sequence in string literal");+ goto fail;+ } else if (ret < 0) {+ /* ignore the '\' (could output a warning) */+ p++;+ } else {+ c = ret;+ }+ }+ break;+ }+ } else if (c >= 0x80) {+ const uint8_t *p_next;+ c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);+ if (c > 0x10FFFF)+ goto invalid_utf8;+ p = p_next;+ }+ if (string_buffer_putc(b, c))+ goto fail;+ }+ token->val = TOK_STRING;+ token->u.str.sep = c;+ token->u.str.str = string_buffer_end(b);+ *pp = p;+ return 0;++ invalid_utf8:+ if (do_throw)+ js_parse_error(s, "invalid UTF-8 sequence");+ goto fail;+ invalid_char:+ if (do_throw)+ js_parse_error(s, "unexpected end of string");+ fail:+ string_buffer_free(b);+ return -1;+}++static inline BOOL token_is_pseudo_keyword(JSParseState *s, JSAtom atom) {+ return s->token.val == TOK_IDENT && s->token.u.ident.atom == atom &&+ !s->token.u.ident.has_escape;+}++static __exception int js_parse_regexp(JSParseState *s)+{+ const uint8_t *p;+ BOOL in_class;+ StringBuffer b_s, *b = &b_s;+ StringBuffer b2_s, *b2 = &b2_s;+ uint32_t c;++ p = s->buf_ptr;+ p++;+ in_class = FALSE;+ if (string_buffer_init(s->ctx, b, 32))+ return -1;+ if (string_buffer_init(s->ctx, b2, 1))+ goto fail;+ for(;;) {+ if (p >= s->buf_end) {+ eof_error:+ js_parse_error(s, "unexpected end of regexp");+ goto fail;+ }+ c = *p++;+ if (c == '\n' || c == '\r') {+ goto eol_error;+ } else if (c == '/') {+ if (!in_class)+ break;+ } else if (c == '[') {+ in_class = TRUE;+ } else if (c == ']') {+ /* XXX: incorrect as the first character in a class */+ in_class = FALSE;+ } else if (c == '\\') {+ if (string_buffer_putc8(b, c))+ goto fail;+ c = *p++;+ if (c == '\n' || c == '\r')+ goto eol_error;+ else if (c == '\0' && p >= s->buf_end)+ goto eof_error;+ else if (c >= 0x80) {+ const uint8_t *p_next;+ c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);+ if (c > 0x10FFFF) {+ goto invalid_utf8;+ }+ p = p_next;+ if (c == CP_LS || c == CP_PS)+ goto eol_error;+ }+ } else if (c >= 0x80) {+ const uint8_t *p_next;+ c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);+ if (c > 0x10FFFF) {+ invalid_utf8:+ js_parse_error(s, "invalid UTF-8 sequence");+ goto fail;+ }+ p = p_next;+ /* LS or PS are considered as line terminator */+ if (c == CP_LS || c == CP_PS) {+ eol_error:+ js_parse_error(s, "unexpected line terminator in regexp");+ goto fail;+ }+ }+ if (string_buffer_putc(b, c))+ goto fail;+ }++ /* flags */+ for(;;) {+ const uint8_t *p_next = p;+ c = *p_next++;+ if (c >= 0x80) {+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);+ if (c > 0x10FFFF) {+ goto invalid_utf8;+ }+ }+ if (!lre_js_is_ident_next(c))+ break;+ if (string_buffer_putc(b2, c))+ goto fail;+ p = p_next;+ }++ s->token.val = TOK_REGEXP;+ s->token.u.regexp.body = string_buffer_end(b);+ s->token.u.regexp.flags = string_buffer_end(b2);+ s->buf_ptr = p;+ return 0;+ fail:+ string_buffer_free(b);+ string_buffer_free(b2);+ return -1;+}++static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize,+ char *static_buf)+{+ char *buf, *new_buf;+ size_t size, new_size;+ + buf = *pbuf;+ size = *psize;+ if (size >= (SIZE_MAX / 3) * 2)+ new_size = SIZE_MAX;+ else+ new_size = size + (size >> 1);+ if (buf == static_buf) {+ new_buf = js_malloc(ctx, new_size);+ if (!new_buf)+ return -1;+ memcpy(new_buf, buf, size);+ } else {+ new_buf = js_realloc(ctx, buf, new_size);+ if (!new_buf)+ return -1;+ }+ *pbuf = new_buf;+ *psize = new_size;+ return 0;+}++/* 'c' is the first character. Return JS_ATOM_NULL in case of error */+static JSAtom parse_ident(JSParseState *s, const uint8_t **pp,+ BOOL *pident_has_escape, int c, BOOL is_private)+{+ const uint8_t *p, *p1;+ char ident_buf[128], *buf;+ size_t ident_size, ident_pos;+ JSAtom atom;+ + p = *pp;+ buf = ident_buf;+ ident_size = sizeof(ident_buf);+ ident_pos = 0;+ if (is_private)+ buf[ident_pos++] = '#';+ for(;;) {+ p1 = p;+ + if (c < 128) {+ buf[ident_pos++] = c;+ } else {+ ident_pos += unicode_to_utf8((uint8_t*)buf + ident_pos, c);+ }+ c = *p1++;+ if (c == '\\' && *p1 == 'u') {+ c = lre_parse_escape(&p1, TRUE);+ *pident_has_escape = TRUE;+ } else if (c >= 128) {+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1);+ }+ if (!lre_js_is_ident_next(c))+ break;+ p = p1;+ if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) {+ if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) {+ atom = JS_ATOM_NULL;+ goto done;+ }+ }+ }+ atom = JS_NewAtomLen(s->ctx, buf, ident_pos);+ done:+ if (unlikely(buf != ident_buf))+ js_free(s->ctx, buf);+ *pp = p;+ return atom;+}+++static __exception int next_token(JSParseState *s)+{+ const uint8_t *p;+ int c;+ BOOL ident_has_escape;+ JSAtom atom;+ + if (js_check_stack_overflow(s->ctx->rt, 0)) {+ return js_parse_error(s, "stack overflow");+ }+ + free_token(s, &s->token);++ p = s->last_ptr = s->buf_ptr;+ s->got_lf = FALSE;+ s->last_line_num = s->token.line_num;+ redo:+ s->token.line_num = s->line_num;+ s->token.ptr = p;+ c = *p;+ switch(c) {+ case 0:+ if (p >= s->buf_end) {+ s->token.val = TOK_EOF;+ } else {+ goto def_token;+ }+ break;+ case '`':+ if (js_parse_template_part(s, p + 1))+ goto fail;+ p = s->buf_ptr;+ break;+ case '\'':+ case '\"':+ if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p))+ goto fail;+ break;+ case '\r': /* accept DOS and MAC newline sequences */+ if (p[1] == '\n') {+ p++;+ }+ /* fall thru */+ case '\n':+ p++;+ line_terminator:+ s->got_lf = TRUE;+ s->line_num++;+ goto redo;+ case '\f':+ case '\v':+ case ' ':+ case '\t':+ p++;+ goto redo;+ case '/':+ if (p[1] == '*') {+ /* comment */+ p += 2;+ for(;;) {+ if (*p == '\0' && p >= s->buf_end) {+ js_parse_error(s, "unexpected end of comment");+ goto fail;+ }+ if (p[0] == '*' && p[1] == '/') {+ p += 2;+ break;+ }+ if (*p == '\n') {+ s->line_num++;+ s->got_lf = TRUE; /* considered as LF for ASI */+ p++;+ } else if (*p == '\r') {+ s->got_lf = TRUE; /* considered as LF for ASI */+ p++;+ } else if (*p >= 0x80) {+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);+ if (c == CP_LS || c == CP_PS) {+ s->got_lf = TRUE; /* considered as LF for ASI */+ } else if (c == -1) {+ p++; /* skip invalid UTF-8 */+ }+ } else {+ p++;+ }+ }+ goto redo;+ } else if (p[1] == '/') {+ /* line comment */+ p += 2;+ skip_line_comment:+ for(;;) {+ if (*p == '\0' && p >= s->buf_end)+ break;+ if (*p == '\r' || *p == '\n')+ break;+ if (*p >= 0x80) {+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);+ /* LS or PS are considered as line terminator */+ if (c == CP_LS || c == CP_PS) {+ break;+ } else if (c == -1) {+ p++; /* skip invalid UTF-8 */+ }+ } else {+ p++;+ }+ }+ goto redo;+ } else if (p[1] == '=') {+ p += 2;+ s->token.val = TOK_DIV_ASSIGN;+ } else {+ p++;+ s->token.val = c;+ }+ break;+ case '\\':+ if (p[1] == 'u') {+ const uint8_t *p1 = p + 1;+ int c1 = lre_parse_escape(&p1, TRUE);+ if (c1 >= 0 && lre_js_is_ident_first(c1)) {+ c = c1;+ p = p1;+ ident_has_escape = TRUE;+ goto has_ident;+ } else {+ /* XXX: syntax error? */+ }+ }+ goto def_token;+ case 'a': case 'b': case 'c': case 'd':+ case 'e': case 'f': case 'g': case 'h':+ case 'i': case 'j': case 'k': case 'l':+ case 'm': case 'n': case 'o': case 'p':+ case 'q': case 'r': case 's': case 't':+ case 'u': case 'v': case 'w': case 'x':+ case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D':+ case 'E': case 'F': case 'G': case 'H':+ case 'I': case 'J': case 'K': case 'L':+ case 'M': case 'N': case 'O': case 'P':+ case 'Q': case 'R': case 'S': case 'T':+ case 'U': case 'V': case 'W': case 'X':+ case 'Y': case 'Z': + case '_':+ case '$':+ /* identifier */+ p++;+ ident_has_escape = FALSE;+ has_ident:+ atom = parse_ident(s, &p, &ident_has_escape, c, FALSE);+ if (atom == JS_ATOM_NULL)+ goto fail;+ s->token.u.ident.atom = atom;+ s->token.u.ident.has_escape = ident_has_escape;+ s->token.u.ident.is_reserved = FALSE;+ if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD ||+ (s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD &&+ (s->cur_func->js_mode & JS_MODE_STRICT)) ||+ (s->token.u.ident.atom == JS_ATOM_yield &&+ ((s->cur_func->func_kind & JS_FUNC_GENERATOR) ||+ (s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&+ !s->cur_func->in_function_body && s->cur_func->parent &&+ (s->cur_func->parent->func_kind & JS_FUNC_GENERATOR)))) ||+ (s->token.u.ident.atom == JS_ATOM_await &&+ (s->is_module ||+ (((s->cur_func->func_kind & JS_FUNC_ASYNC) ||+ (s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&+ !s->cur_func->in_function_body && s->cur_func->parent &&+ (s->cur_func->parent->func_kind & JS_FUNC_ASYNC))))))) {+ if (ident_has_escape) {+ s->token.u.ident.is_reserved = TRUE;+ s->token.val = TOK_IDENT;+ } else {+ /* The keywords atoms are pre allocated */+ s->token.val = s->token.u.ident.atom - 1 + TOK_FIRST_KEYWORD;+ }+ } else {+ s->token.val = TOK_IDENT;+ }+ break;+ case '#':+ /* private name */+ {+ const uint8_t *p1;+ p++;+ p1 = p;+ c = *p1++;+ if (c == '\\' && *p1 == 'u') {+ c = lre_parse_escape(&p1, TRUE);+ } else if (c >= 128) {+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1);+ }+ if (!lre_js_is_ident_first(c)) {+ js_parse_error(s, "invalid first character of private name");+ goto fail;+ }+ p = p1;+ ident_has_escape = FALSE; /* not used */+ atom = parse_ident(s, &p, &ident_has_escape, c, TRUE);+ if (atom == JS_ATOM_NULL)+ goto fail;+ s->token.u.ident.atom = atom;+ s->token.val = TOK_PRIVATE_NAME;+ }+ break;+ case '.':+ if (p[1] == '.' && p[2] == '.') {+ p += 3;+ s->token.val = TOK_ELLIPSIS;+ break;+ }+ if (p[1] >= '0' && p[1] <= '9') {+ goto parse_number;+ } else {+ goto def_token;+ }+ break;+ case '0':+ /* in strict mode, octal literals are not accepted */+ if (is_digit(p[1]) && (s->cur_func->js_mode & JS_MODE_STRICT)) {+ js_parse_error(s, "octal literals are deprecated in strict mode");+ goto fail;+ }+ goto parse_number;+ case '1': case '2': case '3': case '4':+ case '5': case '6': case '7': case '8':+ case '9': + /* number */+ parse_number:+ {+ JSValue ret;+ const uint8_t *p1;+ int flags, radix;+ flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL |+ ATOD_ACCEPT_UNDERSCORES;+#ifdef CONFIG_BIGNUM+ flags |= ATOD_ACCEPT_SUFFIX;+ if (s->cur_func->js_mode & JS_MODE_MATH) {+ flags |= ATOD_MODE_BIGINT;+ if (s->cur_func->js_mode & JS_MODE_MATH)+ flags |= ATOD_TYPE_BIG_FLOAT;+ }+#endif+ radix = 0;+#ifdef CONFIG_BIGNUM+ s->token.u.num.exponent = 0;+ ret = js_atof2(s->ctx, (const char *)p, (const char **)&p, radix,+ flags, &s->token.u.num.exponent);+#else+ ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix,+ flags);+#endif+ if (JS_IsException(ret))+ goto fail;+ /* reject `10instanceof Number` */+ if (JS_VALUE_IS_NAN(ret) ||+ lre_js_is_ident_next(unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1))) {+ JS_FreeValue(s->ctx, ret);+ js_parse_error(s, "invalid number literal");+ goto fail;+ }+ s->token.val = TOK_NUMBER;+ s->token.u.num.val = ret;+ }+ break;+ case '*':+ if (p[1] == '=') {+ p += 2;+ s->token.val = TOK_MUL_ASSIGN;+ } else if (p[1] == '*') {+ if (p[2] == '=') {+ p += 3;+ s->token.val = TOK_POW_ASSIGN;+ } else {+ p += 2;+ s->token.val = TOK_POW;+ }+ } else {+ goto def_token;+ }+ break;+ case '%':+ if (p[1] == '=') {+ p += 2;+ s->token.val = TOK_MOD_ASSIGN;+ } else {+ goto def_token;+ }+ break;+ case '+':+ if (p[1] == '=') {+ p += 2;+ s->token.val = TOK_PLUS_ASSIGN;+ } else if (p[1] == '+') {+ p += 2;+ s->token.val = TOK_INC;+ } else {+ goto def_token;+ }+ break;+ case '-':+ if (p[1] == '=') {+ p += 2;+ s->token.val = TOK_MINUS_ASSIGN;+ } else if (p[1] == '-') {+ if (s->allow_html_comments &&+ p[2] == '>' && s->last_line_num != s->line_num) {+ /* Annex B: `-->` at beginning of line is an html comment end.+ It extends to the end of the line.+ */+ goto skip_line_comment;+ }+ p += 2;+ s->token.val = TOK_DEC;+ } else {+ goto def_token;+ }+ break;+ case '<':+ if (p[1] == '=') {+ p += 2;+ s->token.val = TOK_LTE;+ } else if (p[1] == '<') {+ if (p[2] == '=') {+ p += 3;+ s->token.val = TOK_SHL_ASSIGN;+ } else {+ p += 2;+ s->token.val = TOK_SHL;+ }+ } else if (s->allow_html_comments &&+ p[1] == '!' && p[2] == '-' && p[3] == '-') {+ /* Annex B: handle `<!--` single line html comments */+ goto skip_line_comment;+ } else {+ goto def_token;+ }+ break;+ case '>':+ if (p[1] == '=') {+ p += 2;+ s->token.val = TOK_GTE;+ } else if (p[1] == '>') {+ if (p[2] == '>') {+ if (p[3] == '=') {+ p += 4;+ s->token.val = TOK_SHR_ASSIGN;+ } else {+ p += 3;+ s->token.val = TOK_SHR;+ }+ } else if (p[2] == '=') {+ p += 3;+ s->token.val = TOK_SAR_ASSIGN;+ } else {+ p += 2;+ s->token.val = TOK_SAR;+ }+ } else {+ goto def_token;+ }+ break;+ case '=':+ if (p[1] == '=') {+ if (p[2] == '=') {+ p += 3;+ s->token.val = TOK_STRICT_EQ;+ } else {+ p += 2;+ s->token.val = TOK_EQ;+ }+ } else if (p[1] == '>') {+ p += 2;+ s->token.val = TOK_ARROW;+ } else {+ goto def_token;+ }+ break;+ case '!':+ if (p[1] == '=') {+ if (p[2] == '=') {+ p += 3;+ s->token.val = TOK_STRICT_NEQ;+ } else {+ p += 2;+ s->token.val = TOK_NEQ;+ }+ } else {+ goto def_token;+ }+ break;+ case '&':+ if (p[1] == '=') {+ p += 2;+ s->token.val = TOK_AND_ASSIGN;+ } else if (p[1] == '&') {+ if (p[2] == '=') {+ p += 3;+ s->token.val = TOK_LAND_ASSIGN;+ } else {+ p += 2;+ s->token.val = TOK_LAND;+ }+ } else {+ goto def_token;+ }+ break;+#ifdef CONFIG_BIGNUM+ /* in math mode, '^' is the power operator. '^^' is always the+ xor operator and '**' is always the power operator */+ case '^':+ if (p[1] == '=') {+ p += 2;+ if (s->cur_func->js_mode & JS_MODE_MATH)+ s->token.val = TOK_MATH_POW_ASSIGN;+ else+ s->token.val = TOK_XOR_ASSIGN;+ } else if (p[1] == '^') {+ if (p[2] == '=') {+ p += 3;+ s->token.val = TOK_XOR_ASSIGN;+ } else {+ p += 2;+ s->token.val = '^';+ }+ } else {+ p++;+ if (s->cur_func->js_mode & JS_MODE_MATH)+ s->token.val = TOK_MATH_POW;+ else+ s->token.val = '^';+ }+ break;+#else+ case '^':+ if (p[1] == '=') {+ p += 2;+ s->token.val = TOK_XOR_ASSIGN;+ } else {+ goto def_token;+ }+ break;+#endif+ case '|':+ if (p[1] == '=') {+ p += 2;+ s->token.val = TOK_OR_ASSIGN;+ } else if (p[1] == '|') {+ if (p[2] == '=') {+ p += 3;+ s->token.val = TOK_LOR_ASSIGN;+ } else {+ p += 2;+ s->token.val = TOK_LOR;+ }+ } else {+ goto def_token;+ }+ break;+ case '?':+ if (p[1] == '?') {+ if (p[2] == '=') {+ p += 3;+ s->token.val = TOK_DOUBLE_QUESTION_MARK_ASSIGN;+ } else {+ p += 2;+ s->token.val = TOK_DOUBLE_QUESTION_MARK;+ }+ } else if (p[1] == '.' && !(p[2] >= '0' && p[2] <= '9')) {+ p += 2;+ s->token.val = TOK_QUESTION_MARK_DOT;+ } else {+ goto def_token;+ }+ break;+ default:+ if (c >= 128) {+ /* unicode value */+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);+ switch(c) {+ case CP_PS:+ case CP_LS:+ /* XXX: should avoid incrementing line_number, but+ needed to handle HTML comments */+ goto line_terminator; + default:+ if (lre_is_space(c)) {+ goto redo;+ } else if (lre_js_is_ident_first(c)) {+ ident_has_escape = FALSE;+ goto has_ident;+ } else {+ js_parse_error(s, "unexpected character");+ goto fail;+ }+ }+ }+ def_token:+ s->token.val = c;+ p++;+ break;+ }+ s->buf_ptr = p;++ // dump_token(s, &s->token);+ return 0;++ fail:+ s->token.val = TOK_ERROR;+ return -1;+}++/* 'c' is the first character. Return JS_ATOM_NULL in case of error */+static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c)+{+ const uint8_t *p;+ char ident_buf[128], *buf;+ size_t ident_size, ident_pos;+ JSAtom atom;+ + p = *pp;+ buf = ident_buf;+ ident_size = sizeof(ident_buf);+ ident_pos = 0;+ for(;;) {+ buf[ident_pos++] = c;+ c = *p;+ if (c >= 128 ||+ !((lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1))+ break;+ p++;+ if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) {+ if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) {+ atom = JS_ATOM_NULL;+ goto done;+ }+ }+ }+ atom = JS_NewAtomLen(s->ctx, buf, ident_pos);+ done:+ if (unlikely(buf != ident_buf))+ js_free(s->ctx, buf);+ *pp = p;+ return atom;+}++static __exception int json_next_token(JSParseState *s)+{+ const uint8_t *p;+ int c;+ JSAtom atom;+ + if (js_check_stack_overflow(s->ctx->rt, 0)) {+ return js_parse_error(s, "stack overflow");+ }+ + free_token(s, &s->token);++ p = s->last_ptr = s->buf_ptr;+ s->last_line_num = s->token.line_num;+ redo:+ s->token.line_num = s->line_num;+ s->token.ptr = p;+ c = *p;+ switch(c) {+ case 0:+ if (p >= s->buf_end) {+ s->token.val = TOK_EOF;+ } else {+ goto def_token;+ }+ break;+ case '\'':+ if (!s->ext_json) {+ /* JSON does not accept single quoted strings */+ goto def_token;+ }+ /* fall through */+ case '\"':+ if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p))+ goto fail;+ break;+ case '\r': /* accept DOS and MAC newline sequences */+ if (p[1] == '\n') {+ p++;+ }+ /* fall thru */+ case '\n':+ p++;+ s->line_num++;+ goto redo;+ case '\f':+ case '\v':+ if (!s->ext_json) {+ /* JSONWhitespace does not match <VT>, nor <FF> */+ goto def_token;+ }+ /* fall through */+ case ' ':+ case '\t':+ p++;+ goto redo;+ case '/':+ if (!s->ext_json) {+ /* JSON does not accept comments */+ goto def_token;+ }+ if (p[1] == '*') {+ /* comment */+ p += 2;+ for(;;) {+ if (*p == '\0' && p >= s->buf_end) {+ js_parse_error(s, "unexpected end of comment");+ goto fail;+ }+ if (p[0] == '*' && p[1] == '/') {+ p += 2;+ break;+ }+ if (*p == '\n') {+ s->line_num++;+ p++;+ } else if (*p == '\r') {+ p++;+ } else if (*p >= 0x80) {+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);+ if (c == -1) {+ p++; /* skip invalid UTF-8 */+ }+ } else {+ p++;+ }+ }+ goto redo;+ } else if (p[1] == '/') {+ /* line comment */+ p += 2;+ for(;;) {+ if (*p == '\0' && p >= s->buf_end)+ break;+ if (*p == '\r' || *p == '\n')+ break;+ if (*p >= 0x80) {+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);+ /* LS or PS are considered as line terminator */+ if (c == CP_LS || c == CP_PS) {+ break;+ } else if (c == -1) {+ p++; /* skip invalid UTF-8 */+ }+ } else {+ p++;+ }+ }+ goto redo;+ } else {+ goto def_token;+ }+ break;+ case 'a': case 'b': case 'c': case 'd':+ case 'e': case 'f': case 'g': case 'h':+ case 'i': case 'j': case 'k': case 'l':+ case 'm': case 'n': case 'o': case 'p':+ case 'q': case 'r': case 's': case 't':+ case 'u': case 'v': case 'w': case 'x':+ case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D':+ case 'E': case 'F': case 'G': case 'H':+ case 'I': case 'J': case 'K': case 'L':+ case 'M': case 'N': case 'O': case 'P':+ case 'Q': case 'R': case 'S': case 'T':+ case 'U': case 'V': case 'W': case 'X':+ case 'Y': case 'Z': + case '_':+ case '$':+ /* identifier : only pure ascii characters are accepted */+ p++;+ atom = json_parse_ident(s, &p, c);+ if (atom == JS_ATOM_NULL)+ goto fail;+ s->token.u.ident.atom = atom;+ s->token.u.ident.has_escape = FALSE;+ s->token.u.ident.is_reserved = FALSE;+ s->token.val = TOK_IDENT;+ break;+ case '+':+ if (!s->ext_json || !is_digit(p[1]))+ goto def_token;+ goto parse_number;+ case '0':+ if (is_digit(p[1]))+ goto def_token;+ goto parse_number;+ case '-':+ if (!is_digit(p[1]))+ goto def_token;+ goto parse_number;+ case '1': case '2': case '3': case '4':+ case '5': case '6': case '7': case '8':+ case '9': + /* number */+ parse_number:+ {+ JSValue ret;+ int flags, radix;+ if (!s->ext_json) {+ flags = 0;+ radix = 10;+ } else {+ flags = ATOD_ACCEPT_BIN_OCT;+ radix = 0;+ }+ ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix,+ flags);+ if (JS_IsException(ret))+ goto fail;+ s->token.val = TOK_NUMBER;+ s->token.u.num.val = ret;+ }+ break;+ default:+ if (c >= 128) {+ js_parse_error(s, "unexpected character");+ goto fail;+ }+ def_token:+ s->token.val = c;+ p++;+ break;+ }+ s->buf_ptr = p;++ // dump_token(s, &s->token);+ return 0;++ fail:+ s->token.val = TOK_ERROR;+ return -1;+}++/* only used for ':' and '=>', 'let' or 'function' look-ahead. *pp is+ only set if TOK_IMPORT is returned */+/* XXX: handle all unicode cases */+static int simple_next_token(const uint8_t **pp, BOOL no_line_terminator)+{+ const uint8_t *p;+ uint32_t c;+ + /* skip spaces and comments */+ p = *pp;+ for (;;) {+ switch(c = *p++) {+ case '\r':+ case '\n':+ if (no_line_terminator)+ return '\n';+ continue;+ case ' ':+ case '\t':+ case '\v':+ case '\f':+ continue;+ case '/':+ if (*p == '/') {+ if (no_line_terminator)+ return '\n';+ while (*p && *p != '\r' && *p != '\n')+ p++;+ continue;+ }+ if (*p == '*') {+ while (*++p) {+ if ((*p == '\r' || *p == '\n') && no_line_terminator)+ return '\n';+ if (*p == '*' && p[1] == '/') {+ p += 2;+ break;+ }+ }+ continue;+ }+ break;+ case '=':+ if (*p == '>')+ return TOK_ARROW;+ break;+ default:+ if (lre_js_is_ident_first(c)) {+ if (c == 'i') {+ if (p[0] == 'n' && !lre_js_is_ident_next(p[1])) {+ return TOK_IN;+ }+ if (p[0] == 'm' && p[1] == 'p' && p[2] == 'o' &&+ p[3] == 'r' && p[4] == 't' &&+ !lre_js_is_ident_next(p[5])) {+ *pp = p + 5;+ return TOK_IMPORT;+ }+ } else if (c == 'o' && *p == 'f' && !lre_js_is_ident_next(p[1])) {+ return TOK_OF;+ } else if (c == 'e' &&+ p[0] == 'x' && p[1] == 'p' && p[2] == 'o' &&+ p[3] == 'r' && p[4] == 't' &&+ !lre_js_is_ident_next(p[5])) {+ *pp = p + 5;+ return TOK_EXPORT;+ } else if (c == 'f' && p[0] == 'u' && p[1] == 'n' &&+ p[2] == 'c' && p[3] == 't' && p[4] == 'i' &&+ p[5] == 'o' && p[6] == 'n' && !lre_js_is_ident_next(p[7])) {+ return TOK_FUNCTION;+ }+ return TOK_IDENT;+ }+ break;+ }+ return c;+ }+}++static int peek_token(JSParseState *s, BOOL no_line_terminator)+{+ const uint8_t *p = s->buf_ptr;+ return simple_next_token(&p, no_line_terminator);+}++/* return true if 'input' contains the source of a module+ (heuristic). 'input' must be a zero terminated.++ Heuristic: skip comments and expect 'import' keyword not followed+ by '(' or '.' or export keyword.+*/+BOOL JS_DetectModule(const char *input, size_t input_len)+{+ const uint8_t *p = (const uint8_t *)input;+ int tok;+ switch(simple_next_token(&p, FALSE)) {+ case TOK_IMPORT:+ tok = simple_next_token(&p, FALSE);+ return (tok != '.' && tok != '(');+ case TOK_EXPORT:+ return TRUE;+ default:+ return FALSE;+ }+}++static inline int get_prev_opcode(JSFunctionDef *fd) {+ if (fd->last_opcode_pos < 0)+ return OP_invalid;+ else+ return fd->byte_code.buf[fd->last_opcode_pos];+}++static BOOL js_is_live_code(JSParseState *s) {+ switch (get_prev_opcode(s->cur_func)) {+ case OP_tail_call:+ case OP_tail_call_method:+ case OP_return:+ case OP_return_undef:+ case OP_return_async:+ case OP_throw:+ case OP_throw_error:+ case OP_goto:+#if SHORT_OPCODES+ case OP_goto8:+ case OP_goto16:+#endif+ case OP_ret:+ return FALSE;+ default:+ return TRUE;+ }+}++static void emit_u8(JSParseState *s, uint8_t val)+{+ dbuf_putc(&s->cur_func->byte_code, val);+}++static void emit_u16(JSParseState *s, uint16_t val)+{+ dbuf_put_u16(&s->cur_func->byte_code, val);+}++static void emit_u32(JSParseState *s, uint32_t val)+{+ dbuf_put_u32(&s->cur_func->byte_code, val);+}++static void emit_op(JSParseState *s, uint8_t val)+{+ JSFunctionDef *fd = s->cur_func;+ DynBuf *bc = &fd->byte_code;++ /* Use the line number of the last token used, not the next token,+ nor the current offset in the source file.+ */+ if (unlikely(fd->last_opcode_line_num != s->last_line_num)) {+ dbuf_putc(bc, OP_line_num);+ dbuf_put_u32(bc, s->last_line_num);+ fd->last_opcode_line_num = s->last_line_num;+ }+ fd->last_opcode_pos = bc->size;+ dbuf_putc(bc, val);+}++static void emit_atom(JSParseState *s, JSAtom name)+{+ emit_u32(s, JS_DupAtom(s->ctx, name));+}++static int update_label(JSFunctionDef *s, int label, int delta)+{+ LabelSlot *ls;++ assert(label >= 0 && label < s->label_count);+ ls = &s->label_slots[label];+ ls->ref_count += delta;+ assert(ls->ref_count >= 0);+ return ls->ref_count;+}++static int new_label_fd(JSFunctionDef *fd, int label)+{+ LabelSlot *ls;++ if (label < 0) {+ if (js_resize_array(fd->ctx, (void *)&fd->label_slots,+ sizeof(fd->label_slots[0]),+ &fd->label_size, fd->label_count + 1))+ return -1;+ label = fd->label_count++;+ ls = &fd->label_slots[label];+ ls->ref_count = 0;+ ls->pos = -1;+ ls->pos2 = -1;+ ls->addr = -1;+ ls->first_reloc = NULL;+ }+ return label;+}++static int new_label(JSParseState *s)+{+ return new_label_fd(s->cur_func, -1);+}++/* return the label ID offset */+static int emit_label(JSParseState *s, int label)+{+ if (label >= 0) {+ emit_op(s, OP_label);+ emit_u32(s, label);+ s->cur_func->label_slots[label].pos = s->cur_func->byte_code.size;+ return s->cur_func->byte_code.size - 4;+ } else {+ return -1;+ }+}++/* return label or -1 if dead code */+static int emit_goto(JSParseState *s, int opcode, int label)+{+ if (js_is_live_code(s)) {+ if (label < 0)+ label = new_label(s);+ emit_op(s, opcode);+ emit_u32(s, label);+ s->cur_func->label_slots[label].ref_count++;+ return label;+ }+ return -1;+}++/* return the constant pool index. 'val' is not duplicated. */+static int cpool_add(JSParseState *s, JSValue val)+{+ JSFunctionDef *fd = s->cur_func;+ + if (js_resize_array(s->ctx, (void *)&fd->cpool, sizeof(fd->cpool[0]),+ &fd->cpool_size, fd->cpool_count + 1))+ return -1;+ fd->cpool[fd->cpool_count++] = val;+ return fd->cpool_count - 1;+}++static __exception int emit_push_const(JSParseState *s, JSValueConst val,+ BOOL as_atom)+{+ int idx;++ if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING && as_atom) {+ JSAtom atom;+ /* warning: JS_NewAtomStr frees the string value */+ JS_DupValue(s->ctx, val);+ atom = JS_NewAtomStr(s->ctx, JS_VALUE_GET_STRING(val));+ if (atom != JS_ATOM_NULL && !__JS_AtomIsTaggedInt(atom)) {+ emit_op(s, OP_push_atom_value);+ emit_u32(s, atom);+ return 0;+ }+ }++ idx = cpool_add(s, JS_DupValue(s->ctx, val));+ if (idx < 0)+ return -1;+ emit_op(s, OP_push_const);+ emit_u32(s, idx);+ return 0;+}++/* return the variable index or -1 if not found,+ add ARGUMENT_VAR_OFFSET for argument variables */+static int find_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name)+{+ int i;+ for(i = fd->arg_count; i-- > 0;) {+ if (fd->args[i].var_name == name)+ return i | ARGUMENT_VAR_OFFSET;+ }+ return -1;+}++static int find_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)+{+ int i;+ for(i = fd->var_count; i-- > 0;) {+ if (fd->vars[i].var_name == name && fd->vars[i].scope_level == 0)+ return i;+ }+ return find_arg(ctx, fd, name);+}++/* find a variable declaration in a given scope */+static int find_var_in_scope(JSContext *ctx, JSFunctionDef *fd,+ JSAtom name, int scope_level)+{+ int scope_idx;+ for(scope_idx = fd->scopes[scope_level].first; scope_idx >= 0;+ scope_idx = fd->vars[scope_idx].scope_next) {+ if (fd->vars[scope_idx].scope_level != scope_level)+ break;+ if (fd->vars[scope_idx].var_name == name)+ return scope_idx;+ }+ return -1;+}++/* return true if scope == parent_scope or if scope is a child of+ parent_scope */+static BOOL is_child_scope(JSContext *ctx, JSFunctionDef *fd,+ int scope, int parent_scope)+{+ while (scope >= 0) {+ if (scope == parent_scope)+ return TRUE;+ scope = fd->scopes[scope].parent;+ }+ return FALSE;+}++/* find a 'var' declaration in the same scope or a child scope */+static int find_var_in_child_scope(JSContext *ctx, JSFunctionDef *fd,+ JSAtom name, int scope_level)+{+ int i;+ for(i = 0; i < fd->var_count; i++) {+ JSVarDef *vd = &fd->vars[i];+ if (vd->var_name == name && vd->scope_level == 0) {+ if (is_child_scope(ctx, fd, vd->scope_next,+ scope_level))+ return i;+ }+ }+ return -1;+}+++static JSGlobalVar *find_global_var(JSFunctionDef *fd, JSAtom name)+{+ int i;+ for(i = 0; i < fd->global_var_count; i++) {+ JSGlobalVar *hf = &fd->global_vars[i];+ if (hf->var_name == name)+ return hf;+ }+ return NULL;++}++static JSGlobalVar *find_lexical_global_var(JSFunctionDef *fd, JSAtom name)+{+ JSGlobalVar *hf = find_global_var(fd, name);+ if (hf && hf->is_lexical)+ return hf;+ else+ return NULL;+}++static int find_lexical_decl(JSContext *ctx, JSFunctionDef *fd, JSAtom name,+ int scope_idx, BOOL check_catch_var)+{+ while (scope_idx >= 0) {+ JSVarDef *vd = &fd->vars[scope_idx];+ if (vd->var_name == name &&+ (vd->is_lexical || (vd->var_kind == JS_VAR_CATCH &&+ check_catch_var)))+ return scope_idx;+ scope_idx = vd->scope_next;+ }++ if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_GLOBAL) {+ if (find_lexical_global_var(fd, name))+ return GLOBAL_VAR_OFFSET;+ }+ return -1;+}++static int push_scope(JSParseState *s) {+ if (s->cur_func) {+ JSFunctionDef *fd = s->cur_func;+ int scope = fd->scope_count;+ /* XXX: should check for scope overflow */+ if ((fd->scope_count + 1) > fd->scope_size) {+ int new_size;+ size_t slack;+ JSVarScope *new_buf;+ /* XXX: potential arithmetic overflow */+ new_size = max_int(fd->scope_count + 1, fd->scope_size * 3 / 2);+ if (fd->scopes == fd->def_scope_array) {+ new_buf = js_realloc2(s->ctx, NULL, new_size * sizeof(*fd->scopes), &slack);+ if (!new_buf)+ return -1;+ memcpy(new_buf, fd->scopes, fd->scope_count * sizeof(*fd->scopes));+ } else {+ new_buf = js_realloc2(s->ctx, fd->scopes, new_size * sizeof(*fd->scopes), &slack);+ if (!new_buf)+ return -1;+ }+ new_size += slack / sizeof(*new_buf);+ fd->scopes = new_buf;+ fd->scope_size = new_size;+ }+ fd->scope_count++;+ fd->scopes[scope].parent = fd->scope_level;+ fd->scopes[scope].first = fd->scope_first;+ emit_op(s, OP_enter_scope);+ emit_u16(s, scope);+ return fd->scope_level = scope;+ }+ return 0;+}++static int get_first_lexical_var(JSFunctionDef *fd, int scope)+{+ while (scope >= 0) {+ int scope_idx = fd->scopes[scope].first;+ if (scope_idx >= 0)+ return scope_idx;+ scope = fd->scopes[scope].parent;+ }+ return -1;+}++static void pop_scope(JSParseState *s) {+ if (s->cur_func) {+ /* disable scoped variables */+ JSFunctionDef *fd = s->cur_func;+ int scope = fd->scope_level;+ emit_op(s, OP_leave_scope);+ emit_u16(s, scope);+ fd->scope_level = fd->scopes[scope].parent;+ fd->scope_first = get_first_lexical_var(fd, fd->scope_level);+ }+}++static void close_scopes(JSParseState *s, int scope, int scope_stop)+{+ while (scope > scope_stop) {+ emit_op(s, OP_leave_scope);+ emit_u16(s, scope);+ scope = s->cur_func->scopes[scope].parent;+ }+}++/* return the variable index or -1 if error */+static int add_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)+{+ JSVarDef *vd;++ /* the local variable indexes are currently stored on 16 bits */+ if (fd->var_count >= JS_MAX_LOCAL_VARS) {+ JS_ThrowInternalError(ctx, "too many local variables");+ return -1;+ }+ if (js_resize_array(ctx, (void **)&fd->vars, sizeof(fd->vars[0]),+ &fd->var_size, fd->var_count + 1))+ return -1;+ vd = &fd->vars[fd->var_count++];+ memset(vd, 0, sizeof(*vd));+ vd->var_name = JS_DupAtom(ctx, name);+ vd->func_pool_idx = -1;+ return fd->var_count - 1;+}++static int add_scope_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name,+ JSVarKindEnum var_kind)+{+ int idx = add_var(ctx, fd, name);+ if (idx >= 0) {+ JSVarDef *vd = &fd->vars[idx];+ vd->var_kind = var_kind;+ vd->scope_level = fd->scope_level;+ vd->scope_next = fd->scope_first;+ fd->scopes[fd->scope_level].first = idx;+ fd->scope_first = idx;+ }+ return idx;+}++static int add_func_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)+{+ int idx = fd->func_var_idx;+ if (idx < 0 && (idx = add_var(ctx, fd, name)) >= 0) {+ fd->func_var_idx = idx;+ fd->vars[idx].var_kind = JS_VAR_FUNCTION_NAME;+ if (fd->js_mode & JS_MODE_STRICT)+ fd->vars[idx].is_const = TRUE;+ }+ return idx;+}++static int add_arguments_var(JSContext *ctx, JSFunctionDef *fd)+{+ int idx = fd->arguments_var_idx;+ if (idx < 0 && (idx = add_var(ctx, fd, JS_ATOM_arguments)) >= 0) {+ fd->arguments_var_idx = idx;+ }+ return idx;+}++/* add an argument definition in the argument scope. Only needed when+ "eval()" may be called in the argument scope. Return 0 if OK. */+static int add_arguments_arg(JSContext *ctx, JSFunctionDef *fd)+{+ int idx;+ if (fd->arguments_arg_idx < 0) {+ idx = find_var_in_scope(ctx, fd, JS_ATOM_arguments, ARG_SCOPE_INDEX);+ if (idx < 0) {+ /* XXX: the scope links are not fully updated. May be an+ issue if there are child scopes of the argument+ scope */+ idx = add_var(ctx, fd, JS_ATOM_arguments);+ if (idx < 0)+ return -1;+ fd->vars[idx].scope_next = fd->scopes[ARG_SCOPE_INDEX].first;+ fd->scopes[ARG_SCOPE_INDEX].first = idx;+ fd->vars[idx].scope_level = ARG_SCOPE_INDEX;+ fd->vars[idx].is_lexical = TRUE;++ fd->arguments_arg_idx = idx;+ }+ }+ return 0;+}++static int add_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name)+{+ JSVarDef *vd;++ /* the local variable indexes are currently stored on 16 bits */+ if (fd->arg_count >= JS_MAX_LOCAL_VARS) {+ JS_ThrowInternalError(ctx, "too many arguments");+ return -1;+ }+ if (js_resize_array(ctx, (void **)&fd->args, sizeof(fd->args[0]),+ &fd->arg_size, fd->arg_count + 1))+ return -1;+ vd = &fd->args[fd->arg_count++];+ memset(vd, 0, sizeof(*vd));+ vd->var_name = JS_DupAtom(ctx, name);+ vd->func_pool_idx = -1;+ return fd->arg_count - 1;+}++/* add a global variable definition */+static JSGlobalVar *add_global_var(JSContext *ctx, JSFunctionDef *s,+ JSAtom name)+{+ JSGlobalVar *hf;++ if (js_resize_array(ctx, (void **)&s->global_vars,+ sizeof(s->global_vars[0]),+ &s->global_var_size, s->global_var_count + 1))+ return NULL;+ hf = &s->global_vars[s->global_var_count++];+ hf->cpool_idx = -1;+ hf->force_init = FALSE;+ hf->is_lexical = FALSE;+ hf->is_const = FALSE;+ hf->scope_level = s->scope_level;+ hf->var_name = JS_DupAtom(ctx, name);+ return hf;+}++typedef enum {+ JS_VAR_DEF_WITH,+ JS_VAR_DEF_LET,+ JS_VAR_DEF_CONST,+ JS_VAR_DEF_FUNCTION_DECL, /* function declaration */+ JS_VAR_DEF_NEW_FUNCTION_DECL, /* async/generator function declaration */+ JS_VAR_DEF_CATCH,+ JS_VAR_DEF_VAR,+} JSVarDefEnum;++static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name,+ JSVarDefEnum var_def_type)+{+ JSContext *ctx = s->ctx;+ JSVarDef *vd;+ int idx;++ switch (var_def_type) {+ case JS_VAR_DEF_WITH:+ idx = add_scope_var(ctx, fd, name, JS_VAR_NORMAL);+ break;++ case JS_VAR_DEF_LET:+ case JS_VAR_DEF_CONST:+ case JS_VAR_DEF_FUNCTION_DECL:+ case JS_VAR_DEF_NEW_FUNCTION_DECL:+ idx = find_lexical_decl(ctx, fd, name, fd->scope_first, TRUE);+ if (idx >= 0) {+ if (idx < GLOBAL_VAR_OFFSET) {+ if (fd->vars[idx].scope_level == fd->scope_level) {+ /* same scope: in non strict mode, functions+ can be redefined (annex B.3.3.4). */+ if (!(!(fd->js_mode & JS_MODE_STRICT) &&+ var_def_type == JS_VAR_DEF_FUNCTION_DECL &&+ fd->vars[idx].var_kind == JS_VAR_FUNCTION_DECL)) {+ goto redef_lex_error;+ }+ } else if (fd->vars[idx].var_kind == JS_VAR_CATCH && (fd->vars[idx].scope_level + 2) == fd->scope_level) {+ goto redef_lex_error;+ }+ } else {+ if (fd->scope_level == fd->body_scope) {+ redef_lex_error:+ /* redefining a scoped var in the same scope: error */+ return js_parse_error(s, "invalid redefinition of lexical identifier");+ }+ }+ }+ if (var_def_type != JS_VAR_DEF_FUNCTION_DECL &&+ var_def_type != JS_VAR_DEF_NEW_FUNCTION_DECL &&+ fd->scope_level == fd->body_scope &&+ find_arg(ctx, fd, name) >= 0) {+ /* lexical variable redefines a parameter name */+ return js_parse_error(s, "invalid redefinition of parameter name");+ }++ if (find_var_in_child_scope(ctx, fd, name, fd->scope_level) >= 0) {+ return js_parse_error(s, "invalid redefinition of a variable");+ }+ + if (fd->is_global_var) {+ JSGlobalVar *hf;+ hf = find_global_var(fd, name);+ if (hf && is_child_scope(ctx, fd, hf->scope_level,+ fd->scope_level)) {+ return js_parse_error(s, "invalid redefinition of global identifier");+ }+ }+ + if (fd->is_eval &&+ (fd->eval_type == JS_EVAL_TYPE_GLOBAL ||+ fd->eval_type == JS_EVAL_TYPE_MODULE) &&+ fd->scope_level == fd->body_scope) {+ JSGlobalVar *hf;+ hf = add_global_var(s->ctx, fd, name);+ if (!hf)+ return -1;+ hf->is_lexical = TRUE;+ hf->is_const = (var_def_type == JS_VAR_DEF_CONST);+ idx = GLOBAL_VAR_OFFSET;+ } else {+ JSVarKindEnum var_kind;+ if (var_def_type == JS_VAR_DEF_FUNCTION_DECL)+ var_kind = JS_VAR_FUNCTION_DECL;+ else if (var_def_type == JS_VAR_DEF_NEW_FUNCTION_DECL)+ var_kind = JS_VAR_NEW_FUNCTION_DECL;+ else+ var_kind = JS_VAR_NORMAL;+ idx = add_scope_var(ctx, fd, name, var_kind);+ if (idx >= 0) {+ vd = &fd->vars[idx];+ vd->is_lexical = 1;+ vd->is_const = (var_def_type == JS_VAR_DEF_CONST);+ }+ }+ break;++ case JS_VAR_DEF_CATCH:+ idx = add_scope_var(ctx, fd, name, JS_VAR_CATCH);+ break;++ case JS_VAR_DEF_VAR:+ if (find_lexical_decl(ctx, fd, name, fd->scope_first,+ FALSE) >= 0) {+ invalid_lexical_redefinition:+ /* error to redefine a var that inside a lexical scope */+ return js_parse_error(s, "invalid redefinition of lexical identifier");+ }+ if (fd->is_global_var) {+ JSGlobalVar *hf;+ hf = find_global_var(fd, name);+ if (hf && hf->is_lexical && hf->scope_level == fd->scope_level &&+ fd->eval_type == JS_EVAL_TYPE_MODULE) {+ goto invalid_lexical_redefinition;+ }+ hf = add_global_var(s->ctx, fd, name);+ if (!hf)+ return -1;+ idx = GLOBAL_VAR_OFFSET;+ } else {+ /* if the variable already exists, don't add it again */+ idx = find_var(ctx, fd, name);+ if (idx >= 0)+ break;+ idx = add_var(ctx, fd, name);+ if (idx >= 0) {+ if (name == JS_ATOM_arguments && fd->has_arguments_binding)+ fd->arguments_var_idx = idx;+ fd->vars[idx].scope_next = fd->scope_level;+ }+ }+ break;+ default:+ abort();+ }+ return idx;+}++/* add a private field variable in the current scope */+static int add_private_class_field(JSParseState *s, JSFunctionDef *fd,+ JSAtom name, JSVarKindEnum var_kind)+{+ JSContext *ctx = s->ctx;+ JSVarDef *vd;+ int idx;++ idx = add_scope_var(ctx, fd, name, var_kind);+ if (idx < 0)+ return idx;+ vd = &fd->vars[idx];+ vd->is_lexical = 1;+ vd->is_const = 1;+ return idx;+}++static __exception int js_parse_expr(JSParseState *s);+static __exception int js_parse_function_decl(JSParseState *s,+ JSParseFunctionEnum func_type,+ JSFunctionKindEnum func_kind,+ JSAtom func_name, const uint8_t *ptr,+ int start_line);+static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s);+static __exception int js_parse_function_decl2(JSParseState *s,+ JSParseFunctionEnum func_type,+ JSFunctionKindEnum func_kind,+ JSAtom func_name,+ const uint8_t *ptr,+ int function_line_num,+ JSParseExportEnum export_flag,+ JSFunctionDef **pfd);+static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags);+static __exception int js_parse_assign_expr(JSParseState *s);+static __exception int js_parse_unary(JSParseState *s, int parse_flags);+static void push_break_entry(JSFunctionDef *fd, BlockEnv *be,+ JSAtom label_name,+ int label_break, int label_cont,+ int drop_count);+static void pop_break_entry(JSFunctionDef *fd);+static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m,+ JSAtom local_name, JSAtom export_name,+ JSExportTypeEnum export_type);++/* Note: all the fields are already sealed except length */+static int seal_template_obj(JSContext *ctx, JSValueConst obj)+{+ JSObject *p;+ JSShapeProperty *prs;++ p = JS_VALUE_GET_OBJ(obj);+ prs = find_own_property1(p, JS_ATOM_length);+ if (prs) {+ if (js_update_property_flags(ctx, p, &prs,+ prs->flags & ~(JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)))+ return -1;+ }+ p->extensible = FALSE;+ return 0;+}++static __exception int js_parse_template(JSParseState *s, int call, int *argc)+{+ JSContext *ctx = s->ctx;+ JSValue raw_array, template_object;+ JSToken cooked;+ int depth, ret;++ raw_array = JS_UNDEFINED; /* avoid warning */+ template_object = JS_UNDEFINED; /* avoid warning */+ if (call) {+ /* Create a template object: an array of cooked strings */+ /* Create an array of raw strings and store it to the raw property */+ template_object = JS_NewArray(ctx);+ if (JS_IsException(template_object))+ return -1;+ // pool_idx = s->cur_func->cpool_count;+ ret = emit_push_const(s, template_object, 0);+ JS_FreeValue(ctx, template_object);+ if (ret)+ return -1;+ raw_array = JS_NewArray(ctx);+ if (JS_IsException(raw_array))+ return -1;+ if (JS_DefinePropertyValue(ctx, template_object, JS_ATOM_raw,+ raw_array, JS_PROP_THROW) < 0) {+ return -1;+ }+ }++ depth = 0;+ while (s->token.val == TOK_TEMPLATE) {+ const uint8_t *p = s->token.ptr + 1;+ cooked = s->token;+ if (call) {+ if (JS_DefinePropertyValueUint32(ctx, raw_array, depth,+ JS_DupValue(ctx, s->token.u.str.str),+ JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) {+ return -1;+ }+ /* re-parse the string with escape sequences but do not throw a+ syntax error if it contains invalid sequences+ */+ if (js_parse_string(s, '`', FALSE, p, &cooked, &p)) {+ cooked.u.str.str = JS_UNDEFINED;+ }+ if (JS_DefinePropertyValueUint32(ctx, template_object, depth,+ cooked.u.str.str,+ JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) {+ return -1;+ }+ } else {+ JSString *str;+ /* re-parse the string with escape sequences and throw a+ syntax error if it contains invalid sequences+ */+ JS_FreeValue(ctx, s->token.u.str.str);+ s->token.u.str.str = JS_UNDEFINED;+ if (js_parse_string(s, '`', TRUE, p, &cooked, &p))+ return -1;+ str = JS_VALUE_GET_STRING(cooked.u.str.str);+ if (str->len != 0 || depth == 0) {+ ret = emit_push_const(s, cooked.u.str.str, 1);+ JS_FreeValue(s->ctx, cooked.u.str.str);+ if (ret)+ return -1;+ if (depth == 0) {+ if (s->token.u.str.sep == '`')+ goto done1;+ emit_op(s, OP_get_field2);+ emit_atom(s, JS_ATOM_concat);+ }+ depth++;+ } else {+ JS_FreeValue(s->ctx, cooked.u.str.str);+ }+ }+ if (s->token.u.str.sep == '`')+ goto done;+ if (next_token(s))+ return -1;+ if (js_parse_expr(s))+ return -1;+ depth++;+ if (s->token.val != '}') {+ return js_parse_error(s, "expected '}' after template expression");+ }+ /* XXX: should convert to string at this stage? */+ free_token(s, &s->token);+ /* Resume TOK_TEMPLATE parsing (s->token.line_num and+ * s->token.ptr are OK) */+ s->got_lf = FALSE;+ s->last_line_num = s->token.line_num;+ if (js_parse_template_part(s, s->buf_ptr))+ return -1;+ }+ return js_parse_expect(s, TOK_TEMPLATE);++ done:+ if (call) {+ /* Seal the objects */+ seal_template_obj(ctx, raw_array);+ seal_template_obj(ctx, template_object);+ *argc = depth + 1;+ } else {+ emit_op(s, OP_call_method);+ emit_u16(s, depth - 1);+ }+ done1:+ return next_token(s);+}+++#define PROP_TYPE_IDENT 0+#define PROP_TYPE_VAR 1+#define PROP_TYPE_GET 2+#define PROP_TYPE_SET 3+#define PROP_TYPE_STAR 4+#define PROP_TYPE_ASYNC 5+#define PROP_TYPE_ASYNC_STAR 6++#define PROP_TYPE_PRIVATE (1 << 4)++static BOOL token_is_ident(int tok)+{+ /* Accept keywords and reserved words as property names */+ return (tok == TOK_IDENT ||+ (tok >= TOK_FIRST_KEYWORD &&+ tok <= TOK_LAST_KEYWORD));+}++/* if the property is an expression, name = JS_ATOM_NULL */+static int __exception js_parse_property_name(JSParseState *s,+ JSAtom *pname,+ BOOL allow_method, BOOL allow_var,+ BOOL allow_private)+{+ int is_private = 0;+ BOOL is_non_reserved_ident;+ JSAtom name;+ int prop_type;+ + prop_type = PROP_TYPE_IDENT;+ if (allow_method) {+ if (token_is_pseudo_keyword(s, JS_ATOM_get)+ || token_is_pseudo_keyword(s, JS_ATOM_set)) {+ /* get x(), set x() */+ name = JS_DupAtom(s->ctx, s->token.u.ident.atom);+ if (next_token(s))+ goto fail1;+ if (s->token.val == ':' || s->token.val == ',' ||+ s->token.val == '}' || s->token.val == '(') {+ is_non_reserved_ident = TRUE;+ goto ident_found;+ }+ prop_type = PROP_TYPE_GET + (name == JS_ATOM_set);+ JS_FreeAtom(s->ctx, name);+ } else if (s->token.val == '*') {+ if (next_token(s))+ goto fail;+ prop_type = PROP_TYPE_STAR;+ } else if (token_is_pseudo_keyword(s, JS_ATOM_async) &&+ peek_token(s, TRUE) != '\n') {+ name = JS_DupAtom(s->ctx, s->token.u.ident.atom);+ if (next_token(s))+ goto fail1;+ if (s->token.val == ':' || s->token.val == ',' ||+ s->token.val == '}' || s->token.val == '(') {+ is_non_reserved_ident = TRUE;+ goto ident_found;+ }+ JS_FreeAtom(s->ctx, name);+ if (s->token.val == '*') {+ if (next_token(s))+ goto fail;+ prop_type = PROP_TYPE_ASYNC_STAR;+ } else {+ prop_type = PROP_TYPE_ASYNC;+ }+ }+ }++ if (token_is_ident(s->token.val)) {+ /* variable can only be a non-reserved identifier */+ is_non_reserved_ident =+ (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved);+ /* keywords and reserved words have a valid atom */+ name = JS_DupAtom(s->ctx, s->token.u.ident.atom);+ if (next_token(s))+ goto fail1;+ ident_found:+ if (is_non_reserved_ident &&+ prop_type == PROP_TYPE_IDENT && allow_var) {+ if (!(s->token.val == ':' ||+ (s->token.val == '(' && allow_method))) {+ prop_type = PROP_TYPE_VAR;+ }+ }+ } else if (s->token.val == TOK_STRING) {+ name = JS_ValueToAtom(s->ctx, s->token.u.str.str);+ if (name == JS_ATOM_NULL)+ goto fail;+ if (next_token(s))+ goto fail1;+ } else if (s->token.val == TOK_NUMBER) {+ JSValue val;+ val = s->token.u.num.val;+#ifdef CONFIG_BIGNUM+ if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {+ JSBigFloat *p = JS_VALUE_GET_PTR(val);+ val = s->ctx->rt->bigfloat_ops.+ mul_pow10_to_float64(s->ctx, &p->num,+ s->token.u.num.exponent);+ if (JS_IsException(val))+ goto fail;+ name = JS_ValueToAtom(s->ctx, val);+ JS_FreeValue(s->ctx, val);+ } else+#endif+ {+ name = JS_ValueToAtom(s->ctx, val);+ }+ if (name == JS_ATOM_NULL)+ goto fail;+ if (next_token(s))+ goto fail1;+ } else if (s->token.val == '[') {+ if (next_token(s))+ goto fail;+ if (js_parse_expr(s))+ goto fail;+ if (js_parse_expect(s, ']'))+ goto fail;+ name = JS_ATOM_NULL;+ } else if (s->token.val == TOK_PRIVATE_NAME && allow_private) {+ name = JS_DupAtom(s->ctx, s->token.u.ident.atom);+ if (next_token(s))+ goto fail1;+ is_private = PROP_TYPE_PRIVATE;+ } else {+ goto invalid_prop;+ }+ if (prop_type != PROP_TYPE_IDENT && prop_type != PROP_TYPE_VAR &&+ s->token.val != '(') {+ JS_FreeAtom(s->ctx, name);+ invalid_prop:+ js_parse_error(s, "invalid property name");+ goto fail;+ }+ *pname = name;+ return prop_type | is_private;+ fail1:+ JS_FreeAtom(s->ctx, name);+ fail:+ *pname = JS_ATOM_NULL;+ return -1;+}++typedef struct JSParsePos {+ int last_line_num;+ int line_num;+ BOOL got_lf;+ const uint8_t *ptr;+} JSParsePos;++static int js_parse_get_pos(JSParseState *s, JSParsePos *sp)+{+ sp->last_line_num = s->last_line_num;+ sp->line_num = s->token.line_num;+ sp->ptr = s->token.ptr;+ sp->got_lf = s->got_lf;+ return 0;+}++static __exception int js_parse_seek_token(JSParseState *s, const JSParsePos *sp)+{+ s->token.line_num = sp->last_line_num;+ s->line_num = sp->line_num;+ s->buf_ptr = sp->ptr;+ s->got_lf = sp->got_lf;+ return next_token(s);+}++/* return TRUE if a regexp literal is allowed after this token */+static BOOL is_regexp_allowed(int tok)+{+ switch (tok) {+ case TOK_NUMBER:+ case TOK_STRING:+ case TOK_REGEXP:+ case TOK_DEC:+ case TOK_INC:+ case TOK_NULL:+ case TOK_FALSE:+ case TOK_TRUE:+ case TOK_THIS:+ case ')':+ case ']':+ case '}': /* XXX: regexp may occur after */+ case TOK_IDENT:+ return FALSE;+ default:+ return TRUE;+ }+}++#define SKIP_HAS_SEMI (1 << 0)+#define SKIP_HAS_ELLIPSIS (1 << 1)+#define SKIP_HAS_ASSIGNMENT (1 << 2)++/* XXX: improve speed with early bailout */+/* XXX: no longer works if regexps are present. Could use previous+ regexp parsing heuristics to handle most cases */+static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_terminator)+{+ char state[256];+ size_t level = 0;+ JSParsePos pos;+ int last_tok, tok = TOK_EOF;+ int c, tok_len, bits = 0;++ /* protect from underflow */+ state[level++] = 0;++ js_parse_get_pos(s, &pos);+ last_tok = 0;+ for (;;) {+ switch(s->token.val) {+ case '(':+ case '[':+ case '{':+ if (level >= sizeof(state))+ goto done;+ state[level++] = s->token.val;+ break;+ case ')':+ if (state[--level] != '(')+ goto done;+ break;+ case ']':+ if (state[--level] != '[')+ goto done;+ break;+ case '}':+ c = state[--level];+ if (c == '`') {+ /* continue the parsing of the template */+ free_token(s, &s->token);+ /* Resume TOK_TEMPLATE parsing (s->token.line_num and+ * s->token.ptr are OK) */+ s->got_lf = FALSE;+ s->last_line_num = s->token.line_num;+ if (js_parse_template_part(s, s->buf_ptr))+ goto done;+ goto handle_template;+ } else if (c != '{') {+ goto done;+ }+ break;+ case TOK_TEMPLATE:+ handle_template:+ if (s->token.u.str.sep != '`') {+ /* '${' inside the template : closing '}' and continue+ parsing the template */+ if (level >= sizeof(state))+ goto done;+ state[level++] = '`';+ } + break;+ case TOK_EOF:+ goto done;+ case ';':+ if (level == 2) {+ bits |= SKIP_HAS_SEMI;+ }+ break;+ case TOK_ELLIPSIS:+ if (level == 2) {+ bits |= SKIP_HAS_ELLIPSIS;+ }+ break;+ case '=':+ bits |= SKIP_HAS_ASSIGNMENT;+ break;+ + case TOK_DIV_ASSIGN:+ tok_len = 2;+ goto parse_regexp;+ case '/':+ tok_len = 1;+ parse_regexp:+ if (is_regexp_allowed(last_tok)) {+ s->buf_ptr -= tok_len;+ if (js_parse_regexp(s)) {+ /* XXX: should clear the exception */+ goto done;+ }+ }+ break;+ }+ /* last_tok is only used to recognize regexps */+ if (s->token.val == TOK_IDENT &&+ (token_is_pseudo_keyword(s, JS_ATOM_of) ||+ token_is_pseudo_keyword(s, JS_ATOM_yield))) {+ last_tok = TOK_OF;+ } else {+ last_tok = s->token.val;+ }+ if (next_token(s)) {+ /* XXX: should clear the exception generated by next_token() */+ break;+ }+ if (level <= 1) {+ tok = s->token.val;+ if (token_is_pseudo_keyword(s, JS_ATOM_of))+ tok = TOK_OF;+ if (no_line_terminator && s->last_line_num != s->token.line_num)+ tok = '\n';+ break;+ }+ }+ done:+ if (pbits) {+ *pbits = bits;+ }+ if (js_parse_seek_token(s, &pos))+ return -1;+ return tok;+}++static void set_object_name(JSParseState *s, JSAtom name)+{+ JSFunctionDef *fd = s->cur_func;+ int opcode;++ opcode = get_prev_opcode(fd);+ if (opcode == OP_set_name) {+ /* XXX: should free atom after OP_set_name? */+ fd->byte_code.size = fd->last_opcode_pos;+ fd->last_opcode_pos = -1;+ emit_op(s, OP_set_name);+ emit_atom(s, name);+ } else if (opcode == OP_set_class_name) {+ int define_class_pos;+ JSAtom atom;+ define_class_pos = fd->last_opcode_pos + 1 -+ get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);+ assert(fd->byte_code.buf[define_class_pos] == OP_define_class);+ /* for consistency we free the previous atom which is+ JS_ATOM_empty_string */+ atom = get_u32(fd->byte_code.buf + define_class_pos + 1);+ JS_FreeAtom(s->ctx, atom);+ put_u32(fd->byte_code.buf + define_class_pos + 1,+ JS_DupAtom(s->ctx, name));+ fd->last_opcode_pos = -1;+ }+}++static void set_object_name_computed(JSParseState *s)+{+ JSFunctionDef *fd = s->cur_func;+ int opcode;++ opcode = get_prev_opcode(fd);+ if (opcode == OP_set_name) {+ /* XXX: should free atom after OP_set_name? */+ fd->byte_code.size = fd->last_opcode_pos;+ fd->last_opcode_pos = -1;+ emit_op(s, OP_set_name_computed);+ } else if (opcode == OP_set_class_name) {+ int define_class_pos;+ define_class_pos = fd->last_opcode_pos + 1 -+ get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);+ assert(fd->byte_code.buf[define_class_pos] == OP_define_class);+ fd->byte_code.buf[define_class_pos] = OP_define_class_computed;+ fd->last_opcode_pos = -1;+ }+}++static __exception int js_parse_object_literal(JSParseState *s)+{+ JSAtom name = JS_ATOM_NULL;+ const uint8_t *start_ptr;+ int start_line, prop_type;+ BOOL has_proto;++ if (next_token(s))+ goto fail;+ /* XXX: add an initial length that will be patched back */+ emit_op(s, OP_object);+ has_proto = FALSE;+ while (s->token.val != '}') {+ /* specific case for getter/setter */+ start_ptr = s->token.ptr;+ start_line = s->token.line_num;++ if (s->token.val == TOK_ELLIPSIS) {+ if (next_token(s))+ return -1;+ if (js_parse_assign_expr(s))+ return -1;+ emit_op(s, OP_null); /* dummy excludeList */+ emit_op(s, OP_copy_data_properties);+ emit_u8(s, 2 | (1 << 2) | (0 << 5));+ emit_op(s, OP_drop); /* pop excludeList */+ emit_op(s, OP_drop); /* pop src object */+ goto next;+ }++ prop_type = js_parse_property_name(s, &name, TRUE, TRUE, FALSE);+ if (prop_type < 0)+ goto fail;++ if (prop_type == PROP_TYPE_VAR) {+ /* shortcut for x: x */+ emit_op(s, OP_scope_get_var);+ emit_atom(s, name);+ emit_u16(s, s->cur_func->scope_level);+ emit_op(s, OP_define_field);+ emit_atom(s, name);+ } else if (s->token.val == '(') {+ BOOL is_getset = (prop_type == PROP_TYPE_GET ||+ prop_type == PROP_TYPE_SET);+ JSParseFunctionEnum func_type;+ JSFunctionKindEnum func_kind;+ int op_flags;++ func_kind = JS_FUNC_NORMAL;+ if (is_getset) {+ func_type = JS_PARSE_FUNC_GETTER + prop_type - PROP_TYPE_GET;+ } else {+ func_type = JS_PARSE_FUNC_METHOD;+ if (prop_type == PROP_TYPE_STAR)+ func_kind = JS_FUNC_GENERATOR;+ else if (prop_type == PROP_TYPE_ASYNC)+ func_kind = JS_FUNC_ASYNC;+ else if (prop_type == PROP_TYPE_ASYNC_STAR)+ func_kind = JS_FUNC_ASYNC_GENERATOR;+ }+ if (js_parse_function_decl(s, func_type, func_kind, JS_ATOM_NULL,+ start_ptr, start_line))+ goto fail;+ if (name == JS_ATOM_NULL) {+ emit_op(s, OP_define_method_computed);+ } else {+ emit_op(s, OP_define_method);+ emit_atom(s, name);+ }+ if (is_getset) {+ op_flags = OP_DEFINE_METHOD_GETTER ++ prop_type - PROP_TYPE_GET;+ } else {+ op_flags = OP_DEFINE_METHOD_METHOD;+ }+ emit_u8(s, op_flags | OP_DEFINE_METHOD_ENUMERABLE);+ } else {+ if (js_parse_expect(s, ':'))+ goto fail;+ if (js_parse_assign_expr(s))+ goto fail;+ if (name == JS_ATOM_NULL) {+ set_object_name_computed(s);+ emit_op(s, OP_define_array_el);+ emit_op(s, OP_drop);+ } else if (name == JS_ATOM___proto__) {+ if (has_proto) {+ js_parse_error(s, "duplicate __proto__ property name");+ goto fail;+ }+ emit_op(s, OP_set_proto);+ has_proto = TRUE;+ } else {+ set_object_name(s, name);+ emit_op(s, OP_define_field);+ emit_atom(s, name);+ }+ }+ JS_FreeAtom(s->ctx, name);+ next:+ name = JS_ATOM_NULL;+ if (s->token.val != ',')+ break;+ if (next_token(s))+ goto fail;+ }+ if (js_parse_expect(s, '}'))+ goto fail;+ return 0;+ fail:+ JS_FreeAtom(s->ctx, name);+ return -1;+}++/* allow the 'in' binary operator */+#define PF_IN_ACCEPTED (1 << 0) +/* allow function calls parsing in js_parse_postfix_expr() */+#define PF_POSTFIX_CALL (1 << 1) +/* allow arrow functions parsing in js_parse_postfix_expr() */+#define PF_ARROW_FUNC (1 << 2) +/* allow the exponentiation operator in js_parse_unary() */+#define PF_POW_ALLOWED (1 << 3) +/* forbid the exponentiation operator in js_parse_unary() */+#define PF_POW_FORBIDDEN (1 << 4) ++static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags);++static __exception int js_parse_left_hand_side_expr(JSParseState *s)+{+ return js_parse_postfix_expr(s, PF_POSTFIX_CALL);+}++/* XXX: is there is nicer solution ? */+static __exception int js_parse_class_default_ctor(JSParseState *s,+ BOOL has_super,+ JSFunctionDef **pfd)+{+ JSParsePos pos;+ const char *str;+ int ret, line_num;+ JSParseFunctionEnum func_type;+ const uint8_t *saved_buf_end;+ + js_parse_get_pos(s, &pos);+ if (has_super) {+ str = "(...a){super(...a);}";+ func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR;+ } else {+ str = "(){}";+ func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR;+ }+ line_num = s->token.line_num;+ saved_buf_end = s->buf_end;+ s->buf_ptr = (uint8_t *)str;+ s->buf_end = (uint8_t *)(str + strlen(str));+ ret = next_token(s);+ if (!ret) {+ ret = js_parse_function_decl2(s, func_type, JS_FUNC_NORMAL,+ JS_ATOM_NULL, (uint8_t *)str,+ line_num, JS_PARSE_EXPORT_NONE, pfd);+ }+ s->buf_end = saved_buf_end;+ ret |= js_parse_seek_token(s, &pos);+ return ret;+}++/* find field in the current scope */+static int find_private_class_field(JSContext *ctx, JSFunctionDef *fd,+ JSAtom name, int scope_level)+{+ int idx;+ idx = fd->scopes[scope_level].first;+ while (idx != -1) {+ if (fd->vars[idx].scope_level != scope_level)+ break;+ if (fd->vars[idx].var_name == name)+ return idx;+ idx = fd->vars[idx].scope_next;+ }+ return -1;+}++/* initialize the class fields, called by the constructor. Note:+ super() can be called in an arrow function, so <this> and+ <class_fields_init> can be variable references */+static void emit_class_field_init(JSParseState *s)+{+ int label_next;+ + emit_op(s, OP_scope_get_var);+ emit_atom(s, JS_ATOM_class_fields_init);+ emit_u16(s, s->cur_func->scope_level);++ /* no need to call the class field initializer if not defined */+ emit_op(s, OP_dup);+ label_next = emit_goto(s, OP_if_false, -1);+ + emit_op(s, OP_scope_get_var);+ emit_atom(s, JS_ATOM_this);+ emit_u16(s, 0);+ + emit_op(s, OP_swap);+ + emit_op(s, OP_call_method);+ emit_u16(s, 0);++ emit_label(s, label_next);+ emit_op(s, OP_drop);+}++/* build a private setter function name from the private getter name */+static JSAtom get_private_setter_name(JSContext *ctx, JSAtom name)+{+ return js_atom_concat_str(ctx, name, "<set>");+}++typedef struct {+ JSFunctionDef *fields_init_fd;+ int computed_fields_count;+ BOOL has_brand;+ int brand_push_pos;+} ClassFieldsDef;++static __exception int emit_class_init_start(JSParseState *s,+ ClassFieldsDef *cf)+{+ int label_add_brand;+ + cf->fields_init_fd = js_parse_function_class_fields_init(s);+ if (!cf->fields_init_fd)+ return -1;++ s->cur_func = cf->fields_init_fd;+ + /* XXX: would be better to add the code only if needed, maybe in a+ later pass */+ emit_op(s, OP_push_false); /* will be patched later */+ cf->brand_push_pos = cf->fields_init_fd->last_opcode_pos;+ label_add_brand = emit_goto(s, OP_if_false, -1);+ + emit_op(s, OP_scope_get_var);+ emit_atom(s, JS_ATOM_this);+ emit_u16(s, 0);+ + emit_op(s, OP_scope_get_var);+ emit_atom(s, JS_ATOM_home_object);+ emit_u16(s, 0);+ + emit_op(s, OP_add_brand);+ + emit_label(s, label_add_brand);++ s->cur_func = s->cur_func->parent;+ return 0;+}++static __exception int add_brand(JSParseState *s, ClassFieldsDef *cf)+{+ if (!cf->has_brand) {+ /* define the brand field in 'this' of the initializer */+ if (!cf->fields_init_fd) {+ if (emit_class_init_start(s, cf))+ return -1;+ }+ /* patch the start of the function to enable the OP_add_brand code */+ cf->fields_init_fd->byte_code.buf[cf->brand_push_pos] = OP_push_true;+ + cf->has_brand = TRUE;+ }+ return 0;+}++static void emit_class_init_end(JSParseState *s, ClassFieldsDef *cf)+{+ int cpool_idx;+ + s->cur_func = cf->fields_init_fd;+ emit_op(s, OP_return_undef);+ s->cur_func = s->cur_func->parent;+ + cpool_idx = cpool_add(s, JS_NULL);+ cf->fields_init_fd->parent_cpool_idx = cpool_idx;+ emit_op(s, OP_fclosure);+ emit_u32(s, cpool_idx);+ emit_op(s, OP_set_home_object);+}+++static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,+ JSParseExportEnum export_flag)+{+ JSContext *ctx = s->ctx;+ JSFunctionDef *fd = s->cur_func;+ JSAtom name = JS_ATOM_NULL, class_name = JS_ATOM_NULL, class_name1;+ JSAtom class_var_name = JS_ATOM_NULL;+ JSFunctionDef *method_fd, *ctor_fd;+ int saved_js_mode, class_name_var_idx, prop_type, ctor_cpool_offset;+ int class_flags = 0, i, define_class_offset;+ BOOL is_static, is_private;+ const uint8_t *class_start_ptr = s->token.ptr;+ const uint8_t *start_ptr;+ ClassFieldsDef class_fields[2];+ + /* classes are parsed and executed in strict mode */+ saved_js_mode = fd->js_mode;+ fd->js_mode |= JS_MODE_STRICT;+ if (next_token(s))+ goto fail;+ if (s->token.val == TOK_IDENT) {+ if (s->token.u.ident.is_reserved) {+ js_parse_error_reserved_identifier(s);+ goto fail;+ }+ class_name = JS_DupAtom(ctx, s->token.u.ident.atom);+ if (next_token(s))+ goto fail;+ } else if (!is_class_expr && export_flag != JS_PARSE_EXPORT_DEFAULT) {+ js_parse_error(s, "class statement requires a name");+ goto fail;+ }+ if (!is_class_expr) {+ if (class_name == JS_ATOM_NULL)+ class_var_name = JS_ATOM__default_; /* export default */+ else+ class_var_name = class_name;+ class_var_name = JS_DupAtom(ctx, class_var_name);+ }++ push_scope(s);++ if (s->token.val == TOK_EXTENDS) {+ class_flags = JS_DEFINE_CLASS_HAS_HERITAGE;+ if (next_token(s))+ goto fail;+ if (js_parse_left_hand_side_expr(s))+ goto fail;+ } else {+ emit_op(s, OP_undefined);+ }++ /* add a 'const' definition for the class name */+ if (class_name != JS_ATOM_NULL) {+ class_name_var_idx = define_var(s, fd, class_name, JS_VAR_DEF_CONST);+ if (class_name_var_idx < 0)+ goto fail;+ }++ if (js_parse_expect(s, '{'))+ goto fail;++ /* this scope contains the private fields */+ push_scope(s);++ emit_op(s, OP_push_const);+ ctor_cpool_offset = fd->byte_code.size;+ emit_u32(s, 0); /* will be patched at the end of the class parsing */++ if (class_name == JS_ATOM_NULL) {+ if (class_var_name != JS_ATOM_NULL)+ class_name1 = JS_ATOM_default;+ else+ class_name1 = JS_ATOM_empty_string;+ } else {+ class_name1 = class_name;+ }+ + emit_op(s, OP_define_class);+ emit_atom(s, class_name1);+ emit_u8(s, class_flags);+ define_class_offset = fd->last_opcode_pos;++ for(i = 0; i < 2; i++) {+ ClassFieldsDef *cf = &class_fields[i];+ cf->fields_init_fd = NULL;+ cf->computed_fields_count = 0;+ cf->has_brand = FALSE;+ }+ + ctor_fd = NULL;+ while (s->token.val != '}') {+ if (s->token.val == ';') {+ if (next_token(s))+ goto fail;+ continue;+ }+ is_static = (s->token.val == TOK_STATIC);+ prop_type = -1;+ if (is_static) {+ if (next_token(s))+ goto fail;+ /* allow "static" field name */+ if (s->token.val == ';' || s->token.val == '=') {+ is_static = FALSE;+ name = JS_DupAtom(ctx, JS_ATOM_static);+ prop_type = PROP_TYPE_IDENT;+ }+ }+ if (is_static)+ emit_op(s, OP_swap);+ start_ptr = s->token.ptr;+ if (prop_type < 0) {+ prop_type = js_parse_property_name(s, &name, TRUE, FALSE, TRUE);+ if (prop_type < 0)+ goto fail;+ }+ is_private = prop_type & PROP_TYPE_PRIVATE;+ prop_type &= ~PROP_TYPE_PRIVATE;+ + if ((name == JS_ATOM_constructor && !is_static &&+ prop_type != PROP_TYPE_IDENT) ||+ (name == JS_ATOM_prototype && is_static) ||+ name == JS_ATOM_hash_constructor) {+ js_parse_error(s, "invalid method name");+ goto fail;+ }+ if (prop_type == PROP_TYPE_GET || prop_type == PROP_TYPE_SET) {+ BOOL is_set = prop_type - PROP_TYPE_GET;+ JSFunctionDef *method_fd;++ if (is_private) {+ int idx, var_kind;+ idx = find_private_class_field(ctx, fd, name, fd->scope_level);+ if (idx >= 0) {+ var_kind = fd->vars[idx].var_kind;+ if (var_kind == JS_VAR_PRIVATE_FIELD ||+ var_kind == JS_VAR_PRIVATE_METHOD ||+ var_kind == JS_VAR_PRIVATE_GETTER_SETTER ||+ var_kind == (JS_VAR_PRIVATE_GETTER + is_set)) {+ goto private_field_already_defined;+ }+ fd->vars[idx].var_kind = JS_VAR_PRIVATE_GETTER_SETTER;+ } else {+ if (add_private_class_field(s, fd, name,+ JS_VAR_PRIVATE_GETTER + is_set) < 0)+ goto fail;+ }+ if (add_brand(s, &class_fields[is_static]) < 0)+ goto fail;+ }++ if (js_parse_function_decl2(s, JS_PARSE_FUNC_GETTER + is_set,+ JS_FUNC_NORMAL, JS_ATOM_NULL,+ start_ptr, s->token.line_num,+ JS_PARSE_EXPORT_NONE, &method_fd))+ goto fail;+ if (is_private) {+ method_fd->need_home_object = TRUE; /* needed for brand check */+ emit_op(s, OP_set_home_object);+ /* XXX: missing function name */+ emit_op(s, OP_scope_put_var_init);+ if (is_set) {+ JSAtom setter_name;+ int ret;+ + setter_name = get_private_setter_name(ctx, name);+ if (setter_name == JS_ATOM_NULL)+ goto fail;+ emit_atom(s, setter_name);+ ret = add_private_class_field(s, fd, setter_name,+ JS_VAR_PRIVATE_SETTER);+ JS_FreeAtom(ctx, setter_name);+ if (ret < 0)+ goto fail;+ } else {+ emit_atom(s, name);+ }+ emit_u16(s, s->cur_func->scope_level);+ } else {+ if (name == JS_ATOM_NULL) {+ emit_op(s, OP_define_method_computed);+ } else {+ emit_op(s, OP_define_method);+ emit_atom(s, name);+ }+ emit_u8(s, OP_DEFINE_METHOD_GETTER + is_set);+ }+ } else if (prop_type == PROP_TYPE_IDENT && s->token.val != '(') {+ ClassFieldsDef *cf = &class_fields[is_static];+ JSAtom field_var_name = JS_ATOM_NULL;+ + /* class field */++ /* XXX: spec: not consistent with method name checks */+ if (name == JS_ATOM_constructor || name == JS_ATOM_prototype) {+ js_parse_error(s, "invalid field name");+ goto fail;+ }+ + if (is_private) {+ if (find_private_class_field(ctx, fd, name,+ fd->scope_level) >= 0) {+ goto private_field_already_defined;+ }+ if (add_private_class_field(s, fd, name,+ JS_VAR_PRIVATE_FIELD) < 0)+ goto fail;+ emit_op(s, OP_private_symbol);+ emit_atom(s, name);+ emit_op(s, OP_scope_put_var_init);+ emit_atom(s, name);+ emit_u16(s, s->cur_func->scope_level);+ }++ if (!cf->fields_init_fd) {+ if (emit_class_init_start(s, cf))+ goto fail;+ }+ if (name == JS_ATOM_NULL ) {+ /* save the computed field name into a variable */+ field_var_name = js_atom_concat_num(ctx, JS_ATOM_computed_field + is_static, cf->computed_fields_count);+ if (field_var_name == JS_ATOM_NULL)+ goto fail;+ if (define_var(s, fd, field_var_name, JS_VAR_DEF_CONST) < 0) {+ JS_FreeAtom(ctx, field_var_name);+ goto fail;+ }+ emit_op(s, OP_to_propkey);+ emit_op(s, OP_scope_put_var_init);+ emit_atom(s, field_var_name);+ emit_u16(s, s->cur_func->scope_level);+ }+ s->cur_func = cf->fields_init_fd;+ emit_op(s, OP_scope_get_var);+ emit_atom(s, JS_ATOM_this);+ emit_u16(s, 0);++ if (name == JS_ATOM_NULL) {+ emit_op(s, OP_scope_get_var);+ emit_atom(s, field_var_name);+ emit_u16(s, s->cur_func->scope_level);+ cf->computed_fields_count++;+ JS_FreeAtom(ctx, field_var_name);+ } else if (is_private) {+ emit_op(s, OP_scope_get_var);+ emit_atom(s, name);+ emit_u16(s, s->cur_func->scope_level);+ }+ + if (s->token.val == '=') {+ if (next_token(s))+ goto fail;+ if (js_parse_assign_expr(s))+ goto fail;+ } else {+ emit_op(s, OP_undefined);+ }+ if (is_private) {+ set_object_name_computed(s);+ emit_op(s, OP_define_private_field);+ } else if (name == JS_ATOM_NULL) {+ set_object_name_computed(s);+ emit_op(s, OP_define_array_el);+ emit_op(s, OP_drop);+ } else {+ set_object_name(s, name);+ emit_op(s, OP_define_field);+ emit_atom(s, name);+ }+ s->cur_func = s->cur_func->parent;+ if (js_parse_expect_semi(s))+ goto fail;+ } else {+ JSParseFunctionEnum func_type;+ JSFunctionKindEnum func_kind;+ + func_type = JS_PARSE_FUNC_METHOD;+ func_kind = JS_FUNC_NORMAL;+ if (prop_type == PROP_TYPE_STAR) {+ func_kind = JS_FUNC_GENERATOR;+ } else if (prop_type == PROP_TYPE_ASYNC) {+ func_kind = JS_FUNC_ASYNC;+ } else if (prop_type == PROP_TYPE_ASYNC_STAR) {+ func_kind = JS_FUNC_ASYNC_GENERATOR;+ } else if (name == JS_ATOM_constructor && !is_static) {+ if (ctor_fd) {+ js_parse_error(s, "property constructor appears more than once");+ goto fail;+ }+ if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE)+ func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR;+ else+ func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR;+ }+ if (is_private) {+ if (add_brand(s, &class_fields[is_static]) < 0)+ goto fail;+ }+ 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))+ goto fail;+ if (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR ||+ func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) {+ ctor_fd = method_fd;+ } else if (is_private) {+ method_fd->need_home_object = TRUE; /* needed for brand check */+ if (find_private_class_field(ctx, fd, name,+ fd->scope_level) >= 0) {+ private_field_already_defined:+ js_parse_error(s, "private class field is already defined");+ goto fail;+ }+ if (add_private_class_field(s, fd, name,+ JS_VAR_PRIVATE_METHOD) < 0)+ goto fail;+ emit_op(s, OP_set_home_object);+ emit_op(s, OP_set_name);+ emit_atom(s, name);+ emit_op(s, OP_scope_put_var_init);+ emit_atom(s, name);+ emit_u16(s, s->cur_func->scope_level);+ } else {+ if (name == JS_ATOM_NULL) {+ emit_op(s, OP_define_method_computed);+ } else {+ emit_op(s, OP_define_method);+ emit_atom(s, name);+ }+ emit_u8(s, OP_DEFINE_METHOD_METHOD);+ }+ }+ if (is_static)+ emit_op(s, OP_swap);+ JS_FreeAtom(ctx, name);+ name = JS_ATOM_NULL;+ }++ if (s->token.val != '}') {+ js_parse_error(s, "expecting '%c'", '}');+ goto fail;+ }++ if (!ctor_fd) {+ if (js_parse_class_default_ctor(s, class_flags & JS_DEFINE_CLASS_HAS_HERITAGE, &ctor_fd))+ goto fail;+ }+ /* patch the constant pool index for the constructor */+ put_u32(fd->byte_code.buf + ctor_cpool_offset, ctor_fd->parent_cpool_idx);++ /* store the class source code in the constructor. */+ if (!(fd->js_mode & JS_MODE_STRIP)) {+ js_free(ctx, ctor_fd->source);+ ctor_fd->source_len = s->buf_ptr - class_start_ptr;+ ctor_fd->source = js_strndup(ctx, (const char *)class_start_ptr,+ ctor_fd->source_len);+ if (!ctor_fd->source)+ goto fail;+ }++ /* consume the '}' */+ if (next_token(s))+ goto fail;++ /* store the function to initialize the fields to that it can be+ referenced by the constructor */+ {+ ClassFieldsDef *cf = &class_fields[0];+ int var_idx;+ + var_idx = define_var(s, fd, JS_ATOM_class_fields_init,+ JS_VAR_DEF_CONST);+ if (var_idx < 0)+ goto fail;+ if (cf->fields_init_fd) {+ emit_class_init_end(s, cf);+ } else {+ emit_op(s, OP_undefined);+ }+ emit_op(s, OP_scope_put_var_init);+ emit_atom(s, JS_ATOM_class_fields_init);+ emit_u16(s, s->cur_func->scope_level);+ }++ /* drop the prototype */+ emit_op(s, OP_drop);++ /* initialize the static fields */+ if (class_fields[1].fields_init_fd != NULL) {+ ClassFieldsDef *cf = &class_fields[1];+ emit_op(s, OP_dup);+ emit_class_init_end(s, cf);+ emit_op(s, OP_call_method);+ emit_u16(s, 0);+ emit_op(s, OP_drop);+ }+ + if (class_name != JS_ATOM_NULL) {+ /* store the class name in the scoped class name variable (it+ is independent from the class statement variable+ definition) */+ emit_op(s, OP_dup);+ emit_op(s, OP_scope_put_var_init);+ emit_atom(s, class_name);+ emit_u16(s, fd->scope_level);+ }+ pop_scope(s);+ pop_scope(s);++ /* the class statements have a block level scope */+ if (class_var_name != JS_ATOM_NULL) {+ if (define_var(s, fd, class_var_name, JS_VAR_DEF_LET) < 0)+ goto fail;+ emit_op(s, OP_scope_put_var_init);+ emit_atom(s, class_var_name);+ emit_u16(s, fd->scope_level);+ } else {+ if (class_name == JS_ATOM_NULL) {+ /* cannot use OP_set_name because the name of the class+ must be defined before the static initializers are+ executed */+ emit_op(s, OP_set_class_name);+ emit_u32(s, fd->last_opcode_pos + 1 - define_class_offset);+ }+ }++ if (export_flag != JS_PARSE_EXPORT_NONE) {+ if (!add_export_entry(s, fd->module,+ class_var_name,+ export_flag == JS_PARSE_EXPORT_NAMED ? class_var_name : JS_ATOM_default,+ JS_EXPORT_TYPE_LOCAL))+ goto fail;+ }++ JS_FreeAtom(ctx, class_name);+ JS_FreeAtom(ctx, class_var_name);+ fd->js_mode = saved_js_mode;+ return 0;+ fail:+ JS_FreeAtom(ctx, name);+ JS_FreeAtom(ctx, class_name);+ JS_FreeAtom(ctx, class_var_name);+ fd->js_mode = saved_js_mode;+ return -1;+}++static __exception int js_parse_array_literal(JSParseState *s)+{+ uint32_t idx;+ BOOL need_length;++ if (next_token(s))+ return -1;+ /* small regular arrays are created on the stack */+ idx = 0;+ while (s->token.val != ']' && idx < 32) {+ if (s->token.val == ',' || s->token.val == TOK_ELLIPSIS)+ break;+ if (js_parse_assign_expr(s))+ return -1;+ idx++;+ /* accept trailing comma */+ if (s->token.val == ',') {+ if (next_token(s))+ return -1;+ } else+ if (s->token.val != ']')+ goto done;+ }+ emit_op(s, OP_array_from);+ emit_u16(s, idx);++ /* larger arrays and holes are handled with explicit indices */+ need_length = FALSE;+ while (s->token.val != ']' && idx < 0x7fffffff) {+ if (s->token.val == TOK_ELLIPSIS)+ break;+ need_length = TRUE;+ if (s->token.val != ',') {+ if (js_parse_assign_expr(s))+ return -1;+ emit_op(s, OP_define_field);+ emit_u32(s, __JS_AtomFromUInt32(idx));+ need_length = FALSE;+ }+ idx++;+ /* accept trailing comma */+ if (s->token.val == ',') {+ if (next_token(s))+ return -1;+ }+ }+ if (s->token.val == ']') {+ if (need_length) {+ /* Set the length: Cannot use OP_define_field because+ length is not configurable */+ emit_op(s, OP_dup);+ emit_op(s, OP_push_i32);+ emit_u32(s, idx);+ emit_op(s, OP_put_field);+ emit_atom(s, JS_ATOM_length);+ }+ goto done;+ }++ /* huge arrays and spread elements require a dynamic index on the stack */+ emit_op(s, OP_push_i32);+ emit_u32(s, idx);++ /* stack has array, index */+ while (s->token.val != ']') {+ if (s->token.val == TOK_ELLIPSIS) {+ if (next_token(s))+ return -1;+ if (js_parse_assign_expr(s))+ return -1;+#if 1+ emit_op(s, OP_append);+#else+ int label_next, label_done;+ label_next = new_label(s);+ label_done = new_label(s);+ /* enumerate object */+ emit_op(s, OP_for_of_start);+ emit_op(s, OP_rot5l);+ emit_op(s, OP_rot5l);+ emit_label(s, label_next);+ /* on stack: enum_rec array idx */+ emit_op(s, OP_for_of_next);+ emit_u8(s, 2);+ emit_goto(s, OP_if_true, label_done);+ /* append element */+ /* enum_rec array idx val -> enum_rec array new_idx */+ emit_op(s, OP_define_array_el);+ emit_op(s, OP_inc);+ emit_goto(s, OP_goto, label_next);+ emit_label(s, label_done);+ /* close enumeration */+ emit_op(s, OP_drop); /* drop undef val */+ emit_op(s, OP_nip1); /* drop enum_rec */+ emit_op(s, OP_nip1);+ emit_op(s, OP_nip1);+#endif+ } else {+ need_length = TRUE;+ if (s->token.val != ',') {+ if (js_parse_assign_expr(s))+ return -1;+ /* a idx val */+ emit_op(s, OP_define_array_el);+ need_length = FALSE;+ }+ emit_op(s, OP_inc);+ }+ if (s->token.val != ',')+ break;+ if (next_token(s))+ return -1;+ }+ if (need_length) {+ /* Set the length: cannot use OP_define_field because+ length is not configurable */+ emit_op(s, OP_dup1); /* array length - array array length */+ emit_op(s, OP_put_field);+ emit_atom(s, JS_ATOM_length);+ } else {+ emit_op(s, OP_drop); /* array length - array */+ }+done:+ return js_parse_expect(s, ']');+}++/* XXX: remove */+static BOOL has_with_scope(JSFunctionDef *s, int scope_level)+{+ /* check if scope chain contains a with statement */+ while (s) {+ int scope_idx = s->scopes[scope_level].first;+ while (scope_idx >= 0) {+ JSVarDef *vd = &s->vars[scope_idx];++ if (vd->var_name == JS_ATOM__with_)+ return TRUE;+ scope_idx = vd->scope_next;+ }+ /* check parent scopes */+ scope_level = s->parent_scope_level;+ s = s->parent;+ }+ return FALSE;+}++static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope,+ JSAtom *pname, int *plabel, int *pdepth, BOOL keep,+ int tok)+{+ JSFunctionDef *fd;+ int opcode, scope, label, depth;+ JSAtom name;++ /* we check the last opcode to get the lvalue type */+ fd = s->cur_func;+ scope = 0;+ name = JS_ATOM_NULL;+ label = -1;+ depth = 0;+ switch(opcode = get_prev_opcode(fd)) {+ case OP_scope_get_var:+ name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);+ scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);+ if ((name == JS_ATOM_arguments || name == JS_ATOM_eval) &&+ (fd->js_mode & JS_MODE_STRICT)) {+ return js_parse_error(s, "invalid lvalue in strict mode");+ }+ if (name == JS_ATOM_this || name == JS_ATOM_new_target)+ goto invalid_lvalue;+ depth = 2; /* will generate OP_get_ref_value */+ break;+ case OP_get_field:+ name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);+ depth = 1;+ break;+ case OP_scope_get_private_field:+ name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);+ scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);+ depth = 1;+ break;+ case OP_get_array_el:+ depth = 2;+ break;+ case OP_get_super_value:+ depth = 3;+ break;+ default:+ invalid_lvalue:+ if (tok == TOK_FOR) {+ return js_parse_error(s, "invalid for in/of left hand-side");+ } else if (tok == TOK_INC || tok == TOK_DEC) {+ return js_parse_error(s, "invalid increment/decrement operand");+ } else if (tok == '[' || tok == '{') {+ return js_parse_error(s, "invalid destructuring target");+ } else {+ return js_parse_error(s, "invalid assignment left-hand side");+ }+ }+ /* remove the last opcode */+ fd->byte_code.size = fd->last_opcode_pos;+ fd->last_opcode_pos = -1;++ if (keep) {+ /* get the value but keep the object/fields on the stack */+ switch(opcode) {+ case OP_scope_get_var:+ label = new_label(s);+ emit_op(s, OP_scope_make_ref);+ emit_atom(s, name);+ emit_u32(s, label);+ emit_u16(s, scope);+ update_label(fd, label, 1);+ emit_op(s, OP_get_ref_value);+ opcode = OP_get_ref_value;+ break;+ case OP_get_field:+ emit_op(s, OP_get_field2);+ emit_atom(s, name);+ break;+ case OP_scope_get_private_field:+ emit_op(s, OP_scope_get_private_field2);+ emit_atom(s, name);+ emit_u16(s, scope);+ break;+ case OP_get_array_el:+ /* XXX: replace by a single opcode ? */+ emit_op(s, OP_to_propkey2);+ emit_op(s, OP_dup2);+ emit_op(s, OP_get_array_el);+ break;+ case OP_get_super_value:+ emit_op(s, OP_to_propkey);+ emit_op(s, OP_dup3);+ emit_op(s, OP_get_super_value);+ break;+ default:+ abort();+ }+ } else {+ switch(opcode) {+ case OP_scope_get_var:+ label = new_label(s);+ emit_op(s, OP_scope_make_ref);+ emit_atom(s, name);+ emit_u32(s, label);+ emit_u16(s, scope);+ update_label(fd, label, 1);+ opcode = OP_get_ref_value;+ break;+ case OP_get_array_el:+ emit_op(s, OP_to_propkey2);+ break;+ case OP_get_super_value:+ emit_op(s, OP_to_propkey);+ break;+ }+ }++ *popcode = opcode;+ *pscope = scope;+ /* name has refcount for OP_get_field and OP_get_ref_value,+ and JS_ATOM_NULL for other opcodes */+ *pname = name;+ *plabel = label;+ if (pdepth)+ *pdepth = depth;+ return 0;+}++typedef enum {+ PUT_LVALUE_NOKEEP, /* [depth] v -> */+ PUT_LVALUE_NOKEEP_DEPTH, /* [depth] v -> , keep depth (currently+ just disable optimizations) */+ PUT_LVALUE_KEEP_TOP, /* [depth] v -> v */+ PUT_LVALUE_KEEP_SECOND, /* [depth] v0 v -> v0 */+ PUT_LVALUE_NOKEEP_BOTTOM, /* v [depth] -> */+} PutLValueEnum;++/* name has a live reference. 'is_let' is only used with opcode =+ OP_scope_get_var which is never generated by get_lvalue(). */+static void put_lvalue(JSParseState *s, int opcode, int scope,+ JSAtom name, int label, PutLValueEnum special,+ BOOL is_let)+{+ switch(opcode) {+ case OP_get_field:+ case OP_scope_get_private_field:+ /* depth = 1 */+ switch(special) {+ case PUT_LVALUE_NOKEEP:+ case PUT_LVALUE_NOKEEP_DEPTH:+ break;+ case PUT_LVALUE_KEEP_TOP:+ emit_op(s, OP_insert2); /* obj v -> v obj v */+ break;+ case PUT_LVALUE_KEEP_SECOND:+ emit_op(s, OP_perm3); /* obj v0 v -> v0 obj v */+ break;+ case PUT_LVALUE_NOKEEP_BOTTOM:+ emit_op(s, OP_swap);+ break;+ default:+ abort();+ }+ break;+ case OP_get_array_el:+ case OP_get_ref_value:+ /* depth = 2 */+ if (opcode == OP_get_ref_value) {+ JS_FreeAtom(s->ctx, name);+ emit_label(s, label);+ }+ switch(special) {+ case PUT_LVALUE_NOKEEP:+ emit_op(s, OP_nop); /* will trigger optimization */+ break;+ case PUT_LVALUE_NOKEEP_DEPTH:+ break;+ case PUT_LVALUE_KEEP_TOP:+ emit_op(s, OP_insert3); /* obj prop v -> v obj prop v */+ break;+ case PUT_LVALUE_KEEP_SECOND:+ emit_op(s, OP_perm4); /* obj prop v0 v -> v0 obj prop v */+ break;+ case PUT_LVALUE_NOKEEP_BOTTOM:+ emit_op(s, OP_rot3l);+ break;+ default:+ abort();+ }+ break;+ case OP_get_super_value:+ /* depth = 3 */+ switch(special) {+ case PUT_LVALUE_NOKEEP:+ case PUT_LVALUE_NOKEEP_DEPTH:+ break;+ case PUT_LVALUE_KEEP_TOP:+ emit_op(s, OP_insert4); /* this obj prop v -> v this obj prop v */+ break;+ case PUT_LVALUE_KEEP_SECOND:+ emit_op(s, OP_perm5); /* this obj prop v0 v -> v0 this obj prop v */+ break;+ case PUT_LVALUE_NOKEEP_BOTTOM:+ emit_op(s, OP_rot4l);+ break;+ default:+ abort();+ }+ break;+ default:+ break;+ }+ + switch(opcode) {+ case OP_scope_get_var: /* val -- */+ assert(special == PUT_LVALUE_NOKEEP ||+ special == PUT_LVALUE_NOKEEP_DEPTH);+ emit_op(s, is_let ? OP_scope_put_var_init : OP_scope_put_var);+ emit_u32(s, name); /* has refcount */+ emit_u16(s, scope);+ break;+ case OP_get_field:+ emit_op(s, OP_put_field);+ emit_u32(s, name); /* name has refcount */+ break;+ case OP_scope_get_private_field:+ emit_op(s, OP_scope_put_private_field);+ emit_u32(s, name); /* name has refcount */+ emit_u16(s, scope);+ break;+ case OP_get_array_el:+ emit_op(s, OP_put_array_el);+ break;+ case OP_get_ref_value:+ emit_op(s, OP_put_ref_value);+ break;+ case OP_get_super_value:+ emit_op(s, OP_put_super_value);+ break;+ default:+ abort();+ }+}++static __exception int js_parse_expr_paren(JSParseState *s)+{+ if (js_parse_expect(s, '('))+ return -1;+ if (js_parse_expr(s))+ return -1;+ if (js_parse_expect(s, ')'))+ return -1;+ return 0;+}++static int js_unsupported_keyword(JSParseState *s, JSAtom atom)+{+ char buf[ATOM_GET_STR_BUF_SIZE];+ return js_parse_error(s, "unsupported keyword: %s",+ JS_AtomGetStr(s->ctx, buf, sizeof(buf), atom));+}++static __exception int js_define_var(JSParseState *s, JSAtom name, int tok)+{+ JSFunctionDef *fd = s->cur_func;+ JSVarDefEnum var_def_type;+ + if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) {+ return js_parse_error(s, "yield is a reserved identifier");+ }+ if ((name == JS_ATOM_arguments || name == JS_ATOM_eval)+ && (fd->js_mode & JS_MODE_STRICT)) {+ return js_parse_error(s, "invalid variable name in strict mode");+ }+ if ((name == JS_ATOM_let || name == JS_ATOM_undefined)+ && (tok == TOK_LET || tok == TOK_CONST)) {+ return js_parse_error(s, "invalid lexical variable name");+ }+ switch(tok) {+ case TOK_LET:+ var_def_type = JS_VAR_DEF_LET;+ break;+ case TOK_CONST:+ var_def_type = JS_VAR_DEF_CONST;+ break;+ case TOK_VAR:+ var_def_type = JS_VAR_DEF_VAR;+ break;+ case TOK_CATCH:+ var_def_type = JS_VAR_DEF_CATCH;+ break;+ default:+ abort();+ }+ if (define_var(s, fd, name, var_def_type) < 0)+ return -1;+ return 0;+}++static void js_emit_spread_code(JSParseState *s, int depth)+{+ int label_rest_next, label_rest_done;++ /* XXX: could check if enum object is an actual array and optimize+ slice extraction. enumeration record and target array are in a+ different order from OP_append case. */+ /* enum_rec xxx -- enum_rec xxx array 0 */+ emit_op(s, OP_array_from);+ emit_u16(s, 0);+ emit_op(s, OP_push_i32);+ emit_u32(s, 0);+ emit_label(s, label_rest_next = new_label(s));+ emit_op(s, OP_for_of_next);+ emit_u8(s, 2 + depth);+ label_rest_done = emit_goto(s, OP_if_true, -1);+ /* array idx val -- array idx */+ emit_op(s, OP_define_array_el);+ emit_op(s, OP_inc);+ emit_goto(s, OP_goto, label_rest_next);+ emit_label(s, label_rest_done);+ /* enum_rec xxx array idx undef -- enum_rec xxx array */+ emit_op(s, OP_drop);+ emit_op(s, OP_drop);+}++static int js_parse_check_duplicate_parameter(JSParseState *s, JSAtom name)+{+ /* Check for duplicate parameter names */+ JSFunctionDef *fd = s->cur_func;+ int i;+ for (i = 0; i < fd->arg_count; i++) {+ if (fd->args[i].var_name == name)+ goto duplicate;+ }+ for (i = 0; i < fd->var_count; i++) {+ if (fd->vars[i].var_name == name)+ goto duplicate;+ }+ return 0;++duplicate:+ return js_parse_error(s, "duplicate parameter names not allowed in this context");+}++static JSAtom js_parse_destructuring_var(JSParseState *s, int tok, int is_arg)+{+ JSAtom name;++ if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)+ || ((s->cur_func->js_mode & JS_MODE_STRICT) &&+ (s->token.u.ident.atom == JS_ATOM_eval || s->token.u.ident.atom == JS_ATOM_arguments))) {+ js_parse_error(s, "invalid destructuring target");+ return JS_ATOM_NULL;+ }+ name = JS_DupAtom(s->ctx, s->token.u.ident.atom);+ if (is_arg && js_parse_check_duplicate_parameter(s, name))+ goto fail;+ if (next_token(s))+ goto fail;++ return name;+fail:+ JS_FreeAtom(s->ctx, name);+ return JS_ATOM_NULL;+}++/* Return -1 if error, 0 if no initializer, 1 if an initializer is+ present at the top level. */+static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg,+ int hasval, int has_ellipsis,+ BOOL allow_initializer)+{+ int label_parse, label_assign, label_done, label_lvalue, depth_lvalue;+ int start_addr, assign_addr;+ JSAtom prop_name, var_name;+ int opcode, scope, tok1, skip_bits;+ BOOL has_initializer;+ + if (has_ellipsis < 0) {+ /* pre-parse destructuration target for spread detection */+ js_parse_skip_parens_token(s, &skip_bits, FALSE);+ has_ellipsis = skip_bits & SKIP_HAS_ELLIPSIS;+ }++ label_parse = new_label(s);+ label_assign = new_label(s);++ start_addr = s->cur_func->byte_code.size;+ if (hasval) {+ /* consume value from the stack */+ emit_op(s, OP_dup);+ emit_op(s, OP_undefined);+ emit_op(s, OP_strict_eq);+ emit_goto(s, OP_if_true, label_parse);+ emit_label(s, label_assign);+ } else {+ emit_goto(s, OP_goto, label_parse);+ emit_label(s, label_assign);+ /* leave value on the stack */+ emit_op(s, OP_dup);+ }+ assign_addr = s->cur_func->byte_code.size;+ if (s->token.val == '{') {+ if (next_token(s))+ return -1;+ /* throw an exception if the value cannot be converted to an object */+ emit_op(s, OP_to_object);+ if (has_ellipsis) {+ /* add excludeList on stack just below src object */+ emit_op(s, OP_object);+ emit_op(s, OP_swap);+ }+ while (s->token.val != '}') {+ int prop_type;+ if (s->token.val == TOK_ELLIPSIS) {+ if (!has_ellipsis) {+ JS_ThrowInternalError(s->ctx, "unexpected ellipsis token");+ return -1;+ }+ if (next_token(s))+ return -1;+ if (tok) {+ var_name = js_parse_destructuring_var(s, tok, is_arg);+ if (var_name == JS_ATOM_NULL)+ return -1;+ opcode = OP_scope_get_var;+ scope = s->cur_func->scope_level;+ label_lvalue = -1;+ depth_lvalue = 0;+ } else {+ if (js_parse_left_hand_side_expr(s))+ return -1;++ if (get_lvalue(s, &opcode, &scope, &var_name,+ &label_lvalue, &depth_lvalue, FALSE, '{'))+ return -1;+ }+ if (s->token.val != '}') {+ js_parse_error(s, "assignment rest property must be last");+ goto var_error;+ }+ emit_op(s, OP_object); /* target */+ emit_op(s, OP_copy_data_properties);+ emit_u8(s, 0 | ((depth_lvalue + 1) << 2) | ((depth_lvalue + 2) << 5));+ goto set_val;+ }+ prop_type = js_parse_property_name(s, &prop_name, FALSE, TRUE, FALSE);+ if (prop_type < 0)+ return -1;+ var_name = JS_ATOM_NULL;+ opcode = OP_scope_get_var;+ scope = s->cur_func->scope_level;+ label_lvalue = -1;+ depth_lvalue = 0;+ if (prop_type == PROP_TYPE_IDENT) {+ if (next_token(s))+ goto prop_error;+ if ((s->token.val == '[' || s->token.val == '{')+ && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' ||+ tok1 == '=' || tok1 == '}')) {+ if (prop_name == JS_ATOM_NULL) {+ /* computed property name on stack */+ if (has_ellipsis) {+ /* define the property in excludeList */+ emit_op(s, OP_to_propkey); /* avoid calling ToString twice */+ emit_op(s, OP_perm3); /* TOS: src excludeList prop */+ emit_op(s, OP_null); /* TOS: src excludeList prop null */+ emit_op(s, OP_define_array_el); /* TOS: src excludeList prop */+ emit_op(s, OP_perm3); /* TOS: excludeList src prop */+ }+ /* get the computed property from the source object */+ emit_op(s, OP_get_array_el2);+ } else {+ /* named property */+ if (has_ellipsis) {+ /* define the property in excludeList */+ emit_op(s, OP_swap); /* TOS: src excludeList */+ emit_op(s, OP_null); /* TOS: src excludeList null */+ emit_op(s, OP_define_field); /* TOS: src excludeList */+ emit_atom(s, prop_name);+ emit_op(s, OP_swap); /* TOS: excludeList src */+ }+ /* get the named property from the source object */+ emit_op(s, OP_get_field2);+ emit_u32(s, prop_name);+ }+ if (js_parse_destructuring_element(s, tok, is_arg, TRUE, -1, TRUE) < 0)+ return -1;+ if (s->token.val == '}')+ break;+ /* accept a trailing comma before the '}' */+ if (js_parse_expect(s, ','))+ return -1;+ continue;+ }+ if (prop_name == JS_ATOM_NULL) {+ emit_op(s, OP_to_propkey2);+ if (has_ellipsis) {+ /* define the property in excludeList */+ emit_op(s, OP_perm3);+ emit_op(s, OP_null);+ emit_op(s, OP_define_array_el);+ emit_op(s, OP_perm3);+ }+ /* source prop -- source source prop */+ emit_op(s, OP_dup1);+ } else {+ if (has_ellipsis) {+ /* define the property in excludeList */+ emit_op(s, OP_swap);+ emit_op(s, OP_null);+ emit_op(s, OP_define_field);+ emit_atom(s, prop_name);+ emit_op(s, OP_swap);+ }+ /* source -- source source */+ emit_op(s, OP_dup);+ }+ if (tok) {+ var_name = js_parse_destructuring_var(s, tok, is_arg);+ if (var_name == JS_ATOM_NULL)+ goto prop_error;+ } else {+ if (js_parse_left_hand_side_expr(s))+ goto prop_error;+ lvalue:+ if (get_lvalue(s, &opcode, &scope, &var_name,+ &label_lvalue, &depth_lvalue, FALSE, '{'))+ goto prop_error;+ /* swap ref and lvalue object if any */+ if (prop_name == JS_ATOM_NULL) {+ switch(depth_lvalue) {+ case 1:+ /* source prop x -> x source prop */+ emit_op(s, OP_rot3r);+ break;+ case 2:+ /* source prop x y -> x y source prop */+ emit_op(s, OP_swap2); /* t p2 s p1 */+ break;+ case 3:+ /* source prop x y z -> x y z source prop */+ emit_op(s, OP_rot5l);+ emit_op(s, OP_rot5l);+ break;+ }+ } else {+ switch(depth_lvalue) {+ case 1:+ /* source x -> x source */+ emit_op(s, OP_swap);+ break;+ case 2:+ /* source x y -> x y source */+ emit_op(s, OP_rot3l);+ break;+ case 3:+ /* source x y z -> x y z source */+ emit_op(s, OP_rot4l);+ break;+ }+ }+ }+ if (prop_name == JS_ATOM_NULL) {+ /* computed property name on stack */+ /* XXX: should have OP_get_array_el2x with depth */+ /* source prop -- val */+ emit_op(s, OP_get_array_el);+ } else {+ /* named property */+ /* XXX: should have OP_get_field2x with depth */+ /* source -- val */+ emit_op(s, OP_get_field);+ emit_u32(s, prop_name);+ }+ } else {+ /* prop_type = PROP_TYPE_VAR, cannot be a computed property */+ if (is_arg && js_parse_check_duplicate_parameter(s, prop_name))+ goto prop_error;+ if ((s->cur_func->js_mode & JS_MODE_STRICT) &&+ (prop_name == JS_ATOM_eval || prop_name == JS_ATOM_arguments)) {+ js_parse_error(s, "invalid destructuring target");+ goto prop_error;+ }+ if (has_ellipsis) {+ /* define the property in excludeList */+ emit_op(s, OP_swap);+ emit_op(s, OP_null);+ emit_op(s, OP_define_field);+ emit_atom(s, prop_name);+ emit_op(s, OP_swap);+ }+ if (!tok || tok == TOK_VAR) {+ /* generate reference */+ /* source -- source source */+ emit_op(s, OP_dup);+ emit_op(s, OP_scope_get_var);+ emit_atom(s, prop_name);+ emit_u16(s, s->cur_func->scope_level);+ goto lvalue;+ }+ var_name = JS_DupAtom(s->ctx, prop_name);+ /* source -- source val */+ emit_op(s, OP_get_field2);+ emit_u32(s, prop_name);+ }+ set_val:+ if (tok) {+ if (js_define_var(s, var_name, tok))+ goto var_error;+ scope = s->cur_func->scope_level;+ }+ if (s->token.val == '=') { /* handle optional default value */+ int label_hasval;+ emit_op(s, OP_dup);+ emit_op(s, OP_undefined);+ emit_op(s, OP_strict_eq);+ label_hasval = emit_goto(s, OP_if_false, -1);+ if (next_token(s))+ goto var_error;+ emit_op(s, OP_drop);+ if (js_parse_assign_expr(s))+ goto var_error;+ if (opcode == OP_scope_get_var || opcode == OP_get_ref_value)+ set_object_name(s, var_name);+ emit_label(s, label_hasval);+ }+ /* store value into lvalue object */+ put_lvalue(s, opcode, scope, var_name, label_lvalue,+ PUT_LVALUE_NOKEEP_DEPTH,+ (tok == TOK_CONST || tok == TOK_LET));+ if (s->token.val == '}')+ break;+ /* accept a trailing comma before the '}' */+ if (js_parse_expect(s, ','))+ return -1;+ }+ /* drop the source object */+ emit_op(s, OP_drop);+ if (has_ellipsis) {+ emit_op(s, OP_drop); /* pop excludeList */+ }+ if (next_token(s))+ return -1;+ } else if (s->token.val == '[') {+ BOOL has_spread;+ int enum_depth;+ BlockEnv block_env;++ if (next_token(s))+ return -1;+ /* the block environment is only needed in generators in case+ 'yield' triggers a 'return' */+ push_break_entry(s->cur_func, &block_env,+ JS_ATOM_NULL, -1, -1, 2);+ block_env.has_iterator = TRUE;+ emit_op(s, OP_for_of_start);+ has_spread = FALSE;+ while (s->token.val != ']') {+ /* get the next value */+ if (s->token.val == TOK_ELLIPSIS) {+ if (next_token(s))+ return -1;+ if (s->token.val == ',' || s->token.val == ']')+ return js_parse_error(s, "missing binding pattern...");+ has_spread = TRUE;+ }+ if (s->token.val == ',') {+ /* do nothing, skip the value, has_spread is false */+ emit_op(s, OP_for_of_next);+ emit_u8(s, 0);+ emit_op(s, OP_drop);+ emit_op(s, OP_drop);+ } else if ((s->token.val == '[' || s->token.val == '{')+ && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' ||+ tok1 == '=' || tok1 == ']')) {+ if (has_spread) {+ if (tok1 == '=')+ return js_parse_error(s, "rest element cannot have a default value");+ js_emit_spread_code(s, 0);+ } else {+ emit_op(s, OP_for_of_next);+ emit_u8(s, 0);+ emit_op(s, OP_drop);+ }+ if (js_parse_destructuring_element(s, tok, is_arg, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)+ return -1;+ } else {+ var_name = JS_ATOM_NULL;+ enum_depth = 0;+ if (tok) {+ var_name = js_parse_destructuring_var(s, tok, is_arg);+ if (var_name == JS_ATOM_NULL)+ goto var_error;+ if (js_define_var(s, var_name, tok))+ goto var_error;+ opcode = OP_scope_get_var;+ scope = s->cur_func->scope_level;+ } else {+ if (js_parse_left_hand_side_expr(s))+ return -1;+ if (get_lvalue(s, &opcode, &scope, &var_name,+ &label_lvalue, &enum_depth, FALSE, '[')) {+ return -1;+ }+ }+ if (has_spread) {+ js_emit_spread_code(s, enum_depth);+ } else {+ emit_op(s, OP_for_of_next);+ emit_u8(s, enum_depth);+ emit_op(s, OP_drop);+ }+ if (s->token.val == '=' && !has_spread) {+ /* handle optional default value */+ int label_hasval;+ emit_op(s, OP_dup);+ emit_op(s, OP_undefined);+ emit_op(s, OP_strict_eq);+ label_hasval = emit_goto(s, OP_if_false, -1);+ if (next_token(s))+ goto var_error;+ emit_op(s, OP_drop);+ if (js_parse_assign_expr(s))+ goto var_error;+ if (opcode == OP_scope_get_var || opcode == OP_get_ref_value)+ set_object_name(s, var_name);+ emit_label(s, label_hasval);+ }+ /* store value into lvalue object */+ put_lvalue(s, opcode, scope, var_name,+ label_lvalue, PUT_LVALUE_NOKEEP_DEPTH,+ (tok == TOK_CONST || tok == TOK_LET));+ }+ if (s->token.val == ']')+ break;+ if (has_spread)+ return js_parse_error(s, "rest element must be the last one");+ /* accept a trailing comma before the ']' */+ if (js_parse_expect(s, ','))+ return -1;+ }+ /* close iterator object:+ if completed, enum_obj has been replaced by undefined */+ emit_op(s, OP_iterator_close);+ pop_break_entry(s->cur_func);+ if (next_token(s))+ return -1;+ } else {+ return js_parse_error(s, "invalid assignment syntax");+ }+ if (s->token.val == '=' && allow_initializer) {+ label_done = emit_goto(s, OP_goto, -1);+ if (next_token(s))+ return -1;+ emit_label(s, label_parse);+ if (hasval)+ emit_op(s, OP_drop);+ if (js_parse_assign_expr(s))+ return -1;+ emit_goto(s, OP_goto, label_assign);+ emit_label(s, label_done);+ has_initializer = TRUE;+ } else {+ /* normally hasval is true except if+ js_parse_skip_parens_token() was wrong in the parsing */+ // assert(hasval);+ if (!hasval) {+ js_parse_error(s, "too complicated destructuring expression");+ return -1;+ }+ /* remove test and decrement label ref count */+ memset(s->cur_func->byte_code.buf + start_addr, OP_nop,+ assign_addr - start_addr);+ s->cur_func->label_slots[label_parse].ref_count--;+ has_initializer = FALSE;+ }+ return has_initializer;++ prop_error:+ JS_FreeAtom(s->ctx, prop_name);+ var_error:+ JS_FreeAtom(s->ctx, var_name);+ return -1;+}++typedef enum FuncCallType {+ FUNC_CALL_NORMAL,+ FUNC_CALL_NEW,+ FUNC_CALL_SUPER_CTOR,+ FUNC_CALL_TEMPLATE,+} FuncCallType;++static void optional_chain_test(JSParseState *s, int *poptional_chaining_label,+ int drop_count)+{+ int label_next, i;+ if (*poptional_chaining_label < 0)+ *poptional_chaining_label = new_label(s);+ /* XXX: could be more efficient with a specific opcode */+ emit_op(s, OP_dup);+ emit_op(s, OP_is_undefined_or_null);+ label_next = emit_goto(s, OP_if_false, -1);+ for(i = 0; i < drop_count; i++)+ emit_op(s, OP_drop);+ emit_op(s, OP_undefined);+ emit_goto(s, OP_goto, *poptional_chaining_label);+ emit_label(s, label_next);+}++/* allowed parse_flags: PF_POSTFIX_CALL, PF_ARROW_FUNC */+static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)+{+ FuncCallType call_type;+ int optional_chaining_label;+ BOOL accept_lparen = (parse_flags & PF_POSTFIX_CALL) != 0;+ + call_type = FUNC_CALL_NORMAL;+ switch(s->token.val) {+ case TOK_NUMBER:+ {+ JSValue val;+ val = s->token.u.num.val;++ if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) {+ emit_op(s, OP_push_i32);+ emit_u32(s, JS_VALUE_GET_INT(val));+ } else+#ifdef CONFIG_BIGNUM+ if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {+ slimb_t e;+ int ret;++ /* need a runtime conversion */+ /* XXX: could add a cache and/or do it once at+ the start of the function */+ if (emit_push_const(s, val, 0) < 0)+ return -1;+ e = s->token.u.num.exponent;+ if (e == (int32_t)e) {+ emit_op(s, OP_push_i32);+ emit_u32(s, e);+ } else {+ val = JS_NewBigInt64_1(s->ctx, e);+ if (JS_IsException(val))+ return -1;+ ret = emit_push_const(s, val, 0);+ JS_FreeValue(s->ctx, val);+ if (ret < 0)+ return -1;+ }+ emit_op(s, OP_mul_pow10);+ } else+#endif+ {+ if (emit_push_const(s, val, 0) < 0)+ return -1;+ }+ }+ if (next_token(s))+ return -1;+ break;+ case TOK_TEMPLATE:+ if (js_parse_template(s, 0, NULL))+ return -1;+ break;+ case TOK_STRING:+ if (emit_push_const(s, s->token.u.str.str, 1))+ return -1;+ if (next_token(s))+ return -1;+ break;+ + case TOK_DIV_ASSIGN:+ s->buf_ptr -= 2;+ goto parse_regexp;+ case '/':+ s->buf_ptr--;+ parse_regexp:+ {+ JSValue str;+ int ret, backtrace_flags;+ if (!s->ctx->compile_regexp)+ return js_parse_error(s, "RegExp are not supported");+ /* the previous token is '/' or '/=', so no need to free */+ if (js_parse_regexp(s))+ return -1;+ ret = emit_push_const(s, s->token.u.regexp.body, 0);+ str = s->ctx->compile_regexp(s->ctx, s->token.u.regexp.body,+ s->token.u.regexp.flags);+ if (JS_IsException(str)) {+ /* add the line number info */+ backtrace_flags = 0;+ if (s->cur_func && s->cur_func->backtrace_barrier)+ backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL;+ build_backtrace(s->ctx, s->ctx->rt->current_exception,+ s->filename, s->token.line_num,+ backtrace_flags);+ return -1;+ }+ ret = emit_push_const(s, str, 0);+ JS_FreeValue(s->ctx, str);+ if (ret)+ return -1;+ /* we use a specific opcode to be sure the correct+ function is called (otherwise the bytecode would have+ to be verified by the RegExp constructor) */+ emit_op(s, OP_regexp);+ if (next_token(s))+ return -1;+ }+ break;+ case '(':+ if ((parse_flags & PF_ARROW_FUNC) &&+ js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) {+ if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,+ JS_FUNC_NORMAL, JS_ATOM_NULL,+ s->token.ptr, s->token.line_num))+ return -1;+ } else {+ if (js_parse_expr_paren(s))+ return -1;+ }+ break;+ case TOK_FUNCTION:+ if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR,+ JS_FUNC_NORMAL, JS_ATOM_NULL,+ s->token.ptr, s->token.line_num))+ return -1;+ break;+ case TOK_CLASS:+ if (js_parse_class(s, TRUE, JS_PARSE_EXPORT_NONE))+ return -1;+ break;+ case TOK_NULL:+ if (next_token(s))+ return -1;+ emit_op(s, OP_null);+ break;+ case TOK_THIS:+ if (next_token(s))+ return -1;+ emit_op(s, OP_scope_get_var);+ emit_atom(s, JS_ATOM_this);+ emit_u16(s, 0);+ break;+ case TOK_FALSE:+ if (next_token(s))+ return -1;+ emit_op(s, OP_push_false);+ break;+ case TOK_TRUE:+ if (next_token(s))+ return -1;+ emit_op(s, OP_push_true);+ break;+ case TOK_IDENT:+ {+ JSAtom name;+ if (s->token.u.ident.is_reserved) {+ return js_parse_error_reserved_identifier(s);+ }+ if ((parse_flags & PF_ARROW_FUNC) &&+ peek_token(s, TRUE) == TOK_ARROW) {+ if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,+ JS_FUNC_NORMAL, JS_ATOM_NULL,+ s->token.ptr, s->token.line_num))+ return -1;+ } else if (token_is_pseudo_keyword(s, JS_ATOM_async) &&+ peek_token(s, TRUE) != '\n') {+ const uint8_t *source_ptr;+ int source_line_num;++ source_ptr = s->token.ptr;+ source_line_num = s->token.line_num;+ if (next_token(s))+ return -1;+ if (s->token.val == TOK_FUNCTION) {+ if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR,+ JS_FUNC_ASYNC, JS_ATOM_NULL,+ source_ptr, source_line_num))+ return -1;+ } else if ((parse_flags & PF_ARROW_FUNC) &&+ ((s->token.val == '(' &&+ js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) ||+ (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved &&+ peek_token(s, TRUE) == TOK_ARROW))) {+ if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,+ JS_FUNC_ASYNC, JS_ATOM_NULL,+ source_ptr, source_line_num))+ return -1;+ } else {+ name = JS_DupAtom(s->ctx, JS_ATOM_async);+ goto do_get_var;+ }+ } else {+ if (s->token.u.ident.atom == JS_ATOM_arguments &&+ !s->cur_func->arguments_allowed) {+ js_parse_error(s, "'arguments' identifier is not allowed in class field initializer");+ return -1;+ }+ name = JS_DupAtom(s->ctx, s->token.u.ident.atom);+ if (next_token(s)) /* update line number before emitting code */+ return -1;+ do_get_var:+ emit_op(s, OP_scope_get_var);+ emit_u32(s, name);+ emit_u16(s, s->cur_func->scope_level);+ }+ }+ break;+ case '{':+ case '[':+ {+ int skip_bits;+ if (js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') {+ if (js_parse_destructuring_element(s, 0, 0, FALSE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)+ return -1;+ } else {+ if (s->token.val == '{') {+ if (js_parse_object_literal(s))+ return -1;+ } else {+ if (js_parse_array_literal(s))+ return -1;+ }+ }+ }+ break;+ case TOK_NEW:+ if (next_token(s))+ return -1;+ if (s->token.val == '.') {+ if (next_token(s))+ return -1;+ if (!token_is_pseudo_keyword(s, JS_ATOM_target))+ return js_parse_error(s, "expecting target");+ if (!s->cur_func->new_target_allowed)+ return js_parse_error(s, "new.target only allowed within functions");+ if (next_token(s))+ return -1;+ emit_op(s, OP_scope_get_var);+ emit_atom(s, JS_ATOM_new_target);+ emit_u16(s, 0);+ } else {+ if (js_parse_postfix_expr(s, 0))+ return -1;+ accept_lparen = TRUE;+ if (s->token.val != '(') {+ /* new operator on an object */+ emit_op(s, OP_dup);+ emit_op(s, OP_call_constructor);+ emit_u16(s, 0);+ } else {+ call_type = FUNC_CALL_NEW;+ }+ }+ break;+ case TOK_SUPER:+ if (next_token(s))+ return -1;+ if (s->token.val == '(') {+ if (!s->cur_func->super_call_allowed)+ return js_parse_error(s, "super() is only valid in a derived class constructor");+ call_type = FUNC_CALL_SUPER_CTOR;+ } else if (s->token.val == '.' || s->token.val == '[') {+ if (!s->cur_func->super_allowed)+ return js_parse_error(s, "'super' is only valid in a method");+ emit_op(s, OP_scope_get_var);+ emit_atom(s, JS_ATOM_this);+ emit_u16(s, 0);+ emit_op(s, OP_scope_get_var);+ emit_atom(s, JS_ATOM_home_object);+ emit_u16(s, 0);+ emit_op(s, OP_get_super);+ } else {+ return js_parse_error(s, "invalid use of 'super'");+ }+ break;+ case TOK_IMPORT:+ if (next_token(s))+ return -1;+ if (s->token.val == '.') {+ if (next_token(s))+ return -1;+ if (!token_is_pseudo_keyword(s, JS_ATOM_meta))+ return js_parse_error(s, "meta expected");+ if (!s->is_module)+ return js_parse_error(s, "import.meta only valid in module code");+ if (next_token(s))+ return -1;+ emit_op(s, OP_special_object);+ emit_u8(s, OP_SPECIAL_OBJECT_IMPORT_META);+ } else {+ if (js_parse_expect(s, '('))+ return -1;+ if (!accept_lparen)+ return js_parse_error(s, "invalid use of 'import()'");+ if (js_parse_assign_expr(s))+ return -1;+ if (js_parse_expect(s, ')'))+ return -1;+ emit_op(s, OP_import);+ }+ break;+ default:+ return js_parse_error(s, "unexpected token in expression: '%.*s'",+ (int)(s->buf_ptr - s->token.ptr), s->token.ptr);+ }++ optional_chaining_label = -1;+ for(;;) {+ JSFunctionDef *fd = s->cur_func;+ BOOL has_optional_chain = FALSE;+ + if (s->token.val == TOK_QUESTION_MARK_DOT) {+ /* optional chaining */+ if (next_token(s))+ return -1;+ has_optional_chain = TRUE;+ if (s->token.val == '(' && accept_lparen) {+ goto parse_func_call;+ } else if (s->token.val == '[') {+ goto parse_array_access;+ } else {+ goto parse_property;+ }+ } else if (s->token.val == TOK_TEMPLATE &&+ call_type == FUNC_CALL_NORMAL) {+ if (optional_chaining_label >= 0) {+ return js_parse_error(s, "template literal cannot appear in an optional chain");+ }+ call_type = FUNC_CALL_TEMPLATE;+ goto parse_func_call2;+ } else if (s->token.val == '(' && accept_lparen) {+ int opcode, arg_count, drop_count;++ /* function call */+ parse_func_call:+ if (next_token(s))+ return -1;++ if (call_type == FUNC_CALL_NORMAL) {+ parse_func_call2:+ switch(opcode = get_prev_opcode(fd)) {+ case OP_get_field:+ /* keep the object on the stack */+ fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2;+ drop_count = 2;+ break;+ case OP_scope_get_private_field:+ /* keep the object on the stack */+ fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_private_field2;+ drop_count = 2;+ break;+ case OP_get_array_el:+ /* keep the object on the stack */+ fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2;+ drop_count = 2;+ break;+ case OP_scope_get_var:+ {+ JSAtom name;+ int scope;+ name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);+ scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);+ if (name == JS_ATOM_eval && call_type == FUNC_CALL_NORMAL && !has_optional_chain) {+ /* direct 'eval' */+ opcode = OP_eval;+ } else {+ /* verify if function name resolves to a simple+ get_loc/get_arg: a function call inside a `with`+ statement can resolve to a method call of the+ `with` context object+ */+ /* XXX: always generate the OP_scope_get_ref+ and remove it in variable resolution+ pass ? */+ if (has_with_scope(fd, scope)) {+ opcode = OP_scope_get_ref;+ fd->byte_code.buf[fd->last_opcode_pos] = opcode;+ }+ }+ drop_count = 1;+ }+ break;+ case OP_get_super_value:+ fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el;+ /* on stack: this func_obj */+ opcode = OP_get_array_el;+ drop_count = 2;+ break;+ default:+ opcode = OP_invalid;+ drop_count = 1;+ break;+ }+ if (has_optional_chain) {+ optional_chain_test(s, &optional_chaining_label,+ drop_count);+ }+ } else {+ opcode = OP_invalid;+ }++ if (call_type == FUNC_CALL_TEMPLATE) {+ if (js_parse_template(s, 1, &arg_count))+ return -1;+ goto emit_func_call;+ } else if (call_type == FUNC_CALL_SUPER_CTOR) {+ emit_op(s, OP_scope_get_var);+ emit_atom(s, JS_ATOM_this_active_func);+ emit_u16(s, 0);++ emit_op(s, OP_get_super);++ emit_op(s, OP_scope_get_var);+ emit_atom(s, JS_ATOM_new_target);+ emit_u16(s, 0);+ } else if (call_type == FUNC_CALL_NEW) {+ emit_op(s, OP_dup); /* new.target = function */+ }++ /* parse arguments */+ arg_count = 0;+ while (s->token.val != ')') {+ if (arg_count >= 65535) {+ return js_parse_error(s, "Too many call arguments");+ }+ if (s->token.val == TOK_ELLIPSIS)+ break;+ if (js_parse_assign_expr(s))+ return -1;+ arg_count++;+ if (s->token.val == ')')+ break;+ /* accept a trailing comma before the ')' */+ if (js_parse_expect(s, ','))+ return -1;+ }+ if (s->token.val == TOK_ELLIPSIS) {+ emit_op(s, OP_array_from);+ emit_u16(s, arg_count);+ emit_op(s, OP_push_i32);+ emit_u32(s, arg_count);++ /* on stack: array idx */+ while (s->token.val != ')') {+ if (s->token.val == TOK_ELLIPSIS) {+ if (next_token(s))+ return -1;+ if (js_parse_assign_expr(s))+ return -1;+#if 1+ /* XXX: could pass is_last indicator? */+ emit_op(s, OP_append);+#else+ int label_next, label_done;+ label_next = new_label(s);+ label_done = new_label(s);+ /* push enumerate object below array/idx pair */+ emit_op(s, OP_for_of_start);+ emit_op(s, OP_rot5l);+ emit_op(s, OP_rot5l);+ emit_label(s, label_next);+ /* on stack: enum_rec array idx */+ emit_op(s, OP_for_of_next);+ emit_u8(s, 2);+ emit_goto(s, OP_if_true, label_done);+ /* append element */+ /* enum_rec array idx val -> enum_rec array new_idx */+ emit_op(s, OP_define_array_el);+ emit_op(s, OP_inc);+ emit_goto(s, OP_goto, label_next);+ emit_label(s, label_done);+ /* close enumeration, drop enum_rec and idx */+ emit_op(s, OP_drop); /* drop undef */+ emit_op(s, OP_nip1); /* drop enum_rec */+ emit_op(s, OP_nip1);+ emit_op(s, OP_nip1);+#endif+ } else {+ if (js_parse_assign_expr(s))+ return -1;+ /* array idx val */+ emit_op(s, OP_define_array_el);+ emit_op(s, OP_inc);+ }+ if (s->token.val == ')')+ break;+ /* accept a trailing comma before the ')' */+ if (js_parse_expect(s, ','))+ return -1;+ }+ if (next_token(s))+ return -1;+ /* drop the index */+ emit_op(s, OP_drop);++ /* apply function call */+ switch(opcode) {+ case OP_get_field:+ case OP_scope_get_private_field:+ case OP_get_array_el:+ case OP_scope_get_ref:+ /* obj func array -> func obj array */+ emit_op(s, OP_perm3);+ emit_op(s, OP_apply);+ emit_u16(s, call_type == FUNC_CALL_NEW);+ break;+ case OP_eval:+ emit_op(s, OP_apply_eval);+ emit_u16(s, fd->scope_level);+ fd->has_eval_call = TRUE;+ break;+ default:+ if (call_type == FUNC_CALL_SUPER_CTOR) {+ emit_op(s, OP_apply);+ emit_u16(s, 1);+ /* set the 'this' value */+ emit_op(s, OP_dup);+ emit_op(s, OP_scope_put_var_init);+ emit_atom(s, JS_ATOM_this);+ emit_u16(s, 0);++ emit_class_field_init(s);+ } else if (call_type == FUNC_CALL_NEW) {+ /* obj func array -> func obj array */+ emit_op(s, OP_perm3);+ emit_op(s, OP_apply);+ emit_u16(s, 1);+ } else {+ /* func array -> func undef array */+ emit_op(s, OP_undefined);+ emit_op(s, OP_swap);+ emit_op(s, OP_apply);+ emit_u16(s, 0);+ }+ break;+ }+ } else {+ if (next_token(s))+ return -1;+ emit_func_call:+ switch(opcode) {+ case OP_get_field:+ case OP_scope_get_private_field:+ case OP_get_array_el:+ case OP_scope_get_ref:+ emit_op(s, OP_call_method);+ emit_u16(s, arg_count);+ break;+ case OP_eval:+ emit_op(s, OP_eval);+ emit_u16(s, arg_count);+ emit_u16(s, fd->scope_level);+ fd->has_eval_call = TRUE;+ break;+ default:+ if (call_type == FUNC_CALL_SUPER_CTOR) {+ emit_op(s, OP_call_constructor);+ emit_u16(s, arg_count);++ /* set the 'this' value */+ emit_op(s, OP_dup);+ emit_op(s, OP_scope_put_var_init);+ emit_atom(s, JS_ATOM_this);+ emit_u16(s, 0);++ emit_class_field_init(s);+ } else if (call_type == FUNC_CALL_NEW) {+ emit_op(s, OP_call_constructor);+ emit_u16(s, arg_count);+ } else {+ emit_op(s, OP_call);+ emit_u16(s, arg_count);+ }+ break;+ }+ }+ call_type = FUNC_CALL_NORMAL;+ } else if (s->token.val == '.') {+ if (next_token(s))+ return -1;+ parse_property:+ if (s->token.val == TOK_PRIVATE_NAME) {+ /* private class field */+ if (get_prev_opcode(fd) == OP_get_super) {+ return js_parse_error(s, "private class field forbidden after super");+ }+ if (has_optional_chain) {+ optional_chain_test(s, &optional_chaining_label, 1);+ }+ emit_op(s, OP_scope_get_private_field);+ emit_atom(s, s->token.u.ident.atom);+ emit_u16(s, s->cur_func->scope_level);+ } else {+ if (!token_is_ident(s->token.val)) {+ return js_parse_error(s, "expecting field name");+ }+ if (get_prev_opcode(fd) == OP_get_super) {+ JSValue val;+ int ret;+ val = JS_AtomToValue(s->ctx, s->token.u.ident.atom);+ ret = emit_push_const(s, val, 1);+ JS_FreeValue(s->ctx, val);+ if (ret)+ return -1;+ emit_op(s, OP_get_super_value);+ } else {+ if (has_optional_chain) {+ optional_chain_test(s, &optional_chaining_label, 1);+ }+ emit_op(s, OP_get_field);+ emit_atom(s, s->token.u.ident.atom);+ }+ }+ if (next_token(s))+ return -1;+ } else if (s->token.val == '[') {+ int prev_op;++ parse_array_access:+ prev_op = get_prev_opcode(fd);+ if (has_optional_chain) {+ optional_chain_test(s, &optional_chaining_label, 1);+ }+ if (next_token(s))+ return -1;+ if (js_parse_expr(s))+ return -1;+ if (js_parse_expect(s, ']'))+ return -1;+ if (prev_op == OP_get_super) {+ emit_op(s, OP_get_super_value);+ } else {+ emit_op(s, OP_get_array_el);+ }+ } else {+ break;+ }+ }+ if (optional_chaining_label >= 0)+ emit_label(s, optional_chaining_label);+ return 0;+}++static __exception int js_parse_delete(JSParseState *s)+{+ JSFunctionDef *fd = s->cur_func;+ JSAtom name;+ int opcode;++ if (next_token(s))+ return -1;+ if (js_parse_unary(s, PF_POW_FORBIDDEN))+ return -1;+ switch(opcode = get_prev_opcode(fd)) {+ case OP_get_field:+ {+ JSValue val;+ int ret;++ name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);+ fd->byte_code.size = fd->last_opcode_pos;+ fd->last_opcode_pos = -1;+ val = JS_AtomToValue(s->ctx, name);+ ret = emit_push_const(s, val, 1);+ JS_FreeValue(s->ctx, val);+ JS_FreeAtom(s->ctx, name);+ if (ret)+ return ret;+ }+ goto do_delete;+ case OP_get_array_el:+ fd->byte_code.size = fd->last_opcode_pos;+ fd->last_opcode_pos = -1;+ do_delete:+ emit_op(s, OP_delete);+ break;+ case OP_scope_get_var:+ /* 'delete this': this is not a reference */+ name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);+ if (name == JS_ATOM_this || name == JS_ATOM_new_target)+ goto ret_true;+ if (fd->js_mode & JS_MODE_STRICT) {+ return js_parse_error(s, "cannot delete a direct reference in strict mode");+ } else {+ fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_delete_var;+ }+ break;+ case OP_scope_get_private_field:+ return js_parse_error(s, "cannot delete a private class field");+ case OP_get_super_value:+ emit_op(s, OP_throw_error);+ emit_atom(s, JS_ATOM_NULL);+ emit_u8(s, JS_THROW_ERROR_DELETE_SUPER);+ break;+ default:+ ret_true:+ emit_op(s, OP_drop);+ emit_op(s, OP_push_true);+ break;+ }+ return 0;+}++/* allowed parse_flags: PF_ARROW_FUNC, PF_POW_ALLOWED, PF_POW_FORBIDDEN */+static __exception int js_parse_unary(JSParseState *s, int parse_flags)+{+ int op;++ switch(s->token.val) {+ case '+':+ case '-':+ case '!':+ case '~':+ case TOK_VOID:+ op = s->token.val;+ if (next_token(s))+ return -1;+ if (js_parse_unary(s, PF_POW_FORBIDDEN))+ return -1;+ switch(op) {+ case '-':+ emit_op(s, OP_neg);+ break;+ case '+':+ emit_op(s, OP_plus);+ break;+ case '!':+ emit_op(s, OP_lnot);+ break;+ case '~':+ emit_op(s, OP_not);+ break;+ case TOK_VOID:+ emit_op(s, OP_drop);+ emit_op(s, OP_undefined);+ break;+ default:+ abort();+ }+ parse_flags = 0;+ break;+ case TOK_DEC:+ case TOK_INC:+ {+ int opcode, op, scope, label;+ JSAtom name;+ op = s->token.val;+ if (next_token(s))+ return -1;+ if (js_parse_unary(s, 0))+ return -1;+ if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op))+ return -1;+ emit_op(s, OP_dec + op - TOK_DEC);+ put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP,+ FALSE);+ }+ break;+ case TOK_TYPEOF:+ {+ JSFunctionDef *fd;+ if (next_token(s))+ return -1;+ if (js_parse_unary(s, PF_POW_FORBIDDEN))+ return -1;+ /* reference access should not return an exception, so we+ patch the get_var */+ fd = s->cur_func;+ if (get_prev_opcode(fd) == OP_scope_get_var) {+ fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_var_undef;+ }+ emit_op(s, OP_typeof);+ parse_flags = 0;+ }+ break;+ case TOK_DELETE:+ if (js_parse_delete(s))+ return -1;+ parse_flags = 0;+ break;+ case TOK_AWAIT:+ if (!(s->cur_func->func_kind & JS_FUNC_ASYNC))+ return js_parse_error(s, "unexpected 'await' keyword");+ if (!s->cur_func->in_function_body)+ return js_parse_error(s, "await in default expression");+ if (next_token(s))+ return -1;+ if (js_parse_unary(s, PF_POW_FORBIDDEN))+ return -1;+ emit_op(s, OP_await);+ parse_flags = 0;+ break;+ default:+ if (js_parse_postfix_expr(s, (parse_flags & PF_ARROW_FUNC) |+ PF_POSTFIX_CALL))+ return -1;+ if (!s->got_lf &&+ (s->token.val == TOK_DEC || s->token.val == TOK_INC)) {+ int opcode, op, scope, label;+ JSAtom name;+ op = s->token.val;+ if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op))+ return -1;+ emit_op(s, OP_post_dec + op - TOK_DEC);+ put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_SECOND,+ FALSE);+ if (next_token(s))+ return -1; + }+ break;+ }+ if (parse_flags & (PF_POW_ALLOWED | PF_POW_FORBIDDEN)) {+#ifdef CONFIG_BIGNUM+ if (s->token.val == TOK_POW || s->token.val == TOK_MATH_POW) {+ /* Extended exponentiation syntax rules: we extend the ES7+ grammar in order to have more intuitive semantics:+ -2**2 evaluates to -4. */+ if (!(s->cur_func->js_mode & JS_MODE_MATH)) {+ if (parse_flags & PF_POW_FORBIDDEN) {+ JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'");+ return -1;+ }+ }+ if (next_token(s))+ return -1;+ if (js_parse_unary(s, PF_POW_ALLOWED))+ return -1;+ emit_op(s, OP_pow);+ }+#else+ if (s->token.val == TOK_POW) {+ /* Strict ES7 exponentiation syntax rules: To solve+ conficting semantics between different implementations+ regarding the precedence of prefix operators and the+ postifx exponential, ES7 specifies that -2**2 is a+ syntax error. */+ if (parse_flags & PF_POW_FORBIDDEN) {+ JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'");+ return -1;+ }+ if (next_token(s))+ return -1;+ if (js_parse_unary(s, PF_POW_ALLOWED))+ return -1;+ emit_op(s, OP_pow);+ }+#endif+ }+ return 0;+}++/* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */+static __exception int js_parse_expr_binary(JSParseState *s, int level,+ int parse_flags)+{+ int op, opcode;++ if (level == 0) {+ return js_parse_unary(s, (parse_flags & PF_ARROW_FUNC) |+ PF_POW_ALLOWED);+ }+ if (js_parse_expr_binary(s, level - 1, parse_flags))+ return -1;+ for(;;) {+ op = s->token.val;+ switch(level) {+ case 1:+ switch(op) {+ case '*':+ opcode = OP_mul;+ break;+ case '/':+ opcode = OP_div;+ break;+ case '%':+#ifdef CONFIG_BIGNUM+ if (s->cur_func->js_mode & JS_MODE_MATH)+ opcode = OP_math_mod;+ else+#endif+ opcode = OP_mod;+ break;+ default:+ return 0;+ }+ break;+ case 2:+ switch(op) {+ case '+':+ opcode = OP_add;+ break;+ case '-':+ opcode = OP_sub;+ break;+ default:+ return 0;+ }+ break;+ case 3:+ switch(op) {+ case TOK_SHL:+ opcode = OP_shl;+ break;+ case TOK_SAR:+ opcode = OP_sar;+ break;+ case TOK_SHR:+ opcode = OP_shr;+ break;+ default:+ return 0;+ }+ break;+ case 4:+ switch(op) {+ case '<':+ opcode = OP_lt;+ break;+ case '>':+ opcode = OP_gt;+ break;+ case TOK_LTE:+ opcode = OP_lte;+ break;+ case TOK_GTE:+ opcode = OP_gte;+ break;+ case TOK_INSTANCEOF:+ opcode = OP_instanceof;+ break;+ case TOK_IN:+ if (parse_flags & PF_IN_ACCEPTED) {+ opcode = OP_in;+ } else {+ return 0;+ }+ break;+ default:+ return 0;+ }+ break;+ case 5:+ switch(op) {+ case TOK_EQ:+ opcode = OP_eq;+ break;+ case TOK_NEQ:+ opcode = OP_neq;+ break;+ case TOK_STRICT_EQ:+ opcode = OP_strict_eq;+ break;+ case TOK_STRICT_NEQ:+ opcode = OP_strict_neq;+ break;+ default:+ return 0;+ }+ break;+ case 6:+ switch(op) {+ case '&':+ opcode = OP_and;+ break;+ default:+ return 0;+ }+ break;+ case 7:+ switch(op) {+ case '^':+ opcode = OP_xor;+ break;+ default:+ return 0;+ }+ break;+ case 8:+ switch(op) {+ case '|':+ opcode = OP_or;+ break;+ default:+ return 0;+ }+ break;+ default:+ abort();+ }+ if (next_token(s))+ return -1;+ if (js_parse_expr_binary(s, level - 1, parse_flags & ~PF_ARROW_FUNC))+ return -1;+ emit_op(s, opcode);+ }+ return 0;+}++/* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */+static __exception int js_parse_logical_and_or(JSParseState *s, int op,+ int parse_flags)+{+ int label1;++ if (op == TOK_LAND) {+ if (js_parse_expr_binary(s, 8, parse_flags))+ return -1;+ } else {+ if (js_parse_logical_and_or(s, TOK_LAND, parse_flags))+ return -1;+ }+ if (s->token.val == op) {+ label1 = new_label(s);++ for(;;) {+ if (next_token(s))+ return -1;+ emit_op(s, OP_dup);+ emit_goto(s, op == TOK_LAND ? OP_if_false : OP_if_true, label1);+ emit_op(s, OP_drop);++ if (op == TOK_LAND) {+ if (js_parse_expr_binary(s, 8, parse_flags & ~PF_ARROW_FUNC))+ return -1;+ } else {+ if (js_parse_logical_and_or(s, TOK_LAND,+ parse_flags & ~PF_ARROW_FUNC))+ return -1;+ }+ if (s->token.val != op) {+ if (s->token.val == TOK_DOUBLE_QUESTION_MARK)+ return js_parse_error(s, "cannot mix ?? with && or ||");+ break;+ }+ }++ emit_label(s, label1);+ }+ return 0;+}++static __exception int js_parse_coalesce_expr(JSParseState *s, int parse_flags)+{+ int label1;+ + if (js_parse_logical_and_or(s, TOK_LOR, parse_flags))+ return -1;+ if (s->token.val == TOK_DOUBLE_QUESTION_MARK) {+ label1 = new_label(s);+ for(;;) {+ if (next_token(s))+ return -1;+ + emit_op(s, OP_dup);+ emit_op(s, OP_is_undefined_or_null);+ emit_goto(s, OP_if_false, label1);+ emit_op(s, OP_drop);+ + if (js_parse_expr_binary(s, 8, parse_flags & ~PF_ARROW_FUNC))+ return -1;+ if (s->token.val != TOK_DOUBLE_QUESTION_MARK)+ break;+ }+ emit_label(s, label1);+ }+ return 0;+}++/* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */+static __exception int js_parse_cond_expr(JSParseState *s, int parse_flags)+{+ int label1, label2;++ if (js_parse_coalesce_expr(s, parse_flags))+ return -1;+ if (s->token.val == '?') {+ if (next_token(s))+ return -1;+ label1 = emit_goto(s, OP_if_false, -1);++ if (js_parse_assign_expr(s))+ return -1;+ if (js_parse_expect(s, ':'))+ return -1;++ label2 = emit_goto(s, OP_goto, -1);++ emit_label(s, label1);++ if (js_parse_assign_expr2(s, parse_flags & PF_IN_ACCEPTED))+ return -1;++ emit_label(s, label2);+ }+ return 0;+}++static void emit_return(JSParseState *s, BOOL hasval);++/* allowed parse_flags: PF_IN_ACCEPTED */+static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags)+{+ int opcode, op, scope;+ JSAtom name0 = JS_ATOM_NULL;+ JSAtom name;++ if (s->token.val == TOK_YIELD) {+ BOOL is_star = FALSE, is_async;+ + if (!(s->cur_func->func_kind & JS_FUNC_GENERATOR))+ return js_parse_error(s, "unexpected 'yield' keyword");+ if (!s->cur_func->in_function_body)+ return js_parse_error(s, "yield in default expression");+ if (next_token(s))+ return -1;+ /* XXX: is there a better method to detect 'yield' without+ parameters ? */+ if (s->token.val != ';' && s->token.val != ')' &&+ s->token.val != ']' && s->token.val != '}' &&+ s->token.val != ',' && s->token.val != ':' && !s->got_lf) {+ if (s->token.val == '*') {+ is_star = TRUE;+ if (next_token(s))+ return -1;+ }+ if (js_parse_assign_expr2(s, parse_flags))+ return -1;+ } else {+ emit_op(s, OP_undefined);+ }+ is_async = (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR);++ if (is_star) {+ int label_loop, label_return, label_next;+ int label_return1, label_yield, label_throw, label_throw1;+ int label_throw2;++ label_loop = new_label(s);+ label_yield = new_label(s);++ emit_op(s, is_async ? OP_for_await_of_start : OP_for_of_start);++ /* remove the catch offset (XXX: could avoid pushing back+ undefined) */+ emit_op(s, OP_drop);+ emit_op(s, OP_undefined);+ + emit_op(s, OP_undefined); /* initial value */+ + emit_label(s, label_loop);+ emit_op(s, OP_iterator_next);+ if (is_async)+ emit_op(s, OP_await);+ emit_op(s, OP_iterator_check_object);+ emit_op(s, OP_get_field2);+ emit_atom(s, JS_ATOM_done);+ label_next = emit_goto(s, OP_if_true, -1); /* end of loop */+ emit_label(s, label_yield);+ if (is_async) {+ /* OP_async_yield_star takes the value as parameter */+ emit_op(s, OP_get_field);+ emit_atom(s, JS_ATOM_value);+ emit_op(s, OP_await);+ emit_op(s, OP_async_yield_star);+ } else {+ /* OP_yield_star takes (value, done) as parameter */+ emit_op(s, OP_yield_star);+ }+ emit_op(s, OP_dup);+ label_return = emit_goto(s, OP_if_true, -1);+ emit_op(s, OP_drop);+ emit_goto(s, OP_goto, label_loop);+ + emit_label(s, label_return);+ emit_op(s, OP_push_i32);+ emit_u32(s, 2);+ emit_op(s, OP_strict_eq);+ label_throw = emit_goto(s, OP_if_true, -1);+ + /* return handling */+ if (is_async)+ emit_op(s, OP_await);+ emit_op(s, OP_iterator_call);+ emit_u8(s, 0);+ label_return1 = emit_goto(s, OP_if_true, -1);+ if (is_async)+ emit_op(s, OP_await);+ emit_op(s, OP_iterator_check_object);+ emit_op(s, OP_get_field2);+ emit_atom(s, JS_ATOM_done);+ emit_goto(s, OP_if_false, label_yield);++ emit_op(s, OP_get_field);+ emit_atom(s, JS_ATOM_value);+ + emit_label(s, label_return1);+ emit_op(s, OP_nip);+ emit_op(s, OP_nip);+ emit_op(s, OP_nip);+ emit_return(s, TRUE);+ + /* throw handling */+ emit_label(s, label_throw);+ emit_op(s, OP_iterator_call);+ emit_u8(s, 1);+ label_throw1 = emit_goto(s, OP_if_true, -1);+ if (is_async)+ emit_op(s, OP_await);+ emit_op(s, OP_iterator_check_object);+ emit_op(s, OP_get_field2);+ emit_atom(s, JS_ATOM_done);+ emit_goto(s, OP_if_false, label_yield);+ emit_goto(s, OP_goto, label_next);+ /* close the iterator and throw a type error exception */+ emit_label(s, label_throw1);+ emit_op(s, OP_iterator_call);+ emit_u8(s, 2);+ label_throw2 = emit_goto(s, OP_if_true, -1);+ if (is_async)+ emit_op(s, OP_await);+ emit_label(s, label_throw2);++ emit_op(s, OP_throw_error);+ emit_atom(s, JS_ATOM_NULL);+ emit_u8(s, JS_THROW_ERROR_ITERATOR_THROW);+ + emit_label(s, label_next);+ emit_op(s, OP_get_field);+ emit_atom(s, JS_ATOM_value);+ emit_op(s, OP_nip); /* keep the value associated with+ done = true */+ emit_op(s, OP_nip);+ emit_op(s, OP_nip);+ } else {+ int label_next;+ + if (is_async)+ emit_op(s, OP_await);+ emit_op(s, OP_yield);+ label_next = emit_goto(s, OP_if_false, -1);+ emit_return(s, TRUE);+ emit_label(s, label_next);+ }+ return 0;+ }+ if (s->token.val == TOK_IDENT) {+ /* name0 is used to check for OP_set_name pattern, not duplicated */+ name0 = s->token.u.ident.atom;+ }+ if (js_parse_cond_expr(s, parse_flags | PF_ARROW_FUNC))+ return -1;++ op = s->token.val;+ if (op == '=' || (op >= TOK_MUL_ASSIGN && op <= TOK_POW_ASSIGN)) {+ int label;+ if (next_token(s))+ return -1;+ if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, (op != '='), op) < 0)+ return -1;++ if (js_parse_assign_expr2(s, parse_flags)) {+ JS_FreeAtom(s->ctx, name);+ return -1;+ }++ if (op == '=') {+ if (opcode == OP_get_ref_value && name == name0) {+ set_object_name(s, name);+ }+ } else {+ static const uint8_t assign_opcodes[] = {+ OP_mul, OP_div, OP_mod, OP_add, OP_sub,+ OP_shl, OP_sar, OP_shr, OP_and, OP_xor, OP_or,+#ifdef CONFIG_BIGNUM+ OP_pow,+#endif+ OP_pow,+ };+ op = assign_opcodes[op - TOK_MUL_ASSIGN];+#ifdef CONFIG_BIGNUM+ if (s->cur_func->js_mode & JS_MODE_MATH) {+ if (op == OP_mod)+ op = OP_math_mod;+ }+#endif+ emit_op(s, op);+ }+ put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, FALSE);+ } else if (op >= TOK_LAND_ASSIGN && op <= TOK_DOUBLE_QUESTION_MARK_ASSIGN) {+ int label, label1, depth_lvalue, label2;+ + if (next_token(s))+ return -1;+ if (get_lvalue(s, &opcode, &scope, &name, &label,+ &depth_lvalue, TRUE, op) < 0)+ return -1;++ emit_op(s, OP_dup);+ if (op == TOK_DOUBLE_QUESTION_MARK_ASSIGN)+ emit_op(s, OP_is_undefined_or_null);+ label1 = emit_goto(s, op == TOK_LOR_ASSIGN ? OP_if_true : OP_if_false,+ -1);+ emit_op(s, OP_drop);+ + if (js_parse_assign_expr2(s, parse_flags)) {+ JS_FreeAtom(s->ctx, name);+ return -1;+ }++ if (opcode == OP_get_ref_value && name == name0) {+ set_object_name(s, name);+ }+ + switch(depth_lvalue) {+ case 1:+ emit_op(s, OP_insert2);+ break;+ case 2:+ emit_op(s, OP_insert3);+ break;+ case 3:+ emit_op(s, OP_insert4);+ break;+ default:+ abort();+ }++ /* XXX: we disable the OP_put_ref_value optimization by not+ using put_lvalue() otherwise depth_lvalue is not correct */+ put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_NOKEEP_DEPTH,+ FALSE);+ label2 = emit_goto(s, OP_goto, -1);+ + emit_label(s, label1);++ /* remove the lvalue stack entries */+ while (depth_lvalue != 0) {+ emit_op(s, OP_nip);+ depth_lvalue--;+ }++ emit_label(s, label2);+ }+ return 0;+}++static __exception int js_parse_assign_expr(JSParseState *s)+{+ return js_parse_assign_expr2(s, PF_IN_ACCEPTED);+}++/* allowed parse_flags: PF_IN_ACCEPTED */+static __exception int js_parse_expr2(JSParseState *s, int parse_flags)+{+ BOOL comma = FALSE;+ for(;;) {+ if (js_parse_assign_expr2(s, parse_flags))+ return -1;+ if (comma) {+ /* prevent get_lvalue from using the last expression+ as an lvalue. This also prevents the conversion of+ of get_var to get_ref for method lookup in function+ call inside `with` statement.+ */+ s->cur_func->last_opcode_pos = -1;+ }+ if (s->token.val != ',')+ break;+ comma = TRUE;+ if (next_token(s))+ return -1;+ emit_op(s, OP_drop);+ }+ return 0;+}++static __exception int js_parse_expr(JSParseState *s)+{+ return js_parse_expr2(s, PF_IN_ACCEPTED);+}++static void push_break_entry(JSFunctionDef *fd, BlockEnv *be,+ JSAtom label_name,+ int label_break, int label_cont,+ int drop_count)+{+ be->prev = fd->top_break;+ fd->top_break = be;+ be->label_name = label_name;+ be->label_break = label_break;+ be->label_cont = label_cont;+ be->drop_count = drop_count;+ be->label_finally = -1;+ be->scope_level = fd->scope_level;+ be->has_iterator = FALSE;+}++static void pop_break_entry(JSFunctionDef *fd)+{+ BlockEnv *be;+ be = fd->top_break;+ fd->top_break = be->prev;+}++static __exception int emit_break(JSParseState *s, JSAtom name, int is_cont)+{+ BlockEnv *top;+ int i, scope_level;++ scope_level = s->cur_func->scope_level;+ top = s->cur_func->top_break;+ while (top != NULL) {+ close_scopes(s, scope_level, top->scope_level);+ scope_level = top->scope_level;+ if (is_cont &&+ top->label_cont != -1 &&+ (name == JS_ATOM_NULL || top->label_name == name)) {+ /* continue stays inside the same block */+ emit_goto(s, OP_goto, top->label_cont);+ return 0;+ }+ if (!is_cont &&+ top->label_break != -1 &&+ (name == JS_ATOM_NULL || top->label_name == name)) {+ emit_goto(s, OP_goto, top->label_break);+ return 0;+ }+ i = 0;+ if (top->has_iterator) {+ emit_op(s, OP_iterator_close);+ i += 3;+ }+ for(; i < top->drop_count; i++)+ emit_op(s, OP_drop);+ if (top->label_finally != -1) {+ /* must push dummy value to keep same stack depth */+ emit_op(s, OP_undefined);+ emit_goto(s, OP_gosub, top->label_finally);+ emit_op(s, OP_drop);+ }+ top = top->prev;+ }+ if (name == JS_ATOM_NULL) {+ if (is_cont)+ return js_parse_error(s, "continue must be inside loop");+ else+ return js_parse_error(s, "break must be inside loop or switch");+ } else {+ return js_parse_error(s, "break/continue label not found");+ }+}++/* execute the finally blocks before return */+static void emit_return(JSParseState *s, BOOL hasval)+{+ BlockEnv *top;+ int drop_count;++ drop_count = 0;+ top = s->cur_func->top_break;+ while (top != NULL) {+ /* XXX: emit the appropriate OP_leave_scope opcodes? Probably not+ required as all local variables will be closed upon returning+ from JS_CallInternal, but not in the same order. */+ if (top->has_iterator) {+ /* with 'yield', the exact number of OP_drop to emit is+ unknown, so we use a specific operation to look for+ the catch offset */+ if (!hasval) {+ emit_op(s, OP_undefined);+ hasval = TRUE;+ }+ emit_op(s, OP_iterator_close_return);+ if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {+ int label_next, label_next2;++ emit_op(s, OP_drop); /* catch offset */+ emit_op(s, OP_drop); /* next */+ emit_op(s, OP_get_field2);+ emit_atom(s, JS_ATOM_return);+ /* stack: iter_obj return_func */+ emit_op(s, OP_dup);+ emit_op(s, OP_is_undefined_or_null);+ label_next = emit_goto(s, OP_if_true, -1);+ emit_op(s, OP_call_method);+ emit_u16(s, 0);+ emit_op(s, OP_iterator_check_object);+ emit_op(s, OP_await);+ label_next2 = emit_goto(s, OP_goto, -1);+ emit_label(s, label_next);+ emit_op(s, OP_drop);+ emit_label(s, label_next2);+ emit_op(s, OP_drop);+ } else {+ emit_op(s, OP_iterator_close);+ }+ drop_count = -3;+ }+ drop_count += top->drop_count;+ if (top->label_finally != -1) {+ while(drop_count) {+ /* must keep the stack top if hasval */+ emit_op(s, hasval ? OP_nip : OP_drop);+ drop_count--;+ }+ if (!hasval) {+ /* must push return value to keep same stack size */+ emit_op(s, OP_undefined);+ hasval = TRUE;+ }+ emit_goto(s, OP_gosub, top->label_finally);+ }+ top = top->prev;+ }+ if (s->cur_func->is_derived_class_constructor) {+ int label_return;++ /* 'this' can be uninitialized, so it may be accessed only if+ the derived class constructor does not return an object */+ if (hasval) {+ emit_op(s, OP_check_ctor_return);+ label_return = emit_goto(s, OP_if_false, -1);+ emit_op(s, OP_drop);+ } else {+ label_return = -1;+ }++ /* XXX: if this is not initialized, should throw the+ ReferenceError in the caller realm */+ emit_op(s, OP_scope_get_var);+ emit_atom(s, JS_ATOM_this);+ emit_u16(s, 0);++ emit_label(s, label_return);+ emit_op(s, OP_return);+ } else if (s->cur_func->func_kind != JS_FUNC_NORMAL) {+ if (!hasval) {+ emit_op(s, OP_undefined);+ } else if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {+ emit_op(s, OP_await);+ }+ emit_op(s, OP_return_async);+ } else {+ emit_op(s, hasval ? OP_return : OP_return_undef);+ }+}++#define DECL_MASK_FUNC (1 << 0) /* allow normal function declaration */+/* ored with DECL_MASK_FUNC if function declarations are allowed with a label */+#define DECL_MASK_FUNC_WITH_LABEL (1 << 1)+#define DECL_MASK_OTHER (1 << 2) /* all other declarations */+#define DECL_MASK_ALL (DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL | DECL_MASK_OTHER)++static __exception int js_parse_statement_or_decl(JSParseState *s,+ int decl_mask);++static __exception int js_parse_statement(JSParseState *s)+{+ return js_parse_statement_or_decl(s, 0);+}++static __exception int js_parse_block(JSParseState *s)+{+ if (js_parse_expect(s, '{'))+ return -1;+ if (s->token.val != '}') {+ push_scope(s);+ for(;;) {+ if (js_parse_statement_or_decl(s, DECL_MASK_ALL))+ return -1;+ if (s->token.val == '}')+ break;+ }+ pop_scope(s);+ }+ if (next_token(s))+ return -1;+ return 0;+}++/* allowed parse_flags: PF_IN_ACCEPTED */+static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok,+ BOOL export_flag)+{+ JSContext *ctx = s->ctx;+ JSFunctionDef *fd = s->cur_func;+ JSAtom name = JS_ATOM_NULL;++ for (;;) {+ if (s->token.val == TOK_IDENT) {+ if (s->token.u.ident.is_reserved) {+ return js_parse_error_reserved_identifier(s);+ }+ name = JS_DupAtom(ctx, s->token.u.ident.atom);+ if (name == JS_ATOM_let && (tok == TOK_LET || tok == TOK_CONST)) {+ js_parse_error(s, "'let' is not a valid lexical identifier");+ goto var_error;+ }+ if (next_token(s))+ goto var_error;+ if (js_define_var(s, name, tok))+ goto var_error;+ if (export_flag) {+ if (!add_export_entry(s, s->cur_func->module, name, name,+ JS_EXPORT_TYPE_LOCAL))+ goto var_error;+ }++ if (s->token.val == '=') {+ if (next_token(s))+ goto var_error;+ if (tok == TOK_VAR) {+ /* Must make a reference for proper `with` semantics */+ int opcode, scope, label;+ JSAtom name1;++ emit_op(s, OP_scope_get_var);+ emit_atom(s, name);+ emit_u16(s, fd->scope_level);+ if (get_lvalue(s, &opcode, &scope, &name1, &label, NULL, FALSE, '=') < 0)+ goto var_error;+ if (js_parse_assign_expr2(s, parse_flags)) {+ JS_FreeAtom(ctx, name1);+ goto var_error;+ }+ set_object_name(s, name);+ put_lvalue(s, opcode, scope, name1, label,+ PUT_LVALUE_NOKEEP, FALSE);+ } else {+ if (js_parse_assign_expr2(s, parse_flags))+ goto var_error;+ set_object_name(s, name);+ emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ?+ OP_scope_put_var_init : OP_scope_put_var);+ emit_atom(s, name);+ emit_u16(s, fd->scope_level);+ }+ } else {+ if (tok == TOK_CONST) {+ js_parse_error(s, "missing initializer for const variable");+ goto var_error;+ }+ if (tok == TOK_LET) {+ /* initialize lexical variable upon entering its scope */+ emit_op(s, OP_undefined);+ emit_op(s, OP_scope_put_var_init);+ emit_atom(s, name);+ emit_u16(s, fd->scope_level);+ }+ }+ JS_FreeAtom(ctx, name);+ } else {+ int skip_bits;+ if ((s->token.val == '[' || s->token.val == '{')+ && js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') {+ emit_op(s, OP_undefined);+ if (js_parse_destructuring_element(s, tok, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)+ return -1;+ } else {+ return js_parse_error(s, "variable name expected");+ }+ }+ if (s->token.val != ',')+ break;+ if (next_token(s))+ return -1;+ }+ return 0;++ var_error:+ JS_FreeAtom(ctx, name);+ return -1;+}++/* test if the current token is a label. Use simplistic look-ahead scanner */+static BOOL is_label(JSParseState *s)+{+ return (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved &&+ peek_token(s, FALSE) == ':');+}++/* test if the current token is a let keyword. Use simplistic look-ahead scanner */+static int is_let(JSParseState *s, int decl_mask)+{+ int res = FALSE;++ if (token_is_pseudo_keyword(s, JS_ATOM_let)) {+#if 1+ JSParsePos pos;+ js_parse_get_pos(s, &pos);+ for (;;) {+ if (next_token(s)) {+ res = -1;+ break;+ }+ if (s->token.val == '[') {+ /* let [ is a syntax restriction:+ it never introduces an ExpressionStatement */+ res = TRUE;+ break;+ }+ if (s->token.val == '{' ||+ (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved) ||+ s->token.val == TOK_LET ||+ s->token.val == TOK_YIELD ||+ s->token.val == TOK_AWAIT) {+ /* Check for possible ASI if not scanning for Declaration */+ /* XXX: should also check that `{` introduces a BindingPattern,+ but Firefox does not and rejects eval("let=1;let\n{if(1)2;}") */+ if (s->last_line_num == s->token.line_num || (decl_mask & DECL_MASK_OTHER)) {+ res = TRUE;+ break;+ }+ break;+ }+ break;+ }+ if (js_parse_seek_token(s, &pos)) {+ res = -1;+ }+#else+ int tok = peek_token(s, TRUE);+ if (tok == '{' || tok == TOK_IDENT || peek_token(s, FALSE) == '[') {+ res = TRUE;+ }+#endif+ }+ return res;+}++/* XXX: handle IteratorClose when exiting the loop before the+ enumeration is done */+static __exception int js_parse_for_in_of(JSParseState *s, int label_name,+ BOOL is_async)+{+ JSContext *ctx = s->ctx;+ JSFunctionDef *fd = s->cur_func;+ JSAtom var_name;+ BOOL has_initializer, is_for_of, has_destructuring;+ int tok, tok1, opcode, scope, block_scope_level;+ int label_next, label_expr, label_cont, label_body, label_break;+ int pos_next, pos_expr;+ BlockEnv break_entry;++ has_initializer = FALSE;+ has_destructuring = FALSE;+ is_for_of = FALSE;+ block_scope_level = fd->scope_level;+ label_cont = new_label(s);+ label_body = new_label(s);+ label_break = new_label(s);+ label_next = new_label(s);++ /* create scope for the lexical variables declared in the enumeration+ expressions. XXX: Not completely correct because of weird capturing+ semantics in `for (i of o) a.push(function(){return i})` */+ push_scope(s);++ /* local for_in scope starts here so individual elements+ can be closed in statement. */+ push_break_entry(s->cur_func, &break_entry,+ label_name, label_break, label_cont, 1);+ break_entry.scope_level = block_scope_level;++ label_expr = emit_goto(s, OP_goto, -1);++ pos_next = s->cur_func->byte_code.size;+ emit_label(s, label_next);++ tok = s->token.val;+ switch (is_let(s, DECL_MASK_OTHER)) {+ case TRUE:+ tok = TOK_LET;+ break;+ case FALSE:+ break;+ default:+ return -1;+ }+ if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) {+ if (next_token(s))+ return -1;++ if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) {+ if (s->token.val == '[' || s->token.val == '{') {+ if (js_parse_destructuring_element(s, tok, 0, TRUE, -1, FALSE) < 0)+ return -1;+ has_destructuring = TRUE;+ } else {+ return js_parse_error(s, "variable name expected");+ }+ var_name = JS_ATOM_NULL;+ } else {+ var_name = JS_DupAtom(ctx, s->token.u.ident.atom);+ if (next_token(s)) {+ JS_FreeAtom(s->ctx, var_name);+ return -1;+ }+ if (js_define_var(s, var_name, tok)) {+ JS_FreeAtom(s->ctx, var_name);+ return -1;+ }+ emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ?+ OP_scope_put_var_init : OP_scope_put_var);+ emit_atom(s, var_name);+ emit_u16(s, fd->scope_level);+ }+ } else {+ int skip_bits;+ if ((s->token.val == '[' || s->token.val == '{')+ && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == TOK_IN || tok1 == TOK_OF)) {+ if (js_parse_destructuring_element(s, 0, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)+ return -1;+ } else {+ int lvalue_label;+ if (js_parse_left_hand_side_expr(s))+ return -1;+ if (get_lvalue(s, &opcode, &scope, &var_name, &lvalue_label,+ NULL, FALSE, TOK_FOR))+ return -1;+ put_lvalue(s, opcode, scope, var_name, lvalue_label,+ PUT_LVALUE_NOKEEP_BOTTOM, FALSE);+ }+ var_name = JS_ATOM_NULL;+ }+ emit_goto(s, OP_goto, label_body);++ pos_expr = s->cur_func->byte_code.size;+ emit_label(s, label_expr);+ if (s->token.val == '=') {+ /* XXX: potential scoping issue if inside `with` statement */+ has_initializer = TRUE;+ /* parse and evaluate initializer prior to evaluating the+ object (only used with "for in" with a non lexical variable+ in non strict mode */+ if (next_token(s) || js_parse_assign_expr2(s, 0)) {+ JS_FreeAtom(ctx, var_name);+ return -1;+ }+ if (var_name != JS_ATOM_NULL) {+ emit_op(s, OP_scope_put_var);+ emit_atom(s, var_name);+ emit_u16(s, fd->scope_level);+ }+ }+ JS_FreeAtom(ctx, var_name);++ if (token_is_pseudo_keyword(s, JS_ATOM_of)) {+ break_entry.has_iterator = is_for_of = TRUE;+ break_entry.drop_count += 2;+ if (has_initializer)+ goto initializer_error;+ } else if (s->token.val == TOK_IN) {+ if (is_async)+ return js_parse_error(s, "'for await' loop should be used with 'of'");+ if (has_initializer &&+ (tok != TOK_VAR || (fd->js_mode & JS_MODE_STRICT) ||+ has_destructuring)) {+ initializer_error:+ return js_parse_error(s, "a declaration in the head of a for-%s loop can't have an initializer",+ is_for_of ? "of" : "in");+ }+ } else {+ return js_parse_error(s, "expected 'of' or 'in' in for control expression");+ }+ if (next_token(s))+ return -1;+ if (is_for_of) {+ if (js_parse_assign_expr(s))+ return -1;+ } else {+ if (js_parse_expr(s))+ return -1;+ }+ /* close the scope after having evaluated the expression so that+ the TDZ values are in the closures */+ close_scopes(s, s->cur_func->scope_level, block_scope_level);+ if (is_for_of) {+ if (is_async)+ emit_op(s, OP_for_await_of_start);+ else+ emit_op(s, OP_for_of_start);+ /* on stack: enum_rec */+ } else {+ emit_op(s, OP_for_in_start);+ /* on stack: enum_obj */+ }+ emit_goto(s, OP_goto, label_cont);++ if (js_parse_expect(s, ')'))+ return -1;++ if (OPTIMIZE) {+ /* move the `next` code here */+ DynBuf *bc = &s->cur_func->byte_code;+ int chunk_size = pos_expr - pos_next;+ int offset = bc->size - pos_next;+ int i;+ dbuf_realloc(bc, bc->size + chunk_size);+ dbuf_put(bc, bc->buf + pos_next, chunk_size);+ memset(bc->buf + pos_next, OP_nop, chunk_size);+ /* `next` part ends with a goto */+ s->cur_func->last_opcode_pos = bc->size - 5;+ /* relocate labels */+ for (i = label_cont; i < s->cur_func->label_count; i++) {+ LabelSlot *ls = &s->cur_func->label_slots[i];+ if (ls->pos >= pos_next && ls->pos < pos_expr)+ ls->pos += offset;+ }+ }++ emit_label(s, label_body);+ if (js_parse_statement(s))+ return -1;++ close_scopes(s, s->cur_func->scope_level, block_scope_level);++ emit_label(s, label_cont);+ if (is_for_of) {+ if (is_async) {+ /* call the next method */+ /* stack: iter_obj next catch_offset */+ emit_op(s, OP_dup3);+ emit_op(s, OP_drop);+ emit_op(s, OP_call_method);+ emit_u16(s, 0);+ /* get the result of the promise */+ emit_op(s, OP_await);+ /* unwrap the value and done values */+ emit_op(s, OP_iterator_get_value_done);+ } else {+ emit_op(s, OP_for_of_next);+ emit_u8(s, 0);+ }+ } else {+ emit_op(s, OP_for_in_next);+ }+ /* on stack: enum_rec / enum_obj value bool */+ emit_goto(s, OP_if_false, label_next);+ /* drop the undefined value from for_xx_next */+ emit_op(s, OP_drop);++ emit_label(s, label_break);+ if (is_for_of) {+ /* close and drop enum_rec */+ emit_op(s, OP_iterator_close);+ } else {+ emit_op(s, OP_drop);+ }+ pop_break_entry(s->cur_func);+ pop_scope(s);+ return 0;+}++static void set_eval_ret_undefined(JSParseState *s)+{+ if (s->cur_func->eval_ret_idx >= 0) {+ emit_op(s, OP_undefined);+ emit_op(s, OP_put_loc);+ emit_u16(s, s->cur_func->eval_ret_idx);+ }+}++static __exception int js_parse_statement_or_decl(JSParseState *s,+ int decl_mask)+{+ JSContext *ctx = s->ctx;+ JSAtom label_name;+ int tok;++ /* specific label handling */+ /* XXX: support multiple labels on loop statements */+ label_name = JS_ATOM_NULL;+ if (is_label(s)) {+ BlockEnv *be;++ label_name = JS_DupAtom(ctx, s->token.u.ident.atom);++ for (be = s->cur_func->top_break; be; be = be->prev) {+ if (be->label_name == label_name) {+ js_parse_error(s, "duplicate label name");+ goto fail;+ }+ }++ if (next_token(s))+ goto fail;+ if (js_parse_expect(s, ':'))+ goto fail;+ if (s->token.val != TOK_FOR+ && s->token.val != TOK_DO+ && s->token.val != TOK_WHILE) {+ /* labelled regular statement */+ int label_break, mask;+ BlockEnv break_entry;++ label_break = new_label(s);+ push_break_entry(s->cur_func, &break_entry,+ label_name, label_break, -1, 0);+ if (!(s->cur_func->js_mode & JS_MODE_STRICT) &&+ (decl_mask & DECL_MASK_FUNC_WITH_LABEL)) {+ mask = DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL;+ } else {+ mask = 0;+ }+ if (js_parse_statement_or_decl(s, mask))+ goto fail;+ emit_label(s, label_break);+ pop_break_entry(s->cur_func);+ goto done;+ }+ }++ switch(tok = s->token.val) {+ case '{':+ if (js_parse_block(s))+ goto fail;+ break;+ case TOK_RETURN:+ if (s->cur_func->is_eval) {+ js_parse_error(s, "return not in a function");+ goto fail;+ }+ if (next_token(s))+ goto fail;+ if (s->token.val != ';' && s->token.val != '}' && !s->got_lf) {+ if (js_parse_expr(s))+ goto fail;+ emit_return(s, TRUE);+ } else {+ emit_return(s, FALSE);+ }+ if (js_parse_expect_semi(s))+ goto fail;+ break;+ case TOK_THROW:+ if (next_token(s))+ goto fail;+ if (s->got_lf) {+ js_parse_error(s, "line terminator not allowed after throw");+ goto fail;+ }+ if (js_parse_expr(s))+ goto fail;+ emit_op(s, OP_throw);+ if (js_parse_expect_semi(s))+ goto fail;+ break;+ case TOK_LET:+ case TOK_CONST:+ haslet:+ if (!(decl_mask & DECL_MASK_OTHER)) {+ js_parse_error(s, "lexical declarations can't appear in single-statement context");+ goto fail;+ }+ /* fall thru */+ case TOK_VAR:+ if (next_token(s))+ goto fail;+ if (js_parse_var(s, TRUE, tok, FALSE))+ goto fail;+ if (js_parse_expect_semi(s))+ goto fail;+ break;+ case TOK_IF:+ {+ int label1, label2, mask;+ if (next_token(s))+ goto fail;+ /* create a new scope for `let f;if(1) function f(){}` */+ push_scope(s);+ set_eval_ret_undefined(s);+ if (js_parse_expr_paren(s))+ goto fail;+ label1 = emit_goto(s, OP_if_false, -1);+ if (s->cur_func->js_mode & JS_MODE_STRICT)+ mask = 0;+ else+ mask = DECL_MASK_FUNC; /* Annex B.3.4 */++ if (js_parse_statement_or_decl(s, mask))+ goto fail;++ if (s->token.val == TOK_ELSE) {+ label2 = emit_goto(s, OP_goto, -1);+ if (next_token(s))+ goto fail;++ emit_label(s, label1);+ if (js_parse_statement_or_decl(s, mask))+ goto fail;++ label1 = label2;+ }+ emit_label(s, label1);+ pop_scope(s);+ }+ break;+ case TOK_WHILE:+ {+ int label_cont, label_break;+ BlockEnv break_entry;++ label_cont = new_label(s);+ label_break = new_label(s);++ push_break_entry(s->cur_func, &break_entry,+ label_name, label_break, label_cont, 0);++ if (next_token(s))+ goto fail;++ set_eval_ret_undefined(s);++ emit_label(s, label_cont);+ if (js_parse_expr_paren(s))+ goto fail;+ emit_goto(s, OP_if_false, label_break);++ if (js_parse_statement(s))+ goto fail;+ emit_goto(s, OP_goto, label_cont);++ emit_label(s, label_break);++ pop_break_entry(s->cur_func);+ }+ break;+ case TOK_DO:+ {+ int label_cont, label_break, label1;+ BlockEnv break_entry;++ label_cont = new_label(s);+ label_break = new_label(s);+ label1 = new_label(s);++ push_break_entry(s->cur_func, &break_entry,+ label_name, label_break, label_cont, 0);++ if (next_token(s))+ goto fail;++ emit_label(s, label1);++ set_eval_ret_undefined(s);++ if (js_parse_statement(s))+ goto fail;++ emit_label(s, label_cont);+ if (js_parse_expect(s, TOK_WHILE))+ goto fail;+ if (js_parse_expr_paren(s))+ goto fail;+ /* Insert semicolon if missing */+ if (s->token.val == ';') {+ if (next_token(s))+ goto fail;+ }+ emit_goto(s, OP_if_true, label1);++ emit_label(s, label_break);++ pop_break_entry(s->cur_func);+ }+ break;+ case TOK_FOR:+ {+ int label_cont, label_break, label_body, label_test;+ int pos_cont, pos_body, block_scope_level;+ BlockEnv break_entry;+ int tok, bits;+ BOOL is_async;++ if (next_token(s))+ goto fail;++ set_eval_ret_undefined(s);+ bits = 0;+ is_async = FALSE;+ if (s->token.val == '(') {+ js_parse_skip_parens_token(s, &bits, FALSE);+ } else if (s->token.val == TOK_AWAIT) {+ if (!(s->cur_func->func_kind & JS_FUNC_ASYNC)) {+ js_parse_error(s, "for await is only valid in asynchronous functions");+ goto fail;+ }+ is_async = TRUE;+ if (next_token(s))+ goto fail;+ }+ if (js_parse_expect(s, '('))+ goto fail;++ if (!(bits & SKIP_HAS_SEMI)) {+ /* parse for/in or for/of */+ if (js_parse_for_in_of(s, label_name, is_async))+ goto fail;+ break;+ }+ block_scope_level = s->cur_func->scope_level;++ /* create scope for the lexical variables declared in the initial,+ test and increment expressions */+ push_scope(s);+ /* initial expression */+ tok = s->token.val;+ if (tok != ';') {+ switch (is_let(s, DECL_MASK_OTHER)) {+ case TRUE:+ tok = TOK_LET;+ break;+ case FALSE:+ break;+ default:+ goto fail;+ }+ if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) {+ if (next_token(s))+ goto fail;+ if (js_parse_var(s, FALSE, tok, FALSE))+ goto fail;+ } else {+ if (js_parse_expr2(s, FALSE))+ goto fail;+ emit_op(s, OP_drop);+ }++ /* close the closures before the first iteration */+ close_scopes(s, s->cur_func->scope_level, block_scope_level);+ }+ if (js_parse_expect(s, ';'))+ goto fail;++ label_test = new_label(s);+ label_cont = new_label(s);+ label_body = new_label(s);+ label_break = new_label(s);++ push_break_entry(s->cur_func, &break_entry,+ label_name, label_break, label_cont, 0);++ /* test expression */+ if (s->token.val == ';') {+ /* no test expression */+ label_test = label_body;+ } else {+ emit_label(s, label_test);+ if (js_parse_expr(s))+ goto fail;+ emit_goto(s, OP_if_false, label_break);+ }+ if (js_parse_expect(s, ';'))+ goto fail;++ if (s->token.val == ')') {+ /* no end expression */+ break_entry.label_cont = label_cont = label_test;+ pos_cont = 0; /* avoid warning */+ } else {+ /* skip the end expression */+ emit_goto(s, OP_goto, label_body);++ pos_cont = s->cur_func->byte_code.size;+ emit_label(s, label_cont);+ if (js_parse_expr(s))+ goto fail;+ emit_op(s, OP_drop);+ if (label_test != label_body)+ emit_goto(s, OP_goto, label_test);+ }+ if (js_parse_expect(s, ')'))+ goto fail;++ pos_body = s->cur_func->byte_code.size;+ emit_label(s, label_body);+ if (js_parse_statement(s))+ goto fail;++ /* close the closures before the next iteration */+ /* XXX: check continue case */+ close_scopes(s, s->cur_func->scope_level, block_scope_level);++ if (OPTIMIZE && label_test != label_body && label_cont != label_test) {+ /* move the increment code here */+ DynBuf *bc = &s->cur_func->byte_code;+ int chunk_size = pos_body - pos_cont;+ int offset = bc->size - pos_cont;+ int i;+ dbuf_realloc(bc, bc->size + chunk_size);+ dbuf_put(bc, bc->buf + pos_cont, chunk_size);+ memset(bc->buf + pos_cont, OP_nop, chunk_size);+ /* increment part ends with a goto */+ s->cur_func->last_opcode_pos = bc->size - 5;+ /* relocate labels */+ for (i = label_cont; i < s->cur_func->label_count; i++) {+ LabelSlot *ls = &s->cur_func->label_slots[i];+ if (ls->pos >= pos_cont && ls->pos < pos_body)+ ls->pos += offset;+ }+ } else {+ emit_goto(s, OP_goto, label_cont);+ }++ emit_label(s, label_break);++ pop_break_entry(s->cur_func);+ pop_scope(s);+ }+ break;+ case TOK_BREAK:+ case TOK_CONTINUE:+ {+ int is_cont = s->token.val - TOK_BREAK;+ int label;++ if (next_token(s))+ goto fail;+ if (!s->got_lf && s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)+ label = s->token.u.ident.atom;+ else+ label = JS_ATOM_NULL;+ if (emit_break(s, label, is_cont))+ goto fail;+ if (label != JS_ATOM_NULL) {+ if (next_token(s))+ goto fail;+ }+ if (js_parse_expect_semi(s))+ goto fail;+ }+ break;+ case TOK_SWITCH:+ {+ int label_case, label_break, label1;+ int default_label_pos;+ BlockEnv break_entry;++ if (next_token(s))+ goto fail;++ set_eval_ret_undefined(s);+ if (js_parse_expr_paren(s))+ goto fail;++ push_scope(s);+ label_break = new_label(s);+ push_break_entry(s->cur_func, &break_entry,+ label_name, label_break, -1, 1);++ if (js_parse_expect(s, '{'))+ goto fail;++ default_label_pos = -1;+ label_case = -1;+ while (s->token.val != '}') {+ if (s->token.val == TOK_CASE) {+ label1 = -1;+ if (label_case >= 0) {+ /* skip the case if needed */+ label1 = emit_goto(s, OP_goto, -1);+ }+ emit_label(s, label_case);+ label_case = -1;+ for (;;) {+ /* parse a sequence of case clauses */+ if (next_token(s))+ goto fail;+ emit_op(s, OP_dup);+ if (js_parse_expr(s))+ goto fail;+ if (js_parse_expect(s, ':'))+ goto fail;+ emit_op(s, OP_strict_eq);+ if (s->token.val == TOK_CASE) {+ label1 = emit_goto(s, OP_if_true, label1);+ } else {+ label_case = emit_goto(s, OP_if_false, -1);+ emit_label(s, label1);+ break;+ }+ }+ } else if (s->token.val == TOK_DEFAULT) {+ if (next_token(s))+ goto fail;+ if (js_parse_expect(s, ':'))+ goto fail;+ if (default_label_pos >= 0) {+ js_parse_error(s, "duplicate default");+ goto fail;+ }+ if (label_case < 0) {+ /* falling thru direct from switch expression */+ label_case = emit_goto(s, OP_goto, -1);+ }+ /* Emit a dummy label opcode. Label will be patched after+ the end of the switch body. Do not use emit_label(s, 0)+ because it would clobber label 0 address, preventing+ proper optimizer operation.+ */+ emit_op(s, OP_label);+ emit_u32(s, 0);+ default_label_pos = s->cur_func->byte_code.size - 4;+ } else {+ if (label_case < 0) {+ /* falling thru direct from switch expression */+ js_parse_error(s, "invalid switch statement");+ goto fail;+ }+ if (js_parse_statement_or_decl(s, DECL_MASK_ALL))+ goto fail;+ }+ }+ if (js_parse_expect(s, '}'))+ goto fail;+ if (default_label_pos >= 0) {+ /* Ugly patch for the the `default` label, shameful and risky */+ put_u32(s->cur_func->byte_code.buf + default_label_pos,+ label_case);+ s->cur_func->label_slots[label_case].pos = default_label_pos + 4;+ } else {+ emit_label(s, label_case);+ }+ emit_label(s, label_break);+ emit_op(s, OP_drop); /* drop the switch expression */++ pop_break_entry(s->cur_func);+ pop_scope(s);+ }+ break;+ case TOK_TRY:+ {+ int label_catch, label_catch2, label_finally, label_end;+ JSAtom name;+ BlockEnv block_env;++ set_eval_ret_undefined(s);+ if (next_token(s))+ goto fail;+ label_catch = new_label(s);+ label_catch2 = new_label(s);+ label_finally = new_label(s);+ label_end = new_label(s);++ emit_goto(s, OP_catch, label_catch);++ push_break_entry(s->cur_func, &block_env,+ JS_ATOM_NULL, -1, -1, 1);+ block_env.label_finally = label_finally;++ if (js_parse_block(s))+ goto fail;++ pop_break_entry(s->cur_func);++ if (js_is_live_code(s)) {+ /* drop the catch offset */+ emit_op(s, OP_drop);+ /* must push dummy value to keep same stack size */+ emit_op(s, OP_undefined);+ emit_goto(s, OP_gosub, label_finally);+ emit_op(s, OP_drop);++ emit_goto(s, OP_goto, label_end);+ }++ if (s->token.val == TOK_CATCH) {+ if (next_token(s))+ goto fail;++ push_scope(s); /* catch variable */+ emit_label(s, label_catch);++ if (s->token.val == '{') {+ /* support optional-catch-binding feature */+ emit_op(s, OP_drop); /* pop the exception object */+ } else {+ if (js_parse_expect(s, '('))+ goto fail;+ if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) {+ if (s->token.val == '[' || s->token.val == '{') {+ /* XXX: TOK_LET is not completely correct */+ if (js_parse_destructuring_element(s, TOK_LET, 0, TRUE, -1, TRUE) < 0)+ goto fail;+ } else {+ js_parse_error(s, "identifier expected");+ goto fail;+ }+ } else {+ name = JS_DupAtom(ctx, s->token.u.ident.atom);+ if (next_token(s)+ || js_define_var(s, name, TOK_CATCH) < 0) {+ JS_FreeAtom(ctx, name);+ goto fail;+ }+ /* store the exception value in the catch variable */+ emit_op(s, OP_scope_put_var);+ emit_u32(s, name);+ emit_u16(s, s->cur_func->scope_level);+ }+ if (js_parse_expect(s, ')'))+ goto fail;+ }+ /* XXX: should keep the address to nop it out if there is no finally block */+ emit_goto(s, OP_catch, label_catch2);++ push_scope(s); /* catch block */+ push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL,+ -1, -1, 1);+ block_env.label_finally = label_finally;++ if (js_parse_block(s))+ goto fail;++ pop_break_entry(s->cur_func);+ pop_scope(s); /* catch block */+ pop_scope(s); /* catch variable */++ if (js_is_live_code(s)) {+ /* drop the catch2 offset */+ emit_op(s, OP_drop);+ /* XXX: should keep the address to nop it out if there is no finally block */+ /* must push dummy value to keep same stack size */+ emit_op(s, OP_undefined);+ emit_goto(s, OP_gosub, label_finally);+ emit_op(s, OP_drop);+ emit_goto(s, OP_goto, label_end);+ }+ /* catch exceptions thrown in the catch block to execute the+ * finally clause and rethrow the exception */+ emit_label(s, label_catch2);+ /* catch value is at TOS, no need to push undefined */+ emit_goto(s, OP_gosub, label_finally);+ emit_op(s, OP_throw);++ } else if (s->token.val == TOK_FINALLY) {+ /* finally without catch : execute the finally clause+ * and rethrow the exception */+ emit_label(s, label_catch);+ /* catch value is at TOS, no need to push undefined */+ emit_goto(s, OP_gosub, label_finally);+ emit_op(s, OP_throw);+ } else {+ js_parse_error(s, "expecting catch or finally");+ goto fail;+ }+ emit_label(s, label_finally);+ if (s->token.val == TOK_FINALLY) {+ int saved_eval_ret_idx = 0; /* avoid warning */+ + if (next_token(s))+ goto fail;+ /* on the stack: ret_value gosub_ret_value */+ push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL,+ -1, -1, 2);++ if (s->cur_func->eval_ret_idx >= 0) {+ /* 'finally' updates eval_ret only if not a normal+ termination */+ saved_eval_ret_idx =+ add_var(s->ctx, s->cur_func, JS_ATOM__ret_);+ if (saved_eval_ret_idx < 0)+ goto fail;+ emit_op(s, OP_get_loc);+ emit_u16(s, s->cur_func->eval_ret_idx);+ emit_op(s, OP_put_loc);+ emit_u16(s, saved_eval_ret_idx);+ set_eval_ret_undefined(s);+ }+ + if (js_parse_block(s))+ goto fail;++ if (s->cur_func->eval_ret_idx >= 0) {+ emit_op(s, OP_get_loc);+ emit_u16(s, saved_eval_ret_idx);+ emit_op(s, OP_put_loc);+ emit_u16(s, s->cur_func->eval_ret_idx);+ }+ pop_break_entry(s->cur_func);+ }+ emit_op(s, OP_ret);+ emit_label(s, label_end);+ }+ break;+ case ';':+ /* empty statement */+ if (next_token(s))+ goto fail;+ break;+ case TOK_WITH:+ if (s->cur_func->js_mode & JS_MODE_STRICT) {+ js_parse_error(s, "invalid keyword: with");+ goto fail;+ } else {+ int with_idx;++ if (next_token(s))+ goto fail;++ if (js_parse_expr_paren(s))+ goto fail;++ push_scope(s);+ with_idx = define_var(s, s->cur_func, JS_ATOM__with_,+ JS_VAR_DEF_WITH);+ if (with_idx < 0)+ goto fail;+ emit_op(s, OP_to_object);+ emit_op(s, OP_put_loc);+ emit_u16(s, with_idx);++ set_eval_ret_undefined(s);+ if (js_parse_statement(s))+ goto fail;++ /* Popping scope drops lexical context for the with object variable */+ pop_scope(s);+ }+ break;+ case TOK_FUNCTION:+ /* ES6 Annex B.3.2 and B.3.3 semantics */+ if (!(decl_mask & DECL_MASK_FUNC))+ goto func_decl_error;+ if (!(decl_mask & DECL_MASK_OTHER) && peek_token(s, FALSE) == '*')+ goto func_decl_error;+ goto parse_func_var;+ case TOK_IDENT:+ if (s->token.u.ident.is_reserved) {+ js_parse_error_reserved_identifier(s);+ goto fail;+ }+ /* Determine if `let` introduces a Declaration or an ExpressionStatement */+ switch (is_let(s, decl_mask)) {+ case TRUE:+ tok = TOK_LET;+ goto haslet;+ case FALSE:+ break;+ default:+ goto fail;+ }+ if (token_is_pseudo_keyword(s, JS_ATOM_async) &&+ peek_token(s, TRUE) == TOK_FUNCTION) {+ if (!(decl_mask & DECL_MASK_OTHER)) {+ func_decl_error:+ js_parse_error(s, "function declarations can't appear in single-statement context");+ goto fail;+ }+ parse_func_var:+ if (js_parse_function_decl(s, JS_PARSE_FUNC_VAR,+ JS_FUNC_NORMAL, JS_ATOM_NULL,+ s->token.ptr, s->token.line_num))+ goto fail;+ break;+ }+ goto hasexpr;++ case TOK_CLASS:+ if (!(decl_mask & DECL_MASK_OTHER)) {+ js_parse_error(s, "class declarations can't appear in single-statement context");+ goto fail;+ }+ if (js_parse_class(s, FALSE, JS_PARSE_EXPORT_NONE))+ return -1;+ break;++ case TOK_DEBUGGER:+ /* currently no debugger, so just skip the keyword */+ if (next_token(s))+ goto fail;+ if (js_parse_expect_semi(s))+ goto fail;+ break;+ + case TOK_ENUM:+ case TOK_EXPORT:+ case TOK_EXTENDS:+ js_unsupported_keyword(s, s->token.u.ident.atom);+ goto fail;++ default:+ hasexpr:+ if (js_parse_expr(s))+ goto fail;+ if (s->cur_func->eval_ret_idx >= 0) {+ /* store the expression value so that it can be returned+ by eval() */+ emit_op(s, OP_put_loc);+ emit_u16(s, s->cur_func->eval_ret_idx);+ } else {+ emit_op(s, OP_drop); /* drop the result */+ }+ if (js_parse_expect_semi(s))+ goto fail;+ break;+ }+done:+ JS_FreeAtom(ctx, label_name);+ return 0;+fail:+ JS_FreeAtom(ctx, label_name);+ return -1;+}++/* 'name' is freed */+static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name)+{+ JSModuleDef *m;+ m = js_mallocz(ctx, sizeof(*m));+ if (!m) {+ JS_FreeAtom(ctx, name);+ return NULL;+ }+ m->header.ref_count = 1;+ m->module_name = name;+ m->module_ns = JS_UNDEFINED;+ m->func_obj = JS_UNDEFINED;+ m->eval_exception = JS_UNDEFINED;+ m->meta_obj = JS_UNDEFINED;+ list_add_tail(&m->link, &ctx->loaded_modules);+ return m;+}++static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,+ JS_MarkFunc *mark_func)+{+ int i;++ for(i = 0; i < m->export_entries_count; i++) {+ JSExportEntry *me = &m->export_entries[i];+ if (me->export_type == JS_EXPORT_TYPE_LOCAL &&+ me->u.local.var_ref) {+ mark_func(rt, &me->u.local.var_ref->header);+ }+ }++ JS_MarkValue(rt, m->module_ns, mark_func);+ JS_MarkValue(rt, m->func_obj, mark_func);+ JS_MarkValue(rt, m->eval_exception, mark_func);+ JS_MarkValue(rt, m->meta_obj, mark_func);+}++static void js_free_module_def(JSContext *ctx, JSModuleDef *m)+{+ int i;++ JS_FreeAtom(ctx, m->module_name);++ for(i = 0; i < m->req_module_entries_count; i++) {+ JSReqModuleEntry *rme = &m->req_module_entries[i];+ JS_FreeAtom(ctx, rme->module_name);+ }+ js_free(ctx, m->req_module_entries);++ for(i = 0; i < m->export_entries_count; i++) {+ JSExportEntry *me = &m->export_entries[i];+ if (me->export_type == JS_EXPORT_TYPE_LOCAL)+ free_var_ref(ctx->rt, me->u.local.var_ref);+ JS_FreeAtom(ctx, me->export_name);+ JS_FreeAtom(ctx, me->local_name);+ }+ js_free(ctx, m->export_entries);++ js_free(ctx, m->star_export_entries);++ for(i = 0; i < m->import_entries_count; i++) {+ JSImportEntry *mi = &m->import_entries[i];+ JS_FreeAtom(ctx, mi->import_name);+ }+ js_free(ctx, m->import_entries);++ JS_FreeValue(ctx, m->module_ns);+ JS_FreeValue(ctx, m->func_obj);+ JS_FreeValue(ctx, m->eval_exception);+ JS_FreeValue(ctx, m->meta_obj);+ list_del(&m->link);+ js_free(ctx, m);+}++static int add_req_module_entry(JSContext *ctx, JSModuleDef *m,+ JSAtom module_name)+{+ JSReqModuleEntry *rme;+ int i;++ /* no need to add the module request if it is already present */+ for(i = 0; i < m->req_module_entries_count; i++) {+ rme = &m->req_module_entries[i];+ if (rme->module_name == module_name)+ return i;+ }++ if (js_resize_array(ctx, (void **)&m->req_module_entries,+ sizeof(JSReqModuleEntry),+ &m->req_module_entries_size,+ m->req_module_entries_count + 1))+ return -1;+ rme = &m->req_module_entries[m->req_module_entries_count++];+ rme->module_name = JS_DupAtom(ctx, module_name);+ rme->module = NULL;+ return i;+}++static JSExportEntry *find_export_entry(JSContext *ctx, JSModuleDef *m,+ JSAtom export_name)+{+ JSExportEntry *me;+ int i;+ for(i = 0; i < m->export_entries_count; i++) {+ me = &m->export_entries[i];+ if (me->export_name == export_name)+ return me;+ }+ return NULL;+}++static JSExportEntry *add_export_entry2(JSContext *ctx,+ JSParseState *s, JSModuleDef *m,+ JSAtom local_name, JSAtom export_name,+ JSExportTypeEnum export_type)+{+ JSExportEntry *me;++ if (find_export_entry(ctx, m, export_name)) {+ char buf1[ATOM_GET_STR_BUF_SIZE];+ if (s) {+ js_parse_error(s, "duplicate exported name '%s'",+ JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name));+ } else {+ JS_ThrowSyntaxErrorAtom(ctx, "duplicate exported name '%s'", export_name);+ }+ return NULL;+ }++ if (js_resize_array(ctx, (void **)&m->export_entries,+ sizeof(JSExportEntry),+ &m->export_entries_size,+ m->export_entries_count + 1))+ return NULL;+ me = &m->export_entries[m->export_entries_count++];+ memset(me, 0, sizeof(*me));+ me->local_name = JS_DupAtom(ctx, local_name);+ me->export_name = JS_DupAtom(ctx, export_name);+ me->export_type = export_type;+ return me;+}++static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m,+ JSAtom local_name, JSAtom export_name,+ JSExportTypeEnum export_type)+{+ return add_export_entry2(s->ctx, s, m, local_name, export_name,+ export_type);+}++static int add_star_export_entry(JSContext *ctx, JSModuleDef *m,+ int req_module_idx)+{+ JSStarExportEntry *se;++ if (js_resize_array(ctx, (void **)&m->star_export_entries,+ sizeof(JSStarExportEntry),+ &m->star_export_entries_size,+ m->star_export_entries_count + 1))+ return -1;+ se = &m->star_export_entries[m->star_export_entries_count++];+ se->req_module_idx = req_module_idx;+ return 0;+}++/* create a C module */+JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str,+ JSModuleInitFunc *func)+{+ JSModuleDef *m;+ JSAtom name;+ name = JS_NewAtom(ctx, name_str);+ if (name == JS_ATOM_NULL)+ return NULL;+ m = js_new_module_def(ctx, name);+ m->init_func = func;+ return m;+}++int JS_AddModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name)+{+ JSExportEntry *me;+ JSAtom name;+ name = JS_NewAtom(ctx, export_name);+ if (name == JS_ATOM_NULL)+ return -1;+ me = add_export_entry2(ctx, NULL, m, JS_ATOM_NULL, name,+ JS_EXPORT_TYPE_LOCAL);+ JS_FreeAtom(ctx, name);+ if (!me)+ return -1;+ else+ return 0;+}++int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name,+ JSValue val)+{+ JSExportEntry *me;+ JSAtom name;+ name = JS_NewAtom(ctx, export_name);+ if (name == JS_ATOM_NULL)+ goto fail;+ me = find_export_entry(ctx, m, name);+ JS_FreeAtom(ctx, name);+ if (!me)+ goto fail;+ set_value(ctx, me->u.local.var_ref->pvalue, val);+ return 0;+ fail:+ JS_FreeValue(ctx, val);+ return -1;+}++void JS_SetModuleLoaderFunc(JSRuntime *rt,+ JSModuleNormalizeFunc *module_normalize,+ JSModuleLoaderFunc *module_loader, void *opaque)+{+ rt->module_normalize_func = module_normalize;+ rt->module_loader_func = module_loader;+ rt->module_loader_opaque = opaque;+}++/* default module filename normalizer */+static char *js_default_module_normalize_name(JSContext *ctx,+ const char *base_name,+ const char *name)+{+ char *filename, *p;+ const char *r;+ int len;++ if (name[0] != '.') {+ /* if no initial dot, the module name is not modified */+ return js_strdup(ctx, name);+ }++ p = strrchr(base_name, '/');+ if (p)+ len = p - base_name;+ else+ len = 0;++ filename = js_malloc(ctx, len + strlen(name) + 1 + 1);+ if (!filename)+ return NULL;+ memcpy(filename, base_name, len);+ filename[len] = '\0';++ /* we only normalize the leading '..' or '.' */+ r = name;+ for(;;) {+ if (r[0] == '.' && r[1] == '/') {+ r += 2;+ } else if (r[0] == '.' && r[1] == '.' && r[2] == '/') {+ /* remove the last path element of filename, except if "."+ or ".." */+ if (filename[0] == '\0')+ break;+ p = strrchr(filename, '/');+ if (!p)+ p = filename;+ else+ p++;+ if (!strcmp(p, ".") || !strcmp(p, ".."))+ break;+ if (p > filename)+ p--;+ *p = '\0';+ r += 3;+ } else {+ break;+ }+ }+ if (filename[0] != '\0')+ strcat(filename, "/");+ strcat(filename, r);+ // printf("normalize: %s %s -> %s\n", base_name, name, filename);+ return filename;+}++static JSModuleDef *js_find_loaded_module(JSContext *ctx, JSAtom name)+{+ struct list_head *el;+ JSModuleDef *m;+ + /* first look at the loaded modules */+ list_for_each(el, &ctx->loaded_modules) {+ m = list_entry(el, JSModuleDef, link);+ if (m->module_name == name)+ return m;+ }+ return NULL;+}++/* return NULL in case of exception (e.g. module could not be loaded) */+static JSModuleDef *js_host_resolve_imported_module(JSContext *ctx,+ const char *base_cname,+ const char *cname1)+{+ JSRuntime *rt = ctx->rt;+ JSModuleDef *m;+ char *cname;+ JSAtom module_name;++ if (!rt->module_normalize_func) {+ cname = js_default_module_normalize_name(ctx, base_cname, cname1);+ } else {+ cname = rt->module_normalize_func(ctx, base_cname, cname1,+ rt->module_loader_opaque);+ }+ if (!cname)+ return NULL;++ module_name = JS_NewAtom(ctx, cname);+ if (module_name == JS_ATOM_NULL) {+ js_free(ctx, cname);+ return NULL;+ }++ /* first look at the loaded modules */+ m = js_find_loaded_module(ctx, module_name);+ if (m) {+ js_free(ctx, cname);+ JS_FreeAtom(ctx, module_name);+ return m;+ }++ JS_FreeAtom(ctx, module_name);++ /* load the module */+ if (!rt->module_loader_func) {+ /* XXX: use a syntax error ? */+ JS_ThrowReferenceError(ctx, "could not load module '%s'",+ cname);+ js_free(ctx, cname);+ return NULL;+ }++ m = rt->module_loader_func(ctx, cname, rt->module_loader_opaque);+ js_free(ctx, cname);+ return m;+}++static JSModuleDef *js_host_resolve_imported_module_atom(JSContext *ctx,+ JSAtom base_module_name,+ JSAtom module_name1)+{+ const char *base_cname, *cname;+ JSModuleDef *m;+ + base_cname = JS_AtomToCString(ctx, base_module_name);+ if (!base_cname)+ return NULL;+ cname = JS_AtomToCString(ctx, module_name1);+ if (!cname) {+ JS_FreeCString(ctx, base_cname);+ return NULL;+ }+ m = js_host_resolve_imported_module(ctx, base_cname, cname);+ JS_FreeCString(ctx, base_cname);+ JS_FreeCString(ctx, cname);+ return m;+}++typedef struct JSResolveEntry {+ JSModuleDef *module;+ JSAtom name;+} JSResolveEntry;++typedef struct JSResolveState {+ JSResolveEntry *array;+ int size;+ int count;+} JSResolveState;++static int find_resolve_entry(JSResolveState *s,+ JSModuleDef *m, JSAtom name)+{+ int i;+ for(i = 0; i < s->count; i++) {+ JSResolveEntry *re = &s->array[i];+ if (re->module == m && re->name == name)+ return i;+ }+ return -1;+}++static int add_resolve_entry(JSContext *ctx, JSResolveState *s,+ JSModuleDef *m, JSAtom name)+{+ JSResolveEntry *re;++ if (js_resize_array(ctx, (void **)&s->array,+ sizeof(JSResolveEntry),+ &s->size, s->count + 1))+ return -1;+ re = &s->array[s->count++];+ re->module = m;+ re->name = JS_DupAtom(ctx, name);+ return 0;+}++typedef enum JSResolveResultEnum {+ JS_RESOLVE_RES_EXCEPTION = -1, /* memory alloc error */+ JS_RESOLVE_RES_FOUND = 0,+ JS_RESOLVE_RES_NOT_FOUND,+ JS_RESOLVE_RES_CIRCULAR,+ JS_RESOLVE_RES_AMBIGUOUS,+} JSResolveResultEnum;++static JSResolveResultEnum js_resolve_export1(JSContext *ctx,+ JSModuleDef **pmodule,+ JSExportEntry **pme,+ JSModuleDef *m,+ JSAtom export_name,+ JSResolveState *s)+{+ JSExportEntry *me;++ *pmodule = NULL;+ *pme = NULL;+ if (find_resolve_entry(s, m, export_name) >= 0)+ return JS_RESOLVE_RES_CIRCULAR;+ if (add_resolve_entry(ctx, s, m, export_name) < 0)+ return JS_RESOLVE_RES_EXCEPTION;+ me = find_export_entry(ctx, m, export_name);+ if (me) {+ if (me->export_type == JS_EXPORT_TYPE_LOCAL) {+ /* local export */+ *pmodule = m;+ *pme = me;+ return JS_RESOLVE_RES_FOUND;+ } else {+ /* indirect export */+ JSModuleDef *m1;+ m1 = m->req_module_entries[me->u.req_module_idx].module;+ if (me->local_name == JS_ATOM__star_) {+ /* export ns from */+ *pmodule = m;+ *pme = me;+ return JS_RESOLVE_RES_FOUND;+ } else {+ return js_resolve_export1(ctx, pmodule, pme, m1,+ me->local_name, s);+ }+ }+ } else {+ if (export_name != JS_ATOM_default) {+ /* not found in direct or indirect exports: try star exports */+ int i;++ for(i = 0; i < m->star_export_entries_count; i++) {+ JSStarExportEntry *se = &m->star_export_entries[i];+ JSModuleDef *m1, *res_m;+ JSExportEntry *res_me;+ JSResolveResultEnum ret;++ m1 = m->req_module_entries[se->req_module_idx].module;+ ret = js_resolve_export1(ctx, &res_m, &res_me, m1,+ export_name, s);+ if (ret == JS_RESOLVE_RES_AMBIGUOUS ||+ ret == JS_RESOLVE_RES_EXCEPTION) {+ return ret;+ } else if (ret == JS_RESOLVE_RES_FOUND) {+ if (*pme != NULL) {+ if (*pmodule != res_m ||+ res_me->local_name != (*pme)->local_name) {+ *pmodule = NULL;+ *pme = NULL;+ return JS_RESOLVE_RES_AMBIGUOUS;+ }+ } else {+ *pmodule = res_m;+ *pme = res_me;+ }+ }+ }+ if (*pme != NULL)+ return JS_RESOLVE_RES_FOUND;+ }+ return JS_RESOLVE_RES_NOT_FOUND;+ }+}++/* If the return value is JS_RESOLVE_RES_FOUND, return the module+ (*pmodule) and the corresponding local export entry+ (*pme). Otherwise return (NULL, NULL) */+static JSResolveResultEnum js_resolve_export(JSContext *ctx,+ JSModuleDef **pmodule,+ JSExportEntry **pme,+ JSModuleDef *m,+ JSAtom export_name)+{+ JSResolveState ss, *s = &ss;+ int i;+ JSResolveResultEnum ret;++ s->array = NULL;+ s->size = 0;+ s->count = 0;++ ret = js_resolve_export1(ctx, pmodule, pme, m, export_name, s);++ for(i = 0; i < s->count; i++)+ JS_FreeAtom(ctx, s->array[i].name);+ js_free(ctx, s->array);++ return ret;+}++static void js_resolve_export_throw_error(JSContext *ctx,+ JSResolveResultEnum res,+ JSModuleDef *m, JSAtom export_name)+{+ char buf1[ATOM_GET_STR_BUF_SIZE];+ char buf2[ATOM_GET_STR_BUF_SIZE];+ switch(res) {+ case JS_RESOLVE_RES_EXCEPTION:+ break;+ default:+ case JS_RESOLVE_RES_NOT_FOUND:+ JS_ThrowSyntaxError(ctx, "Could not find export '%s' in module '%s'",+ JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),+ JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));+ break;+ case JS_RESOLVE_RES_CIRCULAR:+ JS_ThrowSyntaxError(ctx, "circular reference when looking for export '%s' in module '%s'",+ JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),+ JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));+ break;+ case JS_RESOLVE_RES_AMBIGUOUS:+ JS_ThrowSyntaxError(ctx, "export '%s' in module '%s' is ambiguous",+ JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),+ JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));+ break;+ }+}+++typedef enum {+ EXPORTED_NAME_AMBIGUOUS,+ EXPORTED_NAME_NORMAL,+ EXPORTED_NAME_NS,+} ExportedNameEntryEnum;++typedef struct ExportedNameEntry {+ JSAtom export_name;+ ExportedNameEntryEnum export_type;+ union {+ JSExportEntry *me; /* using when the list is built */+ JSVarRef *var_ref; /* EXPORTED_NAME_NORMAL */+ JSModuleDef *module; /* for EXPORTED_NAME_NS */+ } u;+} ExportedNameEntry;++typedef struct GetExportNamesState {+ JSModuleDef **modules;+ int modules_size;+ int modules_count;++ ExportedNameEntry *exported_names;+ int exported_names_size;+ int exported_names_count;+} GetExportNamesState;++static int find_exported_name(GetExportNamesState *s, JSAtom name)+{+ int i;+ for(i = 0; i < s->exported_names_count; i++) {+ if (s->exported_names[i].export_name == name)+ return i;+ }+ return -1;+}++static __exception int get_exported_names(JSContext *ctx,+ GetExportNamesState *s,+ JSModuleDef *m, BOOL from_star)+{+ ExportedNameEntry *en;+ int i, j;++ /* check circular reference */+ for(i = 0; i < s->modules_count; i++) {+ if (s->modules[i] == m)+ return 0;+ }+ if (js_resize_array(ctx, (void **)&s->modules, sizeof(s->modules[0]),+ &s->modules_size, s->modules_count + 1))+ return -1;+ s->modules[s->modules_count++] = m;++ for(i = 0; i < m->export_entries_count; i++) {+ JSExportEntry *me = &m->export_entries[i];+ if (from_star && me->export_name == JS_ATOM_default)+ continue;+ j = find_exported_name(s, me->export_name);+ if (j < 0) {+ if (js_resize_array(ctx, (void **)&s->exported_names, sizeof(s->exported_names[0]),+ &s->exported_names_size,+ s->exported_names_count + 1))+ return -1;+ en = &s->exported_names[s->exported_names_count++];+ en->export_name = me->export_name;+ /* avoid a second lookup for simple module exports */+ if (from_star || me->export_type != JS_EXPORT_TYPE_LOCAL)+ en->u.me = NULL;+ else+ en->u.me = me;+ } else {+ en = &s->exported_names[j];+ en->u.me = NULL;+ }+ }+ for(i = 0; i < m->star_export_entries_count; i++) {+ JSStarExportEntry *se = &m->star_export_entries[i];+ JSModuleDef *m1;+ m1 = m->req_module_entries[se->req_module_idx].module;+ if (get_exported_names(ctx, s, m1, TRUE))+ return -1;+ }+ return 0;+}++/* Unfortunately, the spec gives a different behavior from GetOwnProperty ! */+static int js_module_ns_has(JSContext *ctx, JSValueConst obj, JSAtom atom)+{+ return (find_own_property1(JS_VALUE_GET_OBJ(obj), atom) != NULL);+}++static const JSClassExoticMethods js_module_ns_exotic_methods = {+ .has_property = js_module_ns_has,+};++static int exported_names_cmp(const void *p1, const void *p2, void *opaque)+{+ JSContext *ctx = opaque;+ const ExportedNameEntry *me1 = p1;+ const ExportedNameEntry *me2 = p2;+ JSValue str1, str2;+ int ret;++ /* XXX: should avoid allocation memory in atom comparison */+ str1 = JS_AtomToString(ctx, me1->export_name);+ str2 = JS_AtomToString(ctx, me2->export_name);+ if (JS_IsException(str1) || JS_IsException(str2)) {+ /* XXX: raise an error ? */+ ret = 0;+ } else {+ ret = js_string_compare(ctx, JS_VALUE_GET_STRING(str1),+ JS_VALUE_GET_STRING(str2));+ }+ JS_FreeValue(ctx, str1);+ JS_FreeValue(ctx, str2);+ return ret;+}++static JSValue js_get_module_ns(JSContext *ctx, JSModuleDef *m);++static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,+ void *opaque)+{+ JSModuleDef *m = opaque;+ return js_get_module_ns(ctx, m);+}++static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m)+{+ JSValue obj;+ JSObject *p;+ GetExportNamesState s_s, *s = &s_s;+ int i, ret;+ JSProperty *pr;++ obj = JS_NewObjectClass(ctx, JS_CLASS_MODULE_NS);+ if (JS_IsException(obj))+ return obj;+ p = JS_VALUE_GET_OBJ(obj);++ memset(s, 0, sizeof(*s));+ ret = get_exported_names(ctx, s, m, FALSE);+ js_free(ctx, s->modules);+ if (ret)+ goto fail;++ /* Resolve the exported names. The ambiguous exports are removed */+ for(i = 0; i < s->exported_names_count; i++) {+ ExportedNameEntry *en = &s->exported_names[i];+ JSResolveResultEnum res;+ JSExportEntry *res_me;+ JSModuleDef *res_m;++ if (en->u.me) {+ res_me = en->u.me; /* fast case: no resolution needed */+ res_m = m;+ res = JS_RESOLVE_RES_FOUND;+ } else {+ res = js_resolve_export(ctx, &res_m, &res_me, m,+ en->export_name);+ }+ if (res != JS_RESOLVE_RES_FOUND) {+ if (res != JS_RESOLVE_RES_AMBIGUOUS) {+ js_resolve_export_throw_error(ctx, res, m, en->export_name);+ goto fail;+ }+ en->export_type = EXPORTED_NAME_AMBIGUOUS;+ } else {+ if (res_me->local_name == JS_ATOM__star_) {+ en->export_type = EXPORTED_NAME_NS;+ en->u.module = res_m->req_module_entries[res_me->u.req_module_idx].module;+ } else {+ en->export_type = EXPORTED_NAME_NORMAL;+ if (res_me->u.local.var_ref) {+ en->u.var_ref = res_me->u.local.var_ref;+ } else {+ JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj);+ p1 = JS_VALUE_GET_OBJ(res_m->func_obj);+ en->u.var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];+ }+ }+ }+ }++ /* sort the exported names */+ rqsort(s->exported_names, s->exported_names_count,+ sizeof(s->exported_names[0]), exported_names_cmp, ctx);++ for(i = 0; i < s->exported_names_count; i++) {+ ExportedNameEntry *en = &s->exported_names[i];+ switch(en->export_type) {+ case EXPORTED_NAME_NORMAL:+ {+ JSVarRef *var_ref = en->u.var_ref;+ pr = add_property(ctx, p, en->export_name,+ JS_PROP_ENUMERABLE | JS_PROP_WRITABLE |+ JS_PROP_VARREF);+ if (!pr)+ goto fail;+ var_ref->header.ref_count++;+ pr->u.var_ref = var_ref;+ }+ break;+ case EXPORTED_NAME_NS:+ /* the exported namespace must be created on demand */+ if (JS_DefineAutoInitProperty(ctx, obj,+ en->export_name,+ JS_AUTOINIT_ID_MODULE_NS,+ en->u.module, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0)+ goto fail;+ break;+ default:+ break;+ }+ }++ js_free(ctx, s->exported_names);++ JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_toStringTag,+ JS_AtomToString(ctx, JS_ATOM_Module),+ 0);++ p->extensible = FALSE;+ return obj;+ fail:+ js_free(ctx, s->exported_names);+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++static JSValue js_get_module_ns(JSContext *ctx, JSModuleDef *m)+{+ if (JS_IsUndefined(m->module_ns)) {+ JSValue val;+ val = js_build_module_ns(ctx, m);+ if (JS_IsException(val))+ return JS_EXCEPTION;+ m->module_ns = val;+ }+ return JS_DupValue(ctx, m->module_ns);+}++/* Load all the required modules for module 'm' */+static int js_resolve_module(JSContext *ctx, JSModuleDef *m)+{+ int i;+ JSModuleDef *m1;++ if (m->resolved)+ return 0;+#ifdef DUMP_MODULE_RESOLVE+ {+ char buf1[ATOM_GET_STR_BUF_SIZE];+ printf("resolving module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));+ }+#endif+ m->resolved = TRUE;+ /* resolve each requested module */+ for(i = 0; i < m->req_module_entries_count; i++) {+ JSReqModuleEntry *rme = &m->req_module_entries[i];+ m1 = js_host_resolve_imported_module_atom(ctx, m->module_name,+ rme->module_name);+ if (!m1)+ return -1;+ rme->module = m1;+ /* already done in js_host_resolve_imported_module() except if+ the module was loaded with JS_EvalBinary() */+ if (js_resolve_module(ctx, m1) < 0)+ return -1;+ }+ return 0;+}++static JSVarRef *js_create_module_var(JSContext *ctx, BOOL is_lexical)+{+ JSVarRef *var_ref;+ var_ref = js_malloc(ctx, sizeof(JSVarRef));+ if (!var_ref)+ return NULL;+ var_ref->header.ref_count = 1;+ if (is_lexical)+ var_ref->value = JS_UNINITIALIZED;+ else+ var_ref->value = JS_UNDEFINED;+ var_ref->pvalue = &var_ref->value;+ var_ref->is_detached = TRUE;+ add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);+ return var_ref;+}++/* Create the <eval> function associated with the module */+static int js_create_module_bytecode_function(JSContext *ctx, JSModuleDef *m)+{+ JSFunctionBytecode *b;+ int i;+ JSVarRef **var_refs;+ JSValue func_obj, bfunc;+ JSObject *p;++ bfunc = m->func_obj;+ func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,+ JS_CLASS_BYTECODE_FUNCTION);++ if (JS_IsException(func_obj))+ return -1;+ b = JS_VALUE_GET_PTR(bfunc);++ p = JS_VALUE_GET_OBJ(func_obj);+ p->u.func.function_bytecode = b;+ b->header.ref_count++;+ p->u.func.home_object = NULL;+ p->u.func.var_refs = NULL;+ if (b->closure_var_count) {+ var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count);+ if (!var_refs)+ goto fail;+ p->u.func.var_refs = var_refs;++ /* create the global variables. The other variables are+ imported from other modules */+ for(i = 0; i < b->closure_var_count; i++) {+ JSClosureVar *cv = &b->closure_var[i];+ JSVarRef *var_ref;+ if (cv->is_local) {+ var_ref = js_create_module_var(ctx, cv->is_lexical);+ if (!var_ref)+ goto fail;+#ifdef DUMP_MODULE_RESOLVE+ printf("local %d: %p\n", i, var_ref);+#endif+ var_refs[i] = var_ref;+ }+ }+ }+ m->func_obj = func_obj;+ JS_FreeValue(ctx, bfunc);+ return 0;+ fail:+ JS_FreeValue(ctx, func_obj);+ return -1;+}++/* must be done before js_link_module() because of cyclic references */+static int js_create_module_function(JSContext *ctx, JSModuleDef *m)+{+ BOOL is_c_module;+ int i;+ JSVarRef *var_ref;+ + if (m->func_created)+ return 0;++ is_c_module = (m->init_func != NULL);++ if (is_c_module) {+ /* initialize the exported variables */+ for(i = 0; i < m->export_entries_count; i++) {+ JSExportEntry *me = &m->export_entries[i];+ if (me->export_type == JS_EXPORT_TYPE_LOCAL) {+ var_ref = js_create_module_var(ctx, FALSE);+ if (!var_ref)+ return -1;+ me->u.local.var_ref = var_ref;+ }+ }+ } else {+ if (js_create_module_bytecode_function(ctx, m))+ return -1;+ }+ m->func_created = TRUE;++ /* do it on the dependencies */+ + for(i = 0; i < m->req_module_entries_count; i++) {+ JSReqModuleEntry *rme = &m->req_module_entries[i];+ if (js_create_module_function(ctx, rme->module) < 0)+ return -1;+ }++ return 0;+} ++ +/* Prepare a module to be executed by resolving all the imported+ variables. */+static int js_link_module(JSContext *ctx, JSModuleDef *m)+{+ int i;+ JSImportEntry *mi;+ JSModuleDef *m1;+ JSVarRef **var_refs, *var_ref;+ JSObject *p;+ BOOL is_c_module;+ JSValue ret_val;+ + if (m->instantiated)+ return 0;+ m->instantiated = TRUE;++#ifdef DUMP_MODULE_RESOLVE+ {+ char buf1[ATOM_GET_STR_BUF_SIZE];+ printf("start instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));+ }+#endif++ for(i = 0; i < m->req_module_entries_count; i++) {+ JSReqModuleEntry *rme = &m->req_module_entries[i];+ if (js_link_module(ctx, rme->module) < 0)+ goto fail;+ }++#ifdef DUMP_MODULE_RESOLVE+ {+ char buf1[ATOM_GET_STR_BUF_SIZE];+ printf("instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));+ }+#endif+ /* check the indirect exports */+ for(i = 0; i < m->export_entries_count; i++) {+ JSExportEntry *me = &m->export_entries[i];+ if (me->export_type == JS_EXPORT_TYPE_INDIRECT &&+ me->local_name != JS_ATOM__star_) {+ JSResolveResultEnum ret;+ JSExportEntry *res_me;+ JSModuleDef *res_m, *m1;+ m1 = m->req_module_entries[me->u.req_module_idx].module;+ ret = js_resolve_export(ctx, &res_m, &res_me, m1, me->local_name);+ if (ret != JS_RESOLVE_RES_FOUND) {+ js_resolve_export_throw_error(ctx, ret, m, me->export_name);+ goto fail;+ }+ }+ }++#ifdef DUMP_MODULE_RESOLVE+ {+ printf("exported bindings:\n");+ for(i = 0; i < m->export_entries_count; i++) {+ JSExportEntry *me = &m->export_entries[i];+ printf(" name="); print_atom(ctx, me->export_name);+ printf(" local="); print_atom(ctx, me->local_name);+ printf(" type=%d idx=%d\n", me->export_type, me->u.local.var_idx);+ }+ }+#endif++ is_c_module = (m->init_func != NULL);++ if (!is_c_module) {+ p = JS_VALUE_GET_OBJ(m->func_obj);+ var_refs = p->u.func.var_refs;++ for(i = 0; i < m->import_entries_count; i++) {+ mi = &m->import_entries[i];+#ifdef DUMP_MODULE_RESOLVE+ printf("import var_idx=%d name=", mi->var_idx);+ print_atom(ctx, mi->import_name);+ printf(": ");+#endif+ m1 = m->req_module_entries[mi->req_module_idx].module;+ if (mi->import_name == JS_ATOM__star_) {+ JSValue val;+ /* name space import */+ val = js_get_module_ns(ctx, m1);+ if (JS_IsException(val))+ goto fail;+ set_value(ctx, &var_refs[mi->var_idx]->value, val);+#ifdef DUMP_MODULE_RESOLVE+ printf("namespace\n");+#endif+ } else {+ JSResolveResultEnum ret;+ JSExportEntry *res_me;+ JSModuleDef *res_m;+ JSObject *p1;++ ret = js_resolve_export(ctx, &res_m,+ &res_me, m1, mi->import_name);+ if (ret != JS_RESOLVE_RES_FOUND) {+ js_resolve_export_throw_error(ctx, ret, m1, mi->import_name);+ goto fail;+ }+ if (res_me->local_name == JS_ATOM__star_) {+ JSValue val;+ JSModuleDef *m2;+ /* name space import from */+ m2 = res_m->req_module_entries[res_me->u.req_module_idx].module;+ val = js_get_module_ns(ctx, m2);+ if (JS_IsException(val))+ goto fail;+ var_ref = js_create_module_var(ctx, TRUE);+ if (!var_ref) {+ JS_FreeValue(ctx, val);+ goto fail;+ }+ set_value(ctx, &var_ref->value, val);+ var_refs[mi->var_idx] = var_ref;+#ifdef DUMP_MODULE_RESOLVE+ printf("namespace from\n");+#endif+ } else {+ var_ref = res_me->u.local.var_ref;+ if (!var_ref) {+ p1 = JS_VALUE_GET_OBJ(res_m->func_obj);+ var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];+ }+ var_ref->header.ref_count++;+ var_refs[mi->var_idx] = var_ref;+#ifdef DUMP_MODULE_RESOLVE+ printf("local export (var_ref=%p)\n", var_ref);+#endif+ }+ }+ }++ /* keep the exported variables in the module export entries (they+ are used when the eval function is deleted and cannot be+ initialized before in case imports are exported) */+ for(i = 0; i < m->export_entries_count; i++) {+ JSExportEntry *me = &m->export_entries[i];+ if (me->export_type == JS_EXPORT_TYPE_LOCAL) {+ var_ref = var_refs[me->u.local.var_idx];+ var_ref->header.ref_count++;+ me->u.local.var_ref = var_ref;+ }+ }++ /* initialize the global variables */+ ret_val = JS_Call(ctx, m->func_obj, JS_TRUE, 0, NULL);+ if (JS_IsException(ret_val))+ goto fail;+ JS_FreeValue(ctx, ret_val);+ }++#ifdef DUMP_MODULE_RESOLVE+ printf("done instantiate\n");+#endif+ return 0;+ fail:+ return -1;+}++/* return JS_ATOM_NULL if the name cannot be found. Only works with+ not striped bytecode functions. */+JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels)+{+ JSStackFrame *sf;+ JSFunctionBytecode *b;+ JSObject *p;+ /* XXX: currently we just use the filename of the englobing+ function. It does not work for eval(). Need to add a+ ScriptOrModule info in JSFunctionBytecode */+ sf = ctx->rt->current_stack_frame;+ if (!sf)+ return JS_ATOM_NULL;+ while (n_stack_levels-- > 0) {+ sf = sf->prev_frame;+ if (!sf)+ return JS_ATOM_NULL;+ }+ if (JS_VALUE_GET_TAG(sf->cur_func) != JS_TAG_OBJECT)+ return JS_ATOM_NULL;+ p = JS_VALUE_GET_OBJ(sf->cur_func);+ if (!js_class_has_bytecode(p->class_id))+ return JS_ATOM_NULL;+ b = p->u.func.function_bytecode;+ if (!b->has_debug)+ return JS_ATOM_NULL;+ return JS_DupAtom(ctx, b->debug.filename);+}++JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m)+{+ return JS_DupAtom(ctx, m->module_name);+}++JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m)+{+ JSValue obj;+ /* allocate meta_obj only if requested to save memory */+ obj = m->meta_obj;+ if (JS_IsUndefined(obj)) {+ obj = JS_NewObjectProto(ctx, JS_NULL);+ if (JS_IsException(obj))+ return JS_EXCEPTION;+ m->meta_obj = obj;+ }+ return JS_DupValue(ctx, obj);+}++static JSValue js_import_meta(JSContext *ctx)+{+ JSAtom filename;+ JSModuleDef *m;+ + filename = JS_GetScriptOrModuleName(ctx, 0);+ if (filename == JS_ATOM_NULL)+ goto fail;++ /* XXX: inefficient, need to add a module or script pointer in+ JSFunctionBytecode */+ m = js_find_loaded_module(ctx, filename);+ JS_FreeAtom(ctx, filename);+ if (!m) {+ fail:+ JS_ThrowTypeError(ctx, "import.meta not supported in this context");+ return JS_EXCEPTION;+ }+ return JS_GetImportMeta(ctx, m);+}++/* used by os.Worker() and import() */+JSModuleDef *JS_RunModule(JSContext *ctx, const char *basename,+ const char *filename)+{+ JSModuleDef *m;+ JSValue ret, func_obj;+ + m = js_host_resolve_imported_module(ctx, basename, filename);+ if (!m)+ return NULL;+ + if (js_resolve_module(ctx, m) < 0) {+ js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED);+ return NULL;+ }++ /* Evaluate the module code */+ func_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));+ ret = JS_EvalFunction(ctx, func_obj);+ if (JS_IsException(ret))+ return NULL;+ JS_FreeValue(ctx, ret);+ return m;+}++static JSValue js_dynamic_import_job(JSContext *ctx,+ int argc, JSValueConst *argv)+{+ JSValueConst *resolving_funcs = argv;+ JSValueConst basename_val = argv[2];+ JSValueConst specifier = argv[3];+ JSModuleDef *m;+ const char *basename = NULL, *filename;+ JSValue ret, err, ns;++ if (!JS_IsString(basename_val)) {+ JS_ThrowTypeError(ctx, "no function filename for import()");+ goto exception;+ }+ basename = JS_ToCString(ctx, basename_val);+ if (!basename)+ goto exception;++ filename = JS_ToCString(ctx, specifier);+ if (!filename)+ goto exception;+ + m = JS_RunModule(ctx, basename, filename);+ JS_FreeCString(ctx, filename);+ if (!m)+ goto exception;++ /* return the module namespace */+ ns = js_get_module_ns(ctx, m);+ if (JS_IsException(ns))+ goto exception;++ ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED,+ 1, (JSValueConst *)&ns);+ JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */+ JS_FreeValue(ctx, ns);+ JS_FreeCString(ctx, basename);+ return JS_UNDEFINED;+ exception:++ err = JS_GetException(ctx);+ ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,+ 1, (JSValueConst *)&err);+ JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */+ JS_FreeValue(ctx, err);+ JS_FreeCString(ctx, basename);+ return JS_UNDEFINED;+}++static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier)+{+ JSAtom basename;+ JSValue promise, resolving_funcs[2], basename_val;+ JSValueConst args[4];++ basename = JS_GetScriptOrModuleName(ctx, 0);+ if (basename == JS_ATOM_NULL)+ basename_val = JS_NULL;+ else+ basename_val = JS_AtomToValue(ctx, basename);+ JS_FreeAtom(ctx, basename);+ if (JS_IsException(basename_val))+ return basename_val;+ + promise = JS_NewPromiseCapability(ctx, resolving_funcs);+ if (JS_IsException(promise)) {+ JS_FreeValue(ctx, basename_val);+ return promise;+ }++ args[0] = resolving_funcs[0];+ args[1] = resolving_funcs[1];+ args[2] = basename_val;+ args[3] = specifier;+ + JS_EnqueueJob(ctx, js_dynamic_import_job, 4, args);++ JS_FreeValue(ctx, basename_val);+ JS_FreeValue(ctx, resolving_funcs[0]);+ JS_FreeValue(ctx, resolving_funcs[1]);+ return promise;+}++/* Run the <eval> function of the module and of all its requested+ modules. */+static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m)+{+ JSModuleDef *m1;+ int i;+ JSValue ret_val;++ if (m->eval_mark)+ return JS_UNDEFINED; /* avoid cycles */++ if (m->evaluated) {+ /* if the module was already evaluated, rethrow the exception+ it raised */+ if (m->eval_has_exception) {+ return JS_Throw(ctx, JS_DupValue(ctx, m->eval_exception));+ } else {+ return JS_UNDEFINED;+ }+ }++ m->eval_mark = TRUE;++ for(i = 0; i < m->req_module_entries_count; i++) {+ JSReqModuleEntry *rme = &m->req_module_entries[i];+ m1 = rme->module;+ if (!m1->eval_mark) {+ ret_val = js_evaluate_module(ctx, m1);+ if (JS_IsException(ret_val)) {+ m->eval_mark = FALSE;+ return ret_val;+ }+ JS_FreeValue(ctx, ret_val);+ }+ }++ if (m->init_func) {+ /* C module init */+ if (m->init_func(ctx, m) < 0)+ ret_val = JS_EXCEPTION;+ else+ ret_val = JS_UNDEFINED;+ } else {+ ret_val = JS_CallFree(ctx, m->func_obj, JS_UNDEFINED, 0, NULL);+ m->func_obj = JS_UNDEFINED;+ }+ if (JS_IsException(ret_val)) {+ /* save the thrown exception value */+ m->eval_has_exception = TRUE;+ m->eval_exception = JS_DupValue(ctx, ctx->rt->current_exception);+ }+ m->eval_mark = FALSE;+ m->evaluated = TRUE;+ return ret_val;+}++static __exception JSAtom js_parse_from_clause(JSParseState *s)+{+ JSAtom module_name;+ if (!token_is_pseudo_keyword(s, JS_ATOM_from)) {+ js_parse_error(s, "from clause expected");+ return JS_ATOM_NULL;+ }+ if (next_token(s))+ return JS_ATOM_NULL;+ if (s->token.val != TOK_STRING) {+ js_parse_error(s, "string expected");+ return JS_ATOM_NULL;+ }+ module_name = JS_ValueToAtom(s->ctx, s->token.u.str.str);+ if (module_name == JS_ATOM_NULL)+ return JS_ATOM_NULL;+ if (next_token(s)) {+ JS_FreeAtom(s->ctx, module_name);+ return JS_ATOM_NULL;+ }+ return module_name;+}++static __exception int js_parse_export(JSParseState *s)+{+ JSContext *ctx = s->ctx;+ JSModuleDef *m = s->cur_func->module;+ JSAtom local_name, export_name;+ int first_export, idx, i, tok;+ JSAtom module_name;+ JSExportEntry *me;++ if (next_token(s))+ return -1;++ tok = s->token.val;+ if (tok == TOK_CLASS) {+ return js_parse_class(s, FALSE, JS_PARSE_EXPORT_NAMED);+ } else if (tok == TOK_FUNCTION ||+ (token_is_pseudo_keyword(s, JS_ATOM_async) &&+ peek_token(s, TRUE) == TOK_FUNCTION)) {+ return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT,+ JS_FUNC_NORMAL, JS_ATOM_NULL,+ s->token.ptr, s->token.line_num,+ JS_PARSE_EXPORT_NAMED, NULL);+ }++ if (next_token(s))+ return -1;++ switch(tok) {+ case '{':+ first_export = m->export_entries_count;+ while (s->token.val != '}') {+ if (!token_is_ident(s->token.val)) {+ js_parse_error(s, "identifier expected");+ return -1;+ }+ local_name = JS_DupAtom(ctx, s->token.u.ident.atom);+ export_name = JS_ATOM_NULL;+ if (next_token(s))+ goto fail;+ if (token_is_pseudo_keyword(s, JS_ATOM_as)) {+ if (next_token(s))+ goto fail;+ if (!token_is_ident(s->token.val)) {+ js_parse_error(s, "identifier expected");+ goto fail;+ }+ export_name = JS_DupAtom(ctx, s->token.u.ident.atom);+ if (next_token(s)) {+ fail:+ JS_FreeAtom(ctx, local_name);+ fail1:+ JS_FreeAtom(ctx, export_name);+ return -1;+ }+ } else {+ export_name = JS_DupAtom(ctx, local_name);+ }+ me = add_export_entry(s, m, local_name, export_name,+ JS_EXPORT_TYPE_LOCAL);+ JS_FreeAtom(ctx, local_name);+ JS_FreeAtom(ctx, export_name);+ if (!me)+ return -1;+ if (s->token.val != ',')+ break;+ if (next_token(s))+ return -1;+ }+ if (js_parse_expect(s, '}'))+ return -1;+ if (token_is_pseudo_keyword(s, JS_ATOM_from)) {+ module_name = js_parse_from_clause(s);+ if (module_name == JS_ATOM_NULL)+ return -1;+ idx = add_req_module_entry(ctx, m, module_name);+ JS_FreeAtom(ctx, module_name);+ if (idx < 0)+ return -1;+ for(i = first_export; i < m->export_entries_count; i++) {+ me = &m->export_entries[i];+ me->export_type = JS_EXPORT_TYPE_INDIRECT;+ me->u.req_module_idx = idx;+ }+ }+ break;+ case '*':+ if (token_is_pseudo_keyword(s, JS_ATOM_as)) {+ /* export ns from */+ if (next_token(s))+ return -1;+ if (!token_is_ident(s->token.val)) {+ js_parse_error(s, "identifier expected");+ return -1;+ }+ export_name = JS_DupAtom(ctx, s->token.u.ident.atom);+ if (next_token(s))+ goto fail1;+ module_name = js_parse_from_clause(s);+ if (module_name == JS_ATOM_NULL)+ goto fail1;+ idx = add_req_module_entry(ctx, m, module_name);+ JS_FreeAtom(ctx, module_name);+ if (idx < 0)+ goto fail1;+ me = add_export_entry(s, m, JS_ATOM__star_, export_name,+ JS_EXPORT_TYPE_INDIRECT);+ JS_FreeAtom(ctx, export_name);+ if (!me)+ return -1;+ me->u.req_module_idx = idx;+ } else {+ module_name = js_parse_from_clause(s);+ if (module_name == JS_ATOM_NULL)+ return -1;+ idx = add_req_module_entry(ctx, m, module_name);+ JS_FreeAtom(ctx, module_name);+ if (idx < 0)+ return -1;+ if (add_star_export_entry(ctx, m, idx) < 0)+ return -1;+ }+ break;+ case TOK_DEFAULT:+ if (s->token.val == TOK_CLASS) {+ return js_parse_class(s, FALSE, JS_PARSE_EXPORT_DEFAULT);+ } else if (s->token.val == TOK_FUNCTION ||+ (token_is_pseudo_keyword(s, JS_ATOM_async) &&+ peek_token(s, TRUE) == TOK_FUNCTION)) {+ return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT,+ JS_FUNC_NORMAL, JS_ATOM_NULL,+ s->token.ptr, s->token.line_num,+ JS_PARSE_EXPORT_DEFAULT, NULL);+ } else {+ if (js_parse_assign_expr(s))+ return -1;+ }+ /* set the name of anonymous functions */+ set_object_name(s, JS_ATOM_default);++ /* store the value in the _default_ global variable and export+ it */+ local_name = JS_ATOM__default_;+ if (define_var(s, s->cur_func, local_name, JS_VAR_DEF_LET) < 0)+ return -1;+ emit_op(s, OP_scope_put_var_init);+ emit_atom(s, local_name);+ emit_u16(s, 0);++ if (!add_export_entry(s, m, local_name, JS_ATOM_default,+ JS_EXPORT_TYPE_LOCAL))+ return -1;+ break;+ case TOK_VAR:+ case TOK_LET:+ case TOK_CONST:+ return js_parse_var(s, TRUE, tok, TRUE);+ default:+ return js_parse_error(s, "invalid export syntax");+ }+ return js_parse_expect_semi(s);+}++static int add_closure_var(JSContext *ctx, JSFunctionDef *s,+ BOOL is_local, BOOL is_arg,+ int var_idx, JSAtom var_name,+ BOOL is_const, BOOL is_lexical,+ JSVarKindEnum var_kind);++static int add_import(JSParseState *s, JSModuleDef *m,+ JSAtom local_name, JSAtom import_name)+{+ JSContext *ctx = s->ctx;+ int i, var_idx;+ JSImportEntry *mi;+ BOOL is_local;++ if (local_name == JS_ATOM_arguments || local_name == JS_ATOM_eval)+ return js_parse_error(s, "invalid import binding");++ if (local_name != JS_ATOM_default) {+ for (i = 0; i < s->cur_func->closure_var_count; i++) {+ if (s->cur_func->closure_var[i].var_name == local_name)+ return js_parse_error(s, "duplicate import binding");+ }+ }++ is_local = (import_name == JS_ATOM__star_);+ var_idx = add_closure_var(ctx, s->cur_func, is_local, FALSE,+ m->import_entries_count,+ local_name, TRUE, TRUE, FALSE);+ if (var_idx < 0)+ return -1;+ if (js_resize_array(ctx, (void **)&m->import_entries,+ sizeof(JSImportEntry),+ &m->import_entries_size,+ m->import_entries_count + 1))+ return -1;+ mi = &m->import_entries[m->import_entries_count++];+ mi->import_name = JS_DupAtom(ctx, import_name);+ mi->var_idx = var_idx;+ return 0;+}++static __exception int js_parse_import(JSParseState *s)+{+ JSContext *ctx = s->ctx;+ JSModuleDef *m = s->cur_func->module;+ JSAtom local_name, import_name, module_name;+ int first_import, i, idx;++ if (next_token(s))+ return -1;++ first_import = m->import_entries_count;+ if (s->token.val == TOK_STRING) {+ module_name = JS_ValueToAtom(ctx, s->token.u.str.str);+ if (module_name == JS_ATOM_NULL)+ return -1;+ if (next_token(s)) {+ JS_FreeAtom(ctx, module_name);+ return -1;+ }+ } else {+ if (s->token.val == TOK_IDENT) {+ if (s->token.u.ident.is_reserved) {+ return js_parse_error_reserved_identifier(s);+ }+ /* "default" import */+ local_name = JS_DupAtom(ctx, s->token.u.ident.atom);+ import_name = JS_ATOM_default;+ if (next_token(s))+ goto fail;+ if (add_import(s, m, local_name, import_name))+ goto fail;+ JS_FreeAtom(ctx, local_name);++ if (s->token.val != ',')+ goto end_import_clause;+ if (next_token(s))+ return -1;+ }++ if (s->token.val == '*') {+ /* name space import */+ if (next_token(s))+ return -1;+ if (!token_is_pseudo_keyword(s, JS_ATOM_as))+ return js_parse_error(s, "expecting 'as'");+ if (next_token(s))+ return -1;+ if (!token_is_ident(s->token.val)) {+ js_parse_error(s, "identifier expected");+ return -1;+ }+ local_name = JS_DupAtom(ctx, s->token.u.ident.atom);+ import_name = JS_ATOM__star_;+ if (next_token(s))+ goto fail;+ if (add_import(s, m, local_name, import_name))+ goto fail;+ JS_FreeAtom(ctx, local_name);+ } else if (s->token.val == '{') {+ if (next_token(s))+ return -1;++ while (s->token.val != '}') {+ if (!token_is_ident(s->token.val)) {+ js_parse_error(s, "identifier expected");+ return -1;+ }+ import_name = JS_DupAtom(ctx, s->token.u.ident.atom);+ local_name = JS_ATOM_NULL;+ if (next_token(s))+ goto fail;+ if (token_is_pseudo_keyword(s, JS_ATOM_as)) {+ if (next_token(s))+ goto fail;+ if (!token_is_ident(s->token.val)) {+ js_parse_error(s, "identifier expected");+ goto fail;+ }+ local_name = JS_DupAtom(ctx, s->token.u.ident.atom);+ if (next_token(s)) {+ fail:+ JS_FreeAtom(ctx, local_name);+ JS_FreeAtom(ctx, import_name);+ return -1;+ }+ } else {+ local_name = JS_DupAtom(ctx, import_name);+ }+ if (add_import(s, m, local_name, import_name))+ goto fail;+ JS_FreeAtom(ctx, local_name);+ JS_FreeAtom(ctx, import_name);+ if (s->token.val != ',')+ break;+ if (next_token(s))+ return -1;+ }+ if (js_parse_expect(s, '}'))+ return -1;+ }+ end_import_clause:+ module_name = js_parse_from_clause(s);+ if (module_name == JS_ATOM_NULL)+ return -1;+ }+ idx = add_req_module_entry(ctx, m, module_name);+ JS_FreeAtom(ctx, module_name);+ if (idx < 0)+ return -1;+ for(i = first_import; i < m->import_entries_count; i++)+ m->import_entries[i].req_module_idx = idx;++ return js_parse_expect_semi(s);+}++static __exception int js_parse_source_element(JSParseState *s)+{+ JSFunctionDef *fd = s->cur_func;+ int tok;+ + if (s->token.val == TOK_FUNCTION ||+ (token_is_pseudo_keyword(s, JS_ATOM_async) &&+ peek_token(s, TRUE) == TOK_FUNCTION)) {+ if (js_parse_function_decl(s, JS_PARSE_FUNC_STATEMENT,+ JS_FUNC_NORMAL, JS_ATOM_NULL,+ s->token.ptr, s->token.line_num))+ return -1;+ } else if (s->token.val == TOK_EXPORT && fd->module) {+ if (js_parse_export(s))+ return -1;+ } else if (s->token.val == TOK_IMPORT && fd->module &&+ ((tok = peek_token(s, FALSE)) != '(' && tok != '.')) {+ /* the peek_token is needed to avoid confusion with ImportCall+ (dynamic import) or import.meta */+ if (js_parse_import(s))+ return -1;+ } else {+ if (js_parse_statement_or_decl(s, DECL_MASK_ALL))+ return -1;+ }+ return 0;+}++static JSFunctionDef *js_new_function_def(JSContext *ctx,+ JSFunctionDef *parent,+ BOOL is_eval,+ BOOL is_func_expr,+ const char *filename, int line_num)+{+ JSFunctionDef *fd;++ fd = js_mallocz(ctx, sizeof(*fd));+ if (!fd)+ return NULL;++ fd->ctx = ctx;+ init_list_head(&fd->child_list);++ /* insert in parent list */+ fd->parent = parent;+ fd->parent_cpool_idx = -1;+ if (parent) {+ list_add_tail(&fd->link, &parent->child_list);+ fd->js_mode = parent->js_mode;+ fd->parent_scope_level = parent->scope_level;+ }++ fd->is_eval = is_eval;+ fd->is_func_expr = is_func_expr;+ js_dbuf_init(ctx, &fd->byte_code);+ fd->last_opcode_pos = -1;+ fd->func_name = JS_ATOM_NULL;+ fd->var_object_idx = -1;+ fd->arg_var_object_idx = -1;+ fd->arguments_var_idx = -1;+ fd->arguments_arg_idx = -1;+ fd->func_var_idx = -1;+ fd->eval_ret_idx = -1;+ fd->this_var_idx = -1;+ fd->new_target_var_idx = -1;+ fd->this_active_func_var_idx = -1;+ fd->home_object_var_idx = -1;++ /* XXX: should distinguish arg, var and var object and body scopes */+ fd->scopes = fd->def_scope_array;+ fd->scope_size = countof(fd->def_scope_array);+ fd->scope_count = 1;+ fd->scopes[0].first = -1;+ fd->scopes[0].parent = -1;+ fd->scope_level = 0; /* 0: var/arg scope */+ fd->scope_first = -1;+ fd->body_scope = -1;++ fd->filename = JS_NewAtom(ctx, filename);+ fd->line_num = line_num;++ js_dbuf_init(ctx, &fd->pc2line);+ //fd->pc2line_last_line_num = line_num;+ //fd->pc2line_last_pc = 0;+ fd->last_opcode_line_num = line_num;++ return fd;+}++static void free_bytecode_atoms(JSRuntime *rt,+ const uint8_t *bc_buf, int bc_len,+ BOOL use_short_opcodes)+{+ int pos, len, op;+ JSAtom atom;+ const JSOpCode *oi;+ + pos = 0;+ while (pos < bc_len) {+ op = bc_buf[pos];+ if (use_short_opcodes)+ oi = &short_opcode_info(op);+ else+ oi = &opcode_info[op];+ + len = oi->size;+ switch(oi->fmt) {+ case OP_FMT_atom:+ case OP_FMT_atom_u8:+ case OP_FMT_atom_u16:+ case OP_FMT_atom_label_u8:+ case OP_FMT_atom_label_u16:+ atom = get_u32(bc_buf + pos + 1);+ JS_FreeAtomRT(rt, atom);+ break;+ default:+ break;+ }+ pos += len;+ }+}++static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd)+{+ int i;+ struct list_head *el, *el1;++ /* free the child functions */+ list_for_each_safe(el, el1, &fd->child_list) {+ JSFunctionDef *fd1;+ fd1 = list_entry(el, JSFunctionDef, link);+ js_free_function_def(ctx, fd1);+ }++ free_bytecode_atoms(ctx->rt, fd->byte_code.buf, fd->byte_code.size,+ fd->use_short_opcodes);+ dbuf_free(&fd->byte_code);+ js_free(ctx, fd->jump_slots);+ js_free(ctx, fd->label_slots);+ js_free(ctx, fd->line_number_slots);++ for(i = 0; i < fd->cpool_count; i++) {+ JS_FreeValue(ctx, fd->cpool[i]);+ }+ js_free(ctx, fd->cpool);++ JS_FreeAtom(ctx, fd->func_name);++ for(i = 0; i < fd->var_count; i++) {+ JS_FreeAtom(ctx, fd->vars[i].var_name);+ }+ js_free(ctx, fd->vars);+ for(i = 0; i < fd->arg_count; i++) {+ JS_FreeAtom(ctx, fd->args[i].var_name);+ }+ js_free(ctx, fd->args);++ for(i = 0; i < fd->global_var_count; i++) {+ JS_FreeAtom(ctx, fd->global_vars[i].var_name);+ }+ js_free(ctx, fd->global_vars);++ for(i = 0; i < fd->closure_var_count; i++) {+ JSClosureVar *cv = &fd->closure_var[i];+ JS_FreeAtom(ctx, cv->var_name);+ }+ js_free(ctx, fd->closure_var);++ if (fd->scopes != fd->def_scope_array)+ js_free(ctx, fd->scopes);++ JS_FreeAtom(ctx, fd->filename);+ dbuf_free(&fd->pc2line);++ js_free(ctx, fd->source);++ if (fd->parent) {+ /* remove in parent list */+ list_del(&fd->link);+ }+ js_free(ctx, fd);+}++#ifdef DUMP_BYTECODE+static const char *skip_lines(const char *p, int n) {+ while (n-- > 0 && *p) {+ while (*p && *p++ != '\n')+ continue;+ }+ return p;+}++static void print_lines(const char *source, int line, int line1) {+ const char *s = source;+ const char *p = skip_lines(s, line);+ if (*p) {+ while (line++ < line1) {+ p = skip_lines(s = p, 1);+ printf(";; %.*s", (int)(p - s), s);+ if (!*p) {+ if (p[-1] != '\n')+ printf("\n");+ break;+ }+ }+ }+}++static void dump_byte_code(JSContext *ctx, int pass,+ const uint8_t *tab, int len,+ const JSVarDef *args, int arg_count,+ const JSVarDef *vars, int var_count,+ const JSClosureVar *closure_var, int closure_var_count,+ const JSValue *cpool, uint32_t cpool_count,+ const char *source, int line_num,+ const LabelSlot *label_slots, JSFunctionBytecode *b)+{+ const JSOpCode *oi;+ int pos, pos_next, op, size, idx, addr, line, line1, in_source;+ uint8_t *bits = js_mallocz(ctx, len * sizeof(*bits));+ BOOL use_short_opcodes = (b != NULL);++ /* scan for jump targets */+ for (pos = 0; pos < len; pos = pos_next) {+ op = tab[pos];+ if (use_short_opcodes)+ oi = &short_opcode_info(op);+ else+ oi = &opcode_info[op];+ pos_next = pos + oi->size;+ if (op < OP_COUNT) {+ switch (oi->fmt) {+#if SHORT_OPCODES+ case OP_FMT_label8:+ pos++;+ addr = (int8_t)tab[pos];+ goto has_addr;+ case OP_FMT_label16:+ pos++;+ addr = (int16_t)get_u16(tab + pos);+ goto has_addr;+#endif+ case OP_FMT_atom_label_u8:+ case OP_FMT_atom_label_u16:+ pos += 4;+ /* fall thru */+ case OP_FMT_label:+ case OP_FMT_label_u16:+ pos++;+ addr = get_u32(tab + pos);+ goto has_addr;+ has_addr:+ if (pass == 1)+ addr = label_slots[addr].pos;+ if (pass == 2)+ addr = label_slots[addr].pos2;+ if (pass == 3)+ addr += pos;+ if (addr >= 0 && addr < len)+ bits[addr] |= 1;+ break;+ }+ }+ }+ in_source = 0;+ if (source) {+ /* Always print first line: needed if single line */+ print_lines(source, 0, 1);+ in_source = 1;+ }+ line1 = line = 1;+ pos = 0;+ while (pos < len) {+ op = tab[pos];+ if (source) {+ if (b) {+ line1 = find_line_num(ctx, b, pos) - line_num + 1;+ } else if (op == OP_line_num) {+ line1 = get_u32(tab + pos + 1) - line_num + 1;+ }+ if (line1 > line) {+ if (!in_source)+ printf("\n");+ in_source = 1;+ print_lines(source, line, line1);+ line = line1;+ //bits[pos] |= 2;+ }+ }+ if (in_source)+ printf("\n");+ in_source = 0;+ if (op >= OP_COUNT) {+ printf("invalid opcode (0x%02x)\n", op);+ pos++;+ continue;+ }+ if (use_short_opcodes)+ oi = &short_opcode_info(op);+ else+ oi = &opcode_info[op];+ size = oi->size;+ if (pos + size > len) {+ printf("truncated opcode (0x%02x)\n", op);+ break;+ }+#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 16)+ {+ int i, x, x0;+ x = x0 = printf("%5d ", pos);+ for (i = 0; i < size; i++) {+ if (i == 6) {+ printf("\n%*s", x = x0, "");+ }+ x += printf(" %02X", tab[pos + i]);+ }+ printf("%*s", x0 + 20 - x, "");+ }+#endif+ if (bits[pos]) {+ printf("%5d: ", pos);+ } else {+ printf(" ");+ }+ printf("%s", oi->name);+ pos++;+ switch(oi->fmt) {+ case OP_FMT_none_int:+ printf(" %d", op - OP_push_0);+ break;+ case OP_FMT_npopx:+ printf(" %d", op - OP_call0);+ break;+ case OP_FMT_u8:+ printf(" %u", get_u8(tab + pos));+ break;+ case OP_FMT_i8:+ printf(" %d", get_i8(tab + pos));+ break;+ case OP_FMT_u16:+ case OP_FMT_npop:+ printf(" %u", get_u16(tab + pos));+ break;+ case OP_FMT_npop_u16:+ printf(" %u,%u", get_u16(tab + pos), get_u16(tab + pos + 2));+ break;+ case OP_FMT_i16:+ printf(" %d", get_i16(tab + pos));+ break;+ case OP_FMT_i32:+ printf(" %d", get_i32(tab + pos));+ break;+ case OP_FMT_u32:+ printf(" %u", get_u32(tab + pos));+ break;+#if SHORT_OPCODES+ case OP_FMT_label8:+ addr = get_i8(tab + pos);+ goto has_addr1;+ case OP_FMT_label16:+ addr = get_i16(tab + pos);+ goto has_addr1;+#endif+ case OP_FMT_label:+ addr = get_u32(tab + pos);+ goto has_addr1;+ has_addr1:+ if (pass == 1)+ printf(" %u:%u", addr, label_slots[addr].pos);+ if (pass == 2)+ printf(" %u:%u", addr, label_slots[addr].pos2);+ if (pass == 3)+ printf(" %u", addr + pos);+ break;+ case OP_FMT_label_u16:+ addr = get_u32(tab + pos);+ if (pass == 1)+ printf(" %u:%u", addr, label_slots[addr].pos);+ if (pass == 2)+ printf(" %u:%u", addr, label_slots[addr].pos2);+ if (pass == 3)+ printf(" %u", addr + pos);+ printf(",%u", get_u16(tab + pos + 4));+ break;+#if SHORT_OPCODES+ case OP_FMT_const8:+ idx = get_u8(tab + pos);+ goto has_pool_idx;+#endif+ case OP_FMT_const:+ idx = get_u32(tab + pos);+ goto has_pool_idx;+ has_pool_idx:+ printf(" %u: ", idx);+ if (idx < cpool_count) {+ JS_DumpValue(ctx, cpool[idx]);+ }+ break;+ case OP_FMT_atom:+ printf(" ");+ print_atom(ctx, get_u32(tab + pos));+ break;+ case OP_FMT_atom_u8:+ printf(" ");+ print_atom(ctx, get_u32(tab + pos));+ printf(",%d", get_u8(tab + pos + 4));+ break;+ case OP_FMT_atom_u16:+ printf(" ");+ print_atom(ctx, get_u32(tab + pos));+ printf(",%d", get_u16(tab + pos + 4));+ break;+ case OP_FMT_atom_label_u8:+ case OP_FMT_atom_label_u16:+ printf(" ");+ print_atom(ctx, get_u32(tab + pos));+ addr = get_u32(tab + pos + 4);+ if (pass == 1)+ printf(",%u:%u", addr, label_slots[addr].pos);+ if (pass == 2)+ printf(",%u:%u", addr, label_slots[addr].pos2);+ if (pass == 3)+ printf(",%u", addr + pos + 4);+ if (oi->fmt == OP_FMT_atom_label_u8)+ printf(",%u", get_u8(tab + pos + 8));+ else+ printf(",%u", get_u16(tab + pos + 8));+ break;+ case OP_FMT_none_loc:+ idx = (op - OP_get_loc0) % 4;+ goto has_loc;+ case OP_FMT_loc8:+ idx = get_u8(tab + pos);+ goto has_loc;+ case OP_FMT_loc:+ idx = get_u16(tab + pos);+ has_loc:+ printf(" %d: ", idx);+ if (idx < var_count) {+ print_atom(ctx, vars[idx].var_name);+ }+ break;+ case OP_FMT_none_arg:+ idx = (op - OP_get_arg0) % 4;+ goto has_arg;+ case OP_FMT_arg:+ idx = get_u16(tab + pos);+ has_arg:+ printf(" %d: ", idx);+ if (idx < arg_count) {+ print_atom(ctx, args[idx].var_name);+ }+ break;+ case OP_FMT_none_var_ref:+ idx = (op - OP_get_var_ref0) % 4;+ goto has_var_ref;+ case OP_FMT_var_ref:+ idx = get_u16(tab + pos);+ has_var_ref:+ printf(" %d: ", idx);+ if (idx < closure_var_count) {+ print_atom(ctx, closure_var[idx].var_name);+ }+ break;+ default:+ break;+ }+ printf("\n");+ pos += oi->size - 1;+ }+ if (source) {+ if (!in_source)+ printf("\n");+ print_lines(source, line, INT32_MAX);+ }+ js_free(ctx, bits);+}++static __maybe_unused void dump_pc2line(JSContext *ctx, const uint8_t *buf, int len,+ int line_num)+{+ const uint8_t *p_end, *p_next, *p;+ int pc, v;+ unsigned int op;++ if (len <= 0)+ return;++ printf("%5s %5s\n", "PC", "LINE");++ p = buf;+ p_end = buf + len;+ pc = 0;+ while (p < p_end) {+ op = *p++;+ if (op == 0) {+ v = unicode_from_utf8(p, p_end - p, &p_next);+ if (v < 0)+ goto fail;+ pc += v;+ p = p_next;+ v = unicode_from_utf8(p, p_end - p, &p_next);+ if (v < 0) {+ fail:+ printf("invalid pc2line encode pos=%d\n", (int)(p - buf));+ return;+ }+ if (!(v & 1)) {+ v = v >> 1;+ } else {+ v = -(v >> 1) - 1;+ }+ line_num += v;+ p = p_next;+ } else {+ op -= PC2LINE_OP_FIRST;+ pc += (op / PC2LINE_RANGE);+ line_num += (op % PC2LINE_RANGE) + PC2LINE_BASE;+ }+ printf("%5d %5d\n", pc, line_num);+ }+}++static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionBytecode *b)+{+ int i;+ char atom_buf[ATOM_GET_STR_BUF_SIZE];+ const char *str;++ if (b->has_debug && b->debug.filename != JS_ATOM_NULL) {+ str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->debug.filename);+ printf("%s:%d: ", str, b->debug.line_num);+ }++ str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->func_name);+ printf("function: %s%s\n", &"*"[b->func_kind != JS_FUNC_GENERATOR], str);+ if (b->js_mode) {+ printf(" mode:");+ if (b->js_mode & JS_MODE_STRICT)+ printf(" strict");+#ifdef CONFIG_BIGNUM+ if (b->js_mode & JS_MODE_MATH)+ printf(" math");+#endif+ printf("\n");+ }+ if (b->arg_count && b->vardefs) {+ printf(" args:");+ for(i = 0; i < b->arg_count; i++) {+ printf(" %s", JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf),+ b->vardefs[i].var_name));+ }+ printf("\n");+ }+ if (b->var_count && b->vardefs) {+ printf(" locals:\n");+ for(i = 0; i < b->var_count; i++) {+ JSVarDef *vd = &b->vardefs[b->arg_count + i];+ printf("%5d: %s %s", i,+ vd->var_kind == JS_VAR_CATCH ? "catch" :+ (vd->var_kind == JS_VAR_FUNCTION_DECL ||+ vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) ? "function" :+ vd->is_const ? "const" :+ vd->is_lexical ? "let" : "var",+ JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), vd->var_name));+ if (vd->scope_level)+ printf(" [level:%d next:%d]", vd->scope_level, vd->scope_next);+ printf("\n");+ }+ }+ if (b->closure_var_count) {+ printf(" closure vars:\n");+ for(i = 0; i < b->closure_var_count; i++) {+ JSClosureVar *cv = &b->closure_var[i];+ printf("%5d: %s %s:%s%d %s\n", i,+ JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), cv->var_name),+ cv->is_local ? "local" : "parent",+ cv->is_arg ? "arg" : "loc", cv->var_idx,+ cv->is_const ? "const" :+ cv->is_lexical ? "let" : "var");+ }+ }+ printf(" stack_size: %d\n", b->stack_size);+ printf(" opcodes:\n");+ dump_byte_code(ctx, 3, b->byte_code_buf, b->byte_code_len,+ b->vardefs, b->arg_count,+ b->vardefs ? b->vardefs + b->arg_count : NULL, b->var_count,+ b->closure_var, b->closure_var_count,+ b->cpool, b->cpool_count,+ b->has_debug ? b->debug.source : NULL,+ b->has_debug ? b->debug.line_num : -1, NULL, b);+#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 32)+ if (b->has_debug)+ dump_pc2line(ctx, b->debug.pc2line_buf, b->debug.pc2line_len, b->debug.line_num);+#endif+ printf("\n");+}+#endif++static int add_closure_var(JSContext *ctx, JSFunctionDef *s,+ BOOL is_local, BOOL is_arg,+ int var_idx, JSAtom var_name,+ BOOL is_const, BOOL is_lexical,+ JSVarKindEnum var_kind)+{+ JSClosureVar *cv;++ /* the closure variable indexes are currently stored on 16 bits */+ if (s->closure_var_count >= JS_MAX_LOCAL_VARS) {+ JS_ThrowInternalError(ctx, "too many closure variables");+ return -1;+ }++ if (js_resize_array(ctx, (void **)&s->closure_var,+ sizeof(s->closure_var[0]),+ &s->closure_var_size, s->closure_var_count + 1))+ return -1;+ cv = &s->closure_var[s->closure_var_count++];+ cv->is_local = is_local;+ cv->is_arg = is_arg;+ cv->is_const = is_const;+ cv->is_lexical = is_lexical;+ cv->var_kind = var_kind;+ cv->var_idx = var_idx;+ cv->var_name = JS_DupAtom(ctx, var_name);+ return s->closure_var_count - 1;+}++static int find_closure_var(JSContext *ctx, JSFunctionDef *s,+ JSAtom var_name)+{+ int i;+ for(i = 0; i < s->closure_var_count; i++) {+ JSClosureVar *cv = &s->closure_var[i];+ if (cv->var_name == var_name)+ return i;+ }+ return -1;+}++/* 'fd' must be a parent of 's'. Create in 's' a closure referencing a+ local variable (is_local = TRUE) or a closure (is_local = FALSE) in+ 'fd' */+static int get_closure_var2(JSContext *ctx, JSFunctionDef *s,+ JSFunctionDef *fd, BOOL is_local,+ BOOL is_arg, int var_idx, JSAtom var_name,+ BOOL is_const, BOOL is_lexical,+ JSVarKindEnum var_kind)+{+ int i;++ if (fd != s->parent) {+ var_idx = get_closure_var2(ctx, s->parent, fd, is_local,+ is_arg, var_idx, var_name,+ is_const, is_lexical, var_kind);+ if (var_idx < 0)+ return -1;+ is_local = FALSE;+ }+ for(i = 0; i < s->closure_var_count; i++) {+ JSClosureVar *cv = &s->closure_var[i];+ if (cv->var_idx == var_idx && cv->is_arg == is_arg &&+ cv->is_local == is_local)+ return i;+ }+ return add_closure_var(ctx, s, is_local, is_arg, var_idx, var_name,+ is_const, is_lexical, var_kind);+}++static int get_closure_var(JSContext *ctx, JSFunctionDef *s,+ JSFunctionDef *fd, BOOL is_arg,+ int var_idx, JSAtom var_name,+ BOOL is_const, BOOL is_lexical,+ JSVarKindEnum var_kind)+{+ return get_closure_var2(ctx, s, fd, TRUE, is_arg,+ var_idx, var_name, is_const, is_lexical,+ var_kind);+}++static int get_with_scope_opcode(int op)+{+ if (op == OP_scope_get_var_undef)+ return OP_with_get_var;+ else+ return OP_with_get_var + (op - OP_scope_get_var);+}++static BOOL can_opt_put_ref_value(const uint8_t *bc_buf, int pos)+{+ int opcode = bc_buf[pos];+ return (bc_buf[pos + 1] == OP_put_ref_value &&+ (opcode == OP_insert3 ||+ opcode == OP_perm4 ||+ opcode == OP_nop ||+ opcode == OP_rot3l));+}++static BOOL can_opt_put_global_ref_value(const uint8_t *bc_buf, int pos)+{+ int opcode = bc_buf[pos];+ return (bc_buf[pos + 1] == OP_put_ref_value &&+ (opcode == OP_insert3 ||+ opcode == OP_perm4 ||+ opcode == OP_nop ||+ opcode == OP_rot3l));+}++static int optimize_scope_make_ref(JSContext *ctx, JSFunctionDef *s,+ DynBuf *bc, uint8_t *bc_buf,+ LabelSlot *ls, int pos_next,+ int get_op, int var_idx)+{+ int label_pos, end_pos, pos;++ /* XXX: should optimize `loc(a) += expr` as `expr add_loc(a)`+ but only if expr does not modify `a`.+ should scan the code between pos_next and label_pos+ for operations that can potentially change `a`:+ OP_scope_make_ref(a), function calls, jumps and gosub.+ */+ /* replace the reference get/put with normal variable+ accesses */+ if (bc_buf[pos_next] == OP_get_ref_value) {+ dbuf_putc(bc, get_op);+ dbuf_put_u16(bc, var_idx);+ pos_next++;+ }+ /* remove the OP_label to make room for replacement */+ /* label should have a refcount of 0 anyway */+ /* XXX: should avoid this patch by inserting nops in phase 1 */+ label_pos = ls->pos;+ pos = label_pos - 5;+ assert(bc_buf[pos] == OP_label);+ /* label points to an instruction pair:+ - insert3 / put_ref_value+ - perm4 / put_ref_value+ - rot3l / put_ref_value+ - nop / put_ref_value+ */+ end_pos = label_pos + 2;+ if (bc_buf[label_pos] == OP_insert3)+ bc_buf[pos++] = OP_dup;+ bc_buf[pos] = get_op + 1;+ put_u16(bc_buf + pos + 1, var_idx);+ pos += 3;+ /* pad with OP_nop */+ while (pos < end_pos)+ bc_buf[pos++] = OP_nop;+ return pos_next;+}++static int optimize_scope_make_global_ref(JSContext *ctx, JSFunctionDef *s,+ DynBuf *bc, uint8_t *bc_buf,+ LabelSlot *ls, int pos_next,+ JSAtom var_name)+{+ int label_pos, end_pos, pos, op;+ BOOL is_strict;+ is_strict = ((s->js_mode & JS_MODE_STRICT) != 0);++ /* replace the reference get/put with normal variable+ accesses */+ if (is_strict) {+ /* need to check if the variable exists before evaluating the right+ expression */+ /* XXX: need an extra OP_true if destructuring an array */+ dbuf_putc(bc, OP_check_var);+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+ } else {+ /* XXX: need 2 extra OP_true if destructuring an array */+ }+ if (bc_buf[pos_next] == OP_get_ref_value) {+ dbuf_putc(bc, OP_get_var);+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+ pos_next++;+ }+ /* remove the OP_label to make room for replacement */+ /* label should have a refcount of 0 anyway */+ /* XXX: should have emitted several OP_nop to avoid this kludge */+ label_pos = ls->pos;+ pos = label_pos - 5;+ assert(bc_buf[pos] == OP_label);+ end_pos = label_pos + 2;+ op = bc_buf[label_pos];+ if (is_strict) {+ if (op != OP_nop) {+ switch(op) {+ case OP_insert3:+ op = OP_insert2;+ break;+ case OP_perm4:+ op = OP_perm3;+ break;+ case OP_rot3l:+ op = OP_swap;+ break;+ default:+ abort();+ }+ bc_buf[pos++] = op;+ }+ } else {+ if (op == OP_insert3)+ bc_buf[pos++] = OP_dup;+ }+ if (is_strict) {+ bc_buf[pos] = OP_put_var_strict;+ /* XXX: need 1 extra OP_drop if destructuring an array */+ } else {+ bc_buf[pos] = OP_put_var;+ /* XXX: need 2 extra OP_drop if destructuring an array */+ }+ put_u32(bc_buf + pos + 1, JS_DupAtom(ctx, var_name));+ pos += 5;+ /* pad with OP_nop */+ while (pos < end_pos)+ bc_buf[pos++] = OP_nop;+ return pos_next;+}++static int add_var_this(JSContext *ctx, JSFunctionDef *fd)+{+ int idx;+ idx = add_var(ctx, fd, JS_ATOM_this);+ if (idx >= 0 && fd->is_derived_class_constructor) {+ JSVarDef *vd = &fd->vars[idx];+ /* XXX: should have is_this flag or var type */+ vd->is_lexical = 1; /* used to trigger 'uninitialized' checks+ in a derived class constructor */+ }+ return idx;+}++static int resolve_pseudo_var(JSContext *ctx, JSFunctionDef *s,+ JSAtom var_name)+{+ int var_idx;++ if (!s->has_this_binding)+ return -1;+ switch(var_name) {+ case JS_ATOM_home_object:+ /* 'home_object' pseudo variable */+ if (s->home_object_var_idx < 0)+ s->home_object_var_idx = add_var(ctx, s, var_name);+ var_idx = s->home_object_var_idx;+ break;+ case JS_ATOM_this_active_func:+ /* 'this.active_func' pseudo variable */+ if (s->this_active_func_var_idx < 0)+ s->this_active_func_var_idx = add_var(ctx, s, var_name);+ var_idx = s->this_active_func_var_idx;+ break;+ case JS_ATOM_new_target:+ /* 'new.target' pseudo variable */+ if (s->new_target_var_idx < 0)+ s->new_target_var_idx = add_var(ctx, s, var_name);+ var_idx = s->new_target_var_idx;+ break;+ case JS_ATOM_this:+ /* 'this' pseudo variable */+ if (s->this_var_idx < 0)+ s->this_var_idx = add_var_this(ctx, s);+ var_idx = s->this_var_idx;+ break;+ default:+ var_idx = -1;+ break;+ }+ return var_idx;+}++/* test if 'var_name' is in the variable object on the stack. If is it+ the case, handle it and jump to 'label_done' */+static void var_object_test(JSContext *ctx, JSFunctionDef *s,+ JSAtom var_name, int op, DynBuf *bc,+ int *plabel_done, BOOL is_with)+{+ dbuf_putc(bc, get_with_scope_opcode(op));+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+ *plabel_done = new_label_fd(s, *plabel_done);+ dbuf_put_u32(bc, *plabel_done);+ dbuf_putc(bc, is_with);+ update_label(s, *plabel_done, 1);+ s->jump_size++;+}+ +/* return the position of the next opcode */+static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s,+ JSAtom var_name, int scope_level, int op,+ DynBuf *bc, uint8_t *bc_buf,+ LabelSlot *ls, int pos_next)+{+ int idx, var_idx, is_put;+ int label_done;+ JSFunctionDef *fd;+ JSVarDef *vd;+ BOOL is_pseudo_var, is_arg_scope;++ label_done = -1;++ /* XXX: could be simpler to use a specific function to+ resolve the pseudo variables */+ is_pseudo_var = (var_name == JS_ATOM_home_object ||+ var_name == JS_ATOM_this_active_func ||+ var_name == JS_ATOM_new_target ||+ var_name == JS_ATOM_this);++ /* resolve local scoped variables */+ var_idx = -1;+ for (idx = s->scopes[scope_level].first; idx >= 0;) {+ vd = &s->vars[idx];+ if (vd->var_name == var_name) {+ if (op == OP_scope_put_var || op == OP_scope_make_ref) {+ if (vd->is_const) {+ dbuf_putc(bc, OP_throw_error);+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+ dbuf_putc(bc, JS_THROW_VAR_RO);+ goto done;+ }+ }+ var_idx = idx;+ break;+ } else+ if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) {+ dbuf_putc(bc, OP_get_loc);+ dbuf_put_u16(bc, idx);+ var_object_test(ctx, s, var_name, op, bc, &label_done, 1);+ }+ idx = vd->scope_next;+ }+ is_arg_scope = (idx == ARG_SCOPE_END);+ if (var_idx < 0) {+ /* argument scope: variables are not visible but pseudo+ variables are visible */+ if (!is_arg_scope) {+ var_idx = find_var(ctx, s, var_name);+ }++ if (var_idx < 0 && is_pseudo_var)+ var_idx = resolve_pseudo_var(ctx, s, var_name);++ if (var_idx < 0 && var_name == JS_ATOM_arguments &&+ s->has_arguments_binding) {+ /* 'arguments' pseudo variable */+ var_idx = add_arguments_var(ctx, s);+ }+ if (var_idx < 0 && s->is_func_expr && var_name == s->func_name) {+ /* add a new variable with the function name */+ var_idx = add_func_var(ctx, s, var_name);+ }+ }+ if (var_idx >= 0) {+ if ((op == OP_scope_put_var || op == OP_scope_make_ref) &&+ !(var_idx & ARGUMENT_VAR_OFFSET) &&+ s->vars[var_idx].is_const) {+ /* only happens when assigning a function expression name+ in strict mode */+ dbuf_putc(bc, OP_throw_error);+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+ dbuf_putc(bc, JS_THROW_VAR_RO);+ goto done;+ }+ /* OP_scope_put_var_init is only used to initialize a+ lexical variable, so it is never used in a with or var object. It+ can be used with a closure (module global variable case). */+ switch (op) {+ case OP_scope_make_ref:+ if (!(var_idx & ARGUMENT_VAR_OFFSET) &&+ s->vars[var_idx].var_kind == JS_VAR_FUNCTION_NAME) {+ /* Create a dummy object reference for the func_var */+ dbuf_putc(bc, OP_object);+ dbuf_putc(bc, OP_get_loc);+ dbuf_put_u16(bc, var_idx);+ dbuf_putc(bc, OP_define_field);+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+ dbuf_putc(bc, OP_push_atom_value);+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+ } else+ if (label_done == -1 && can_opt_put_ref_value(bc_buf, ls->pos)) {+ int get_op;+ if (var_idx & ARGUMENT_VAR_OFFSET) {+ get_op = OP_get_arg;+ var_idx -= ARGUMENT_VAR_OFFSET;+ } else {+ if (s->vars[var_idx].is_lexical)+ get_op = OP_get_loc_check;+ else+ get_op = OP_get_loc;+ }+ pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls,+ pos_next, get_op, var_idx);+ } else {+ /* Create a dummy object with a named slot that is+ a reference to the local variable */+ if (var_idx & ARGUMENT_VAR_OFFSET) {+ dbuf_putc(bc, OP_make_arg_ref);+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+ dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET);+ } else {+ dbuf_putc(bc, OP_make_loc_ref);+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+ dbuf_put_u16(bc, var_idx);+ }+ }+ break;+ case OP_scope_get_ref:+ dbuf_putc(bc, OP_undefined);+ /* fall thru */+ case OP_scope_get_var_undef:+ case OP_scope_get_var:+ case OP_scope_put_var:+ case OP_scope_put_var_init:+ is_put = (op == OP_scope_put_var || op == OP_scope_put_var_init);+ if (var_idx & ARGUMENT_VAR_OFFSET) {+ dbuf_putc(bc, OP_get_arg + is_put);+ dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET);+ } else {+ if (is_put) {+ if (s->vars[var_idx].is_lexical) {+ if (op == OP_scope_put_var_init) {+ /* 'this' can only be initialized once */+ if (var_name == JS_ATOM_this)+ dbuf_putc(bc, OP_put_loc_check_init);+ else+ dbuf_putc(bc, OP_put_loc);+ } else {+ dbuf_putc(bc, OP_put_loc_check);+ }+ } else {+ dbuf_putc(bc, OP_put_loc);+ }+ } else {+ if (s->vars[var_idx].is_lexical) {+ dbuf_putc(bc, OP_get_loc_check);+ } else {+ dbuf_putc(bc, OP_get_loc);+ }+ }+ dbuf_put_u16(bc, var_idx);+ }+ break;+ case OP_scope_delete_var:+ dbuf_putc(bc, OP_push_false);+ break;+ }+ goto done;+ }+ /* check eval object */+ if (!is_arg_scope && s->var_object_idx >= 0 && !is_pseudo_var) {+ dbuf_putc(bc, OP_get_loc);+ dbuf_put_u16(bc, s->var_object_idx);+ var_object_test(ctx, s, var_name, op, bc, &label_done, 0);+ }+ /* check eval object in argument scope */+ if (s->arg_var_object_idx >= 0 && !is_pseudo_var) {+ dbuf_putc(bc, OP_get_loc);+ dbuf_put_u16(bc, s->arg_var_object_idx);+ var_object_test(ctx, s, var_name, op, bc, &label_done, 0);+ }++ /* check parent scopes */+ for (fd = s; fd->parent;) {+ scope_level = fd->parent_scope_level;+ fd = fd->parent;+ for (idx = fd->scopes[scope_level].first; idx >= 0;) {+ vd = &fd->vars[idx];+ if (vd->var_name == var_name) {+ if (op == OP_scope_put_var || op == OP_scope_make_ref) {+ if (vd->is_const) {+ dbuf_putc(bc, OP_throw_error);+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+ dbuf_putc(bc, JS_THROW_VAR_RO);+ goto done;+ }+ }+ var_idx = idx;+ break;+ } else if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) {+ vd->is_captured = 1;+ idx = get_closure_var(ctx, s, fd, FALSE, idx, vd->var_name, FALSE, FALSE, JS_VAR_NORMAL);+ if (idx >= 0) {+ dbuf_putc(bc, OP_get_var_ref);+ dbuf_put_u16(bc, idx);+ var_object_test(ctx, s, var_name, op, bc, &label_done, 1);+ }+ }+ idx = vd->scope_next;+ }+ is_arg_scope = (idx == ARG_SCOPE_END);+ if (var_idx >= 0)+ break;+ + if (!is_arg_scope) {+ var_idx = find_var(ctx, fd, var_name);+ if (var_idx >= 0)+ break;+ }+ if (is_pseudo_var) {+ var_idx = resolve_pseudo_var(ctx, fd, var_name);+ if (var_idx >= 0)+ break;+ }+ if (var_name == JS_ATOM_arguments && fd->has_arguments_binding) {+ var_idx = add_arguments_var(ctx, fd);+ break;+ }+ if (fd->is_func_expr && fd->func_name == var_name) {+ /* add a new variable with the function name */+ var_idx = add_func_var(ctx, fd, var_name);+ break;+ }++ /* check eval object */+ if (!is_arg_scope && fd->var_object_idx >= 0 && !is_pseudo_var) {+ vd = &fd->vars[fd->var_object_idx];+ vd->is_captured = 1;+ idx = get_closure_var(ctx, s, fd, FALSE,+ fd->var_object_idx, vd->var_name,+ FALSE, FALSE, JS_VAR_NORMAL);+ dbuf_putc(bc, OP_get_var_ref);+ dbuf_put_u16(bc, idx);+ var_object_test(ctx, s, var_name, op, bc, &label_done, 0);+ }++ /* check eval object in argument scope */+ if (fd->arg_var_object_idx >= 0 && !is_pseudo_var) {+ vd = &fd->vars[fd->arg_var_object_idx];+ vd->is_captured = 1;+ idx = get_closure_var(ctx, s, fd, FALSE,+ fd->arg_var_object_idx, vd->var_name,+ FALSE, FALSE, JS_VAR_NORMAL);+ dbuf_putc(bc, OP_get_var_ref);+ dbuf_put_u16(bc, idx);+ var_object_test(ctx, s, var_name, op, bc, &label_done, 0);+ }+ + if (fd->is_eval)+ break; /* it it necessarily the top level function */+ }++ /* check direct eval scope (in the closure of the eval function+ which is necessarily at the top level) */+ if (!fd)+ fd = s;+ if (var_idx < 0 && fd->is_eval) {+ int idx1;+ for (idx1 = 0; idx1 < fd->closure_var_count; idx1++) {+ JSClosureVar *cv = &fd->closure_var[idx1];+ if (var_name == cv->var_name) {+ if (fd != s) {+ idx = get_closure_var2(ctx, s, fd,+ FALSE,+ cv->is_arg, idx1,+ cv->var_name, cv->is_const,+ cv->is_lexical, cv->var_kind);+ } else {+ idx = idx1;+ }+ goto has_idx;+ } else if ((cv->var_name == JS_ATOM__var_ ||+ cv->var_name == JS_ATOM__arg_var_ ||+ cv->var_name == JS_ATOM__with_) && !is_pseudo_var) {+ int is_with = (cv->var_name == JS_ATOM__with_);+ if (fd != s) {+ idx = get_closure_var2(ctx, s, fd,+ FALSE,+ cv->is_arg, idx1,+ cv->var_name, FALSE, FALSE,+ JS_VAR_NORMAL);+ } else {+ idx = idx1;+ }+ dbuf_putc(bc, OP_get_var_ref);+ dbuf_put_u16(bc, idx);+ var_object_test(ctx, s, var_name, op, bc, &label_done, is_with);+ }+ }+ }++ if (var_idx >= 0) {+ /* find the corresponding closure variable */+ if (var_idx & ARGUMENT_VAR_OFFSET) {+ fd->args[var_idx - ARGUMENT_VAR_OFFSET].is_captured = 1;+ idx = get_closure_var(ctx, s, fd,+ TRUE, var_idx - ARGUMENT_VAR_OFFSET,+ var_name, FALSE, FALSE, JS_VAR_NORMAL);+ } else {+ fd->vars[var_idx].is_captured = 1;+ idx = get_closure_var(ctx, s, fd,+ FALSE, var_idx,+ var_name,+ fd->vars[var_idx].is_const,+ fd->vars[var_idx].is_lexical,+ fd->vars[var_idx].var_kind);+ }+ if (idx >= 0) {+ has_idx:+ if ((op == OP_scope_put_var || op == OP_scope_make_ref) &&+ s->closure_var[idx].is_const) {+ dbuf_putc(bc, OP_throw_error);+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+ dbuf_putc(bc, JS_THROW_VAR_RO);+ goto done;+ }+ switch (op) {+ case OP_scope_make_ref:+ if (s->closure_var[idx].var_kind == JS_VAR_FUNCTION_NAME) {+ /* Create a dummy object reference for the func_var */+ dbuf_putc(bc, OP_object);+ dbuf_putc(bc, OP_get_var_ref);+ dbuf_put_u16(bc, idx);+ dbuf_putc(bc, OP_define_field);+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+ dbuf_putc(bc, OP_push_atom_value);+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+ } else+ if (label_done == -1 &&+ can_opt_put_ref_value(bc_buf, ls->pos)) {+ int get_op;+ if (s->closure_var[idx].is_lexical)+ get_op = OP_get_var_ref_check;+ else+ get_op = OP_get_var_ref;+ pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls,+ pos_next,+ get_op, idx);+ } else {+ /* Create a dummy object with a named slot that is+ a reference to the closure variable */+ dbuf_putc(bc, OP_make_var_ref_ref);+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+ dbuf_put_u16(bc, idx);+ }+ break;+ case OP_scope_get_ref:+ /* XXX: should create a dummy object with a named slot that is+ a reference to the closure variable */+ dbuf_putc(bc, OP_undefined);+ /* fall thru */+ case OP_scope_get_var_undef:+ case OP_scope_get_var:+ case OP_scope_put_var:+ case OP_scope_put_var_init:+ is_put = (op == OP_scope_put_var ||+ op == OP_scope_put_var_init);+ if (is_put) {+ if (s->closure_var[idx].is_lexical) {+ if (op == OP_scope_put_var_init) {+ /* 'this' can only be initialized once */+ if (var_name == JS_ATOM_this)+ dbuf_putc(bc, OP_put_var_ref_check_init);+ else+ dbuf_putc(bc, OP_put_var_ref);+ } else {+ dbuf_putc(bc, OP_put_var_ref_check);+ }+ } else {+ dbuf_putc(bc, OP_put_var_ref);+ }+ } else {+ if (s->closure_var[idx].is_lexical) {+ dbuf_putc(bc, OP_get_var_ref_check);+ } else {+ dbuf_putc(bc, OP_get_var_ref);+ }+ }+ dbuf_put_u16(bc, idx);+ break;+ case OP_scope_delete_var:+ dbuf_putc(bc, OP_push_false);+ break;+ }+ goto done;+ }+ }++ /* global variable access */++ switch (op) {+ case OP_scope_make_ref:+ if (label_done == -1 && can_opt_put_global_ref_value(bc_buf, ls->pos)) {+ pos_next = optimize_scope_make_global_ref(ctx, s, bc, bc_buf, ls,+ pos_next, var_name);+ } else {+ dbuf_putc(bc, OP_make_var_ref);+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+ }+ break;+ case OP_scope_get_ref:+ /* XXX: should create a dummy object with a named slot that is+ a reference to the global variable */+ dbuf_putc(bc, OP_undefined);+ dbuf_putc(bc, OP_get_var);+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+ break;+ case OP_scope_get_var_undef:+ case OP_scope_get_var:+ case OP_scope_put_var:+ dbuf_putc(bc, OP_get_var_undef + (op - OP_scope_get_var_undef));+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+ break;+ case OP_scope_put_var_init:+ dbuf_putc(bc, OP_put_var_init);+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+ break;+ case OP_scope_delete_var:+ dbuf_putc(bc, OP_delete_var);+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+ break;+ }+done:+ if (label_done >= 0) {+ dbuf_putc(bc, OP_label);+ dbuf_put_u32(bc, label_done);+ s->label_slots[label_done].pos2 = bc->size;+ }+ return pos_next;+}++/* search in all scopes */+static int find_private_class_field_all(JSContext *ctx, JSFunctionDef *fd,+ JSAtom name, int scope_level)+{+ int idx;++ idx = fd->scopes[scope_level].first;+ while (idx >= 0) {+ if (fd->vars[idx].var_name == name)+ return idx;+ idx = fd->vars[idx].scope_next;+ }+ return -1;+}++static void get_loc_or_ref(DynBuf *bc, BOOL is_ref, int idx)+{+ /* if the field is not initialized, the error is catched when+ accessing it */+ if (is_ref) + dbuf_putc(bc, OP_get_var_ref);+ else+ dbuf_putc(bc, OP_get_loc);+ dbuf_put_u16(bc, idx);+}++static int resolve_scope_private_field1(JSContext *ctx,+ BOOL *pis_ref, int *pvar_kind,+ JSFunctionDef *s,+ JSAtom var_name, int scope_level)+{+ int idx, var_kind;+ JSFunctionDef *fd;+ BOOL is_ref;+ + fd = s;+ is_ref = FALSE;+ for(;;) {+ idx = find_private_class_field_all(ctx, fd, var_name, scope_level);+ if (idx >= 0) {+ var_kind = fd->vars[idx].var_kind;+ if (is_ref) {+ idx = get_closure_var(ctx, s, fd, FALSE, idx, var_name,+ TRUE, TRUE, JS_VAR_NORMAL);+ if (idx < 0)+ return -1;+ }+ break;+ }+ scope_level = fd->parent_scope_level;+ if (!fd->parent) {+ if (fd->is_eval) {+ /* closure of the eval function (top level) */+ for (idx = 0; idx < fd->closure_var_count; idx++) {+ JSClosureVar *cv = &fd->closure_var[idx];+ if (cv->var_name == var_name) {+ var_kind = cv->var_kind;+ is_ref = TRUE;+ if (fd != s) {+ idx = get_closure_var2(ctx, s, fd,+ FALSE,+ cv->is_arg, idx,+ cv->var_name, cv->is_const,+ cv->is_lexical,+ cv->var_kind);+ if (idx < 0)+ return -1;+ }+ goto done;+ }+ }+ }+ /* XXX: no line number info */+ JS_ThrowSyntaxErrorAtom(ctx, "undefined private field '%s'",+ var_name);+ return -1;+ } else {+ fd = fd->parent;+ }+ is_ref = TRUE;+ }+ done:+ *pis_ref = is_ref;+ *pvar_kind = var_kind;+ return idx;+}++/* return 0 if OK or -1 if the private field could not be resolved */+static int resolve_scope_private_field(JSContext *ctx, JSFunctionDef *s,+ JSAtom var_name, int scope_level, int op,+ DynBuf *bc)+{+ int idx, var_kind;+ BOOL is_ref;++ idx = resolve_scope_private_field1(ctx, &is_ref, &var_kind, s,+ var_name, scope_level);+ if (idx < 0)+ return -1;+ assert(var_kind != JS_VAR_NORMAL);+ switch (op) {+ case OP_scope_get_private_field:+ case OP_scope_get_private_field2:+ switch(var_kind) {+ case JS_VAR_PRIVATE_FIELD:+ if (op == OP_scope_get_private_field2)+ dbuf_putc(bc, OP_dup);+ get_loc_or_ref(bc, is_ref, idx);+ dbuf_putc(bc, OP_get_private_field);+ break;+ case JS_VAR_PRIVATE_METHOD:+ get_loc_or_ref(bc, is_ref, idx);+ dbuf_putc(bc, OP_check_brand);+ if (op != OP_scope_get_private_field2)+ dbuf_putc(bc, OP_nip);+ break;+ case JS_VAR_PRIVATE_GETTER:+ case JS_VAR_PRIVATE_GETTER_SETTER:+ if (op == OP_scope_get_private_field2)+ dbuf_putc(bc, OP_dup);+ get_loc_or_ref(bc, is_ref, idx);+ dbuf_putc(bc, OP_check_brand);+ dbuf_putc(bc, OP_call_method);+ dbuf_put_u16(bc, 0);+ break;+ case JS_VAR_PRIVATE_SETTER:+ /* XXX: add clearer error message */+ dbuf_putc(bc, OP_throw_error);+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+ dbuf_putc(bc, JS_THROW_VAR_RO);+ break;+ default:+ abort();+ }+ break;+ case OP_scope_put_private_field:+ switch(var_kind) {+ case JS_VAR_PRIVATE_FIELD:+ get_loc_or_ref(bc, is_ref, idx);+ dbuf_putc(bc, OP_put_private_field);+ break;+ case JS_VAR_PRIVATE_METHOD:+ case JS_VAR_PRIVATE_GETTER:+ /* XXX: add clearer error message */+ dbuf_putc(bc, OP_throw_error);+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));+ dbuf_putc(bc, JS_THROW_VAR_RO);+ break;+ case JS_VAR_PRIVATE_SETTER:+ case JS_VAR_PRIVATE_GETTER_SETTER:+ {+ JSAtom setter_name = get_private_setter_name(ctx, var_name);+ if (setter_name == JS_ATOM_NULL)+ return -1;+ idx = resolve_scope_private_field1(ctx, &is_ref,+ &var_kind, s,+ setter_name, scope_level);+ JS_FreeAtom(ctx, setter_name);+ if (idx < 0)+ return -1;+ assert(var_kind == JS_VAR_PRIVATE_SETTER);+ get_loc_or_ref(bc, is_ref, idx);+ dbuf_putc(bc, OP_swap);+ /* obj func value */+ dbuf_putc(bc, OP_rot3r);+ /* value obj func */+ dbuf_putc(bc, OP_check_brand);+ dbuf_putc(bc, OP_rot3l);+ /* obj func value */+ dbuf_putc(bc, OP_call_method);+ dbuf_put_u16(bc, 1);+ }+ break;+ default:+ abort();+ }+ break;+ default:+ abort();+ }+ return 0;+}++static void mark_eval_captured_variables(JSContext *ctx, JSFunctionDef *s,+ int scope_level)+{+ int idx;+ JSVarDef *vd;++ for (idx = s->scopes[scope_level].first; idx >= 0;) {+ vd = &s->vars[idx];+ vd->is_captured = 1;+ idx = vd->scope_next;+ }+}++/* XXX: should handle the argument scope generically */+static BOOL is_var_in_arg_scope(const JSVarDef *vd)+{+ return (vd->var_name == JS_ATOM_home_object ||+ vd->var_name == JS_ATOM_this_active_func ||+ vd->var_name == JS_ATOM_new_target ||+ vd->var_name == JS_ATOM_this ||+ vd->var_name == JS_ATOM__arg_var_ ||+ vd->var_kind == JS_VAR_FUNCTION_NAME);+}++static void add_eval_variables(JSContext *ctx, JSFunctionDef *s)+{+ JSFunctionDef *fd;+ JSVarDef *vd;+ int i, scope_level, scope_idx;+ BOOL has_arguments_binding, has_this_binding, is_arg_scope;++ /* in non strict mode, variables are created in the caller's+ environment object */+ if (!s->is_eval && !(s->js_mode & JS_MODE_STRICT)) {+ s->var_object_idx = add_var(ctx, s, JS_ATOM__var_);+ if (s->has_parameter_expressions) {+ /* an additional variable object is needed for the+ argument scope */+ s->arg_var_object_idx = add_var(ctx, s, JS_ATOM__arg_var_);+ }+ }++ /* eval can potentially use 'arguments' so we must define it */+ has_this_binding = s->has_this_binding;+ if (has_this_binding) {+ if (s->this_var_idx < 0)+ s->this_var_idx = add_var_this(ctx, s);+ if (s->new_target_var_idx < 0)+ s->new_target_var_idx = add_var(ctx, s, JS_ATOM_new_target);+ if (s->is_derived_class_constructor && s->this_active_func_var_idx < 0)+ s->this_active_func_var_idx = add_var(ctx, s, JS_ATOM_this_active_func);+ if (s->has_home_object && s->home_object_var_idx < 0)+ s->home_object_var_idx = add_var(ctx, s, JS_ATOM_home_object);+ }+ has_arguments_binding = s->has_arguments_binding;+ if (has_arguments_binding) {+ add_arguments_var(ctx, s);+ /* also add an arguments binding in the argument scope to+ raise an error if a direct eval in the argument scope tries+ to redefine it */+ if (s->has_parameter_expressions && !(s->js_mode & JS_MODE_STRICT))+ add_arguments_arg(ctx, s);+ }+ if (s->is_func_expr && s->func_name != JS_ATOM_NULL)+ add_func_var(ctx, s, s->func_name);++ /* eval can use all the variables of the enclosing functions, so+ they must be all put in the closure. The closure variables are+ ordered by scope. It works only because no closure are created+ before. */+ assert(s->is_eval || s->closure_var_count == 0);++ /* XXX: inefficient, but eval performance is less critical */+ fd = s;+ for(;;) {+ scope_level = fd->parent_scope_level;+ fd = fd->parent;+ if (!fd)+ break;+ /* add 'this' if it was not previously added */+ if (!has_this_binding && fd->has_this_binding) {+ if (fd->this_var_idx < 0)+ fd->this_var_idx = add_var_this(ctx, fd);+ if (fd->new_target_var_idx < 0)+ fd->new_target_var_idx = add_var(ctx, fd, JS_ATOM_new_target);+ if (fd->is_derived_class_constructor && fd->this_active_func_var_idx < 0)+ fd->this_active_func_var_idx = add_var(ctx, fd, JS_ATOM_this_active_func);+ if (fd->has_home_object && fd->home_object_var_idx < 0)+ fd->home_object_var_idx = add_var(ctx, fd, JS_ATOM_home_object);+ has_this_binding = TRUE;+ }+ /* add 'arguments' if it was not previously added */+ if (!has_arguments_binding && fd->has_arguments_binding) {+ add_arguments_var(ctx, fd);+ has_arguments_binding = TRUE;+ }+ /* add function name */+ if (fd->is_func_expr && fd->func_name != JS_ATOM_NULL)+ add_func_var(ctx, fd, fd->func_name);++ /* add lexical variables */+ scope_idx = fd->scopes[scope_level].first;+ while (scope_idx >= 0) {+ vd = &fd->vars[scope_idx];+ vd->is_captured = 1;+ get_closure_var(ctx, s, fd, FALSE, scope_idx,+ vd->var_name, vd->is_const, vd->is_lexical, vd->var_kind);+ scope_idx = vd->scope_next;+ }+ is_arg_scope = (scope_idx == ARG_SCOPE_END);+ if (!is_arg_scope) {+ /* add unscoped variables */+ for(i = 0; i < fd->arg_count; i++) {+ vd = &fd->args[i];+ if (vd->var_name != JS_ATOM_NULL) {+ get_closure_var(ctx, s, fd,+ TRUE, i, vd->var_name, FALSE, FALSE,+ JS_VAR_NORMAL);+ }+ }+ for(i = 0; i < fd->var_count; i++) {+ vd = &fd->vars[i];+ /* do not close top level last result */+ if (vd->scope_level == 0 &&+ vd->var_name != JS_ATOM__ret_ &&+ vd->var_name != JS_ATOM_NULL) {+ get_closure_var(ctx, s, fd,+ FALSE, i, vd->var_name, FALSE, FALSE,+ JS_VAR_NORMAL);+ }+ }+ } else {+ for(i = 0; i < fd->var_count; i++) {+ vd = &fd->vars[i];+ /* do not close top level last result */+ if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) {+ get_closure_var(ctx, s, fd,+ FALSE, i, vd->var_name, FALSE, FALSE,+ JS_VAR_NORMAL);+ }+ }+ }+ if (fd->is_eval) {+ int idx;+ /* add direct eval variables (we are necessarily at the+ top level) */+ for (idx = 0; idx < fd->closure_var_count; idx++) {+ JSClosureVar *cv = &fd->closure_var[idx];+ get_closure_var2(ctx, s, fd,+ FALSE, cv->is_arg,+ idx, cv->var_name, cv->is_const,+ cv->is_lexical, cv->var_kind);+ }+ }+ }+}++static void set_closure_from_var(JSContext *ctx, JSClosureVar *cv,+ JSVarDef *vd, int var_idx)+{+ cv->is_local = TRUE;+ cv->is_arg = FALSE;+ cv->is_const = vd->is_const;+ cv->is_lexical = vd->is_lexical;+ cv->var_kind = vd->var_kind;+ cv->var_idx = var_idx;+ cv->var_name = JS_DupAtom(ctx, vd->var_name);+}++/* for direct eval compilation: add references to the variables of the+ calling function */+static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s,+ JSFunctionBytecode *b, int scope_idx)+{+ int i, count;+ JSVarDef *vd;+ BOOL is_arg_scope;+ + count = b->arg_count + b->var_count + b->closure_var_count;+ s->closure_var = NULL;+ s->closure_var_count = 0;+ s->closure_var_size = count;+ if (count == 0)+ return 0;+ s->closure_var = js_malloc(ctx, sizeof(s->closure_var[0]) * count);+ if (!s->closure_var)+ return -1;+ /* Add lexical variables in scope at the point of evaluation */+ for (i = scope_idx; i >= 0;) {+ vd = &b->vardefs[b->arg_count + i];+ if (vd->scope_level > 0) {+ JSClosureVar *cv = &s->closure_var[s->closure_var_count++];+ set_closure_from_var(ctx, cv, vd, i);+ }+ i = vd->scope_next;+ }+ is_arg_scope = (i == ARG_SCOPE_END);+ if (!is_arg_scope) {+ /* Add argument variables */+ for(i = 0; i < b->arg_count; i++) {+ JSClosureVar *cv = &s->closure_var[s->closure_var_count++];+ vd = &b->vardefs[i];+ cv->is_local = TRUE;+ cv->is_arg = TRUE;+ cv->is_const = FALSE;+ cv->is_lexical = FALSE;+ cv->var_kind = JS_VAR_NORMAL;+ cv->var_idx = i;+ cv->var_name = JS_DupAtom(ctx, vd->var_name);+ }+ /* Add local non lexical variables */+ for(i = 0; i < b->var_count; i++) {+ vd = &b->vardefs[b->arg_count + i];+ if (vd->scope_level == 0 && vd->var_name != JS_ATOM__ret_) {+ JSClosureVar *cv = &s->closure_var[s->closure_var_count++];+ set_closure_from_var(ctx, cv, vd, i);+ }+ }+ } else {+ /* only add pseudo variables */+ for(i = 0; i < b->var_count; i++) {+ vd = &b->vardefs[b->arg_count + i];+ if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) {+ JSClosureVar *cv = &s->closure_var[s->closure_var_count++];+ set_closure_from_var(ctx, cv, vd, i);+ }+ }+ }+ for(i = 0; i < b->closure_var_count; i++) {+ JSClosureVar *cv0 = &b->closure_var[i];+ JSClosureVar *cv = &s->closure_var[s->closure_var_count++];+ cv->is_local = FALSE;+ cv->is_arg = cv0->is_arg;+ cv->is_const = cv0->is_const;+ cv->is_lexical = cv0->is_lexical;+ cv->var_kind = cv0->var_kind;+ cv->var_idx = i;+ cv->var_name = JS_DupAtom(ctx, cv0->var_name);+ }+ return 0;+}++typedef struct CodeContext {+ const uint8_t *bc_buf; /* code buffer */+ int bc_len; /* length of the code buffer */+ int pos; /* position past the matched code pattern */+ int line_num; /* last visited OP_line_num parameter or -1 */+ int op;+ int idx;+ int label;+ int val;+ JSAtom atom;+} CodeContext;++#define M2(op1, op2) ((op1) | ((op2) << 8))+#define M3(op1, op2, op3) ((op1) | ((op2) << 8) | ((op3) << 16))+#define M4(op1, op2, op3, op4) ((op1) | ((op2) << 8) | ((op3) << 16) | ((op4) << 24))++static BOOL code_match(CodeContext *s, int pos, ...)+{+ const uint8_t *tab = s->bc_buf;+ int op, len, op1, line_num, pos_next;+ va_list ap;+ BOOL ret = FALSE;++ line_num = -1;+ va_start(ap, pos);++ for(;;) {+ op1 = va_arg(ap, int);+ if (op1 == -1) {+ s->pos = pos;+ s->line_num = line_num;+ ret = TRUE;+ break;+ }+ for (;;) {+ if (pos >= s->bc_len)+ goto done;+ op = tab[pos];+ len = opcode_info[op].size;+ pos_next = pos + len;+ if (pos_next > s->bc_len)+ goto done;+ if (op == OP_line_num) {+ line_num = get_u32(tab + pos + 1);+ pos = pos_next;+ } else {+ break;+ }+ }+ if (op != op1) {+ if (op1 == (uint8_t)op1 || !op)+ break;+ if (op != (uint8_t)op1+ && op != (uint8_t)(op1 >> 8)+ && op != (uint8_t)(op1 >> 16)+ && op != (uint8_t)(op1 >> 24)) {+ break;+ }+ s->op = op;+ }++ pos++;+ switch(opcode_info[op].fmt) {+ case OP_FMT_loc8:+ case OP_FMT_u8:+ {+ int idx = tab[pos];+ int arg = va_arg(ap, int);+ if (arg == -1) {+ s->idx = idx;+ } else {+ if (arg != idx)+ goto done;+ }+ break;+ }+ case OP_FMT_u16:+ case OP_FMT_npop:+ case OP_FMT_loc:+ case OP_FMT_arg:+ case OP_FMT_var_ref:+ {+ int idx = get_u16(tab + pos);+ int arg = va_arg(ap, int);+ if (arg == -1) {+ s->idx = idx;+ } else {+ if (arg != idx)+ goto done;+ }+ break;+ }+ case OP_FMT_i32:+ case OP_FMT_u32:+ case OP_FMT_label:+ case OP_FMT_const:+ {+ s->label = get_u32(tab + pos);+ break;+ }+ case OP_FMT_label_u16:+ {+ s->label = get_u32(tab + pos);+ s->val = get_u16(tab + pos + 4);+ break;+ }+ case OP_FMT_atom:+ {+ s->atom = get_u32(tab + pos);+ break;+ }+ case OP_FMT_atom_u8:+ {+ s->atom = get_u32(tab + pos);+ s->val = get_u8(tab + pos + 4);+ break;+ }+ case OP_FMT_atom_u16:+ {+ s->atom = get_u32(tab + pos);+ s->val = get_u16(tab + pos + 4);+ break;+ }+ case OP_FMT_atom_label_u8:+ {+ s->atom = get_u32(tab + pos);+ s->label = get_u32(tab + pos + 4);+ s->val = get_u8(tab + pos + 8);+ break;+ }+ default:+ break;+ }+ pos = pos_next;+ }+ done:+ va_end(ap);+ return ret;+}++static void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, DynBuf *bc)+{+ int i, idx, label_next = -1;++ /* add the hoisted functions in arguments and local variables */+ for(i = 0; i < s->arg_count; i++) {+ JSVarDef *vd = &s->args[i];+ if (vd->func_pool_idx >= 0) {+ dbuf_putc(bc, OP_fclosure);+ dbuf_put_u32(bc, vd->func_pool_idx);+ dbuf_putc(bc, OP_put_arg);+ dbuf_put_u16(bc, i);+ }+ }+ for(i = 0; i < s->var_count; i++) {+ JSVarDef *vd = &s->vars[i];+ if (vd->scope_level == 0 && vd->func_pool_idx >= 0) {+ dbuf_putc(bc, OP_fclosure);+ dbuf_put_u32(bc, vd->func_pool_idx);+ dbuf_putc(bc, OP_put_loc);+ dbuf_put_u16(bc, i);+ }+ }++ /* the module global variables must be initialized before+ evaluating the module so that the exported functions are+ visible if there are cyclic module references */+ if (s->module) {+ label_next = new_label_fd(s, -1);+ + /* if 'this' is true, initialize the global variables and return */+ dbuf_putc(bc, OP_push_this);+ dbuf_putc(bc, OP_if_false);+ dbuf_put_u32(bc, label_next);+ update_label(s, label_next, 1);+ s->jump_size++;+ }+ + /* add the global variables (only happens if s->is_global_var is+ true) */+ for(i = 0; i < s->global_var_count; i++) {+ JSGlobalVar *hf = &s->global_vars[i];+ int has_closure = 0;+ BOOL force_init = hf->force_init;+ /* we are in an eval, so the closure contains all the+ enclosing variables */+ /* If the outer function has a variable environment,+ create a property for the variable there */+ for(idx = 0; idx < s->closure_var_count; idx++) {+ JSClosureVar *cv = &s->closure_var[idx];+ if (cv->var_name == hf->var_name) {+ has_closure = 2;+ force_init = FALSE;+ break;+ }+ if (cv->var_name == JS_ATOM__var_ ||+ cv->var_name == JS_ATOM__arg_var_) {+ dbuf_putc(bc, OP_get_var_ref);+ dbuf_put_u16(bc, idx);+ has_closure = 1;+ force_init = TRUE;+ break;+ }+ }+ if (!has_closure) {+ int flags;+ + flags = 0;+ if (s->eval_type != JS_EVAL_TYPE_GLOBAL)+ flags |= JS_PROP_CONFIGURABLE;+ if (hf->cpool_idx >= 0 && !hf->is_lexical) {+ /* global function definitions need a specific handling */+ dbuf_putc(bc, OP_fclosure);+ dbuf_put_u32(bc, hf->cpool_idx);+ + dbuf_putc(bc, OP_define_func);+ dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));+ dbuf_putc(bc, flags);+ + goto done_global_var;+ } else {+ if (hf->is_lexical) {+ flags |= DEFINE_GLOBAL_LEX_VAR;+ if (!hf->is_const)+ flags |= JS_PROP_WRITABLE;+ }+ dbuf_putc(bc, OP_define_var);+ dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));+ dbuf_putc(bc, flags);+ }+ }+ if (hf->cpool_idx >= 0 || force_init) {+ if (hf->cpool_idx >= 0) {+ dbuf_putc(bc, OP_fclosure);+ dbuf_put_u32(bc, hf->cpool_idx);+ if (hf->var_name == JS_ATOM__default_) {+ /* set default export function name */+ dbuf_putc(bc, OP_set_name);+ dbuf_put_u32(bc, JS_DupAtom(ctx, JS_ATOM_default));+ }+ } else {+ dbuf_putc(bc, OP_undefined);+ }+ if (has_closure == 2) {+ dbuf_putc(bc, OP_put_var_ref);+ dbuf_put_u16(bc, idx);+ } else if (has_closure == 1) {+ dbuf_putc(bc, OP_define_field);+ dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));+ dbuf_putc(bc, OP_drop);+ } else {+ /* XXX: Check if variable is writable and enumerable */+ dbuf_putc(bc, OP_put_var);+ dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));+ }+ }+ done_global_var:+ JS_FreeAtom(ctx, hf->var_name);+ }++ if (s->module) {+ dbuf_putc(bc, OP_return_undef);+ + dbuf_putc(bc, OP_label);+ dbuf_put_u32(bc, label_next);+ s->label_slots[label_next].pos2 = bc->size;+ }++ js_free(ctx, s->global_vars);+ s->global_vars = NULL;+ s->global_var_count = 0;+ s->global_var_size = 0;+}++static int skip_dead_code(JSFunctionDef *s, const uint8_t *bc_buf, int bc_len,+ int pos, int *linep)+{+ int op, len, label;++ for (; pos < bc_len; pos += len) {+ op = bc_buf[pos];+ len = opcode_info[op].size;+ if (op == OP_line_num) {+ *linep = get_u32(bc_buf + pos + 1);+ } else+ if (op == OP_label) {+ label = get_u32(bc_buf + pos + 1);+ if (update_label(s, label, 0) > 0)+ break;+#if 0+ if (s->label_slots[label].first_reloc) {+ printf("line %d: unreferenced label %d:%d has relocations\n",+ *linep, label, s->label_slots[label].pos2);+ }+#endif+ assert(s->label_slots[label].first_reloc == NULL);+ } else {+ /* XXX: output a warning for unreachable code? */+ JSAtom atom;+ switch(opcode_info[op].fmt) {+ case OP_FMT_label:+ case OP_FMT_label_u16:+ label = get_u32(bc_buf + pos + 1);+ update_label(s, label, -1);+ break;+ case OP_FMT_atom_label_u8:+ case OP_FMT_atom_label_u16:+ label = get_u32(bc_buf + pos + 5);+ update_label(s, label, -1);+ /* fall thru */+ case OP_FMT_atom:+ case OP_FMT_atom_u8:+ case OP_FMT_atom_u16:+ atom = get_u32(bc_buf + pos + 1);+ JS_FreeAtom(s->ctx, atom);+ break;+ default:+ break;+ }+ }+ }+ return pos;+}++static int get_label_pos(JSFunctionDef *s, int label)+{+ int i, pos;+ for (i = 0; i < 20; i++) {+ pos = s->label_slots[label].pos;+ for (;;) {+ switch (s->byte_code.buf[pos]) {+ case OP_line_num:+ case OP_label:+ pos += 5;+ continue;+ case OP_goto:+ label = get_u32(s->byte_code.buf + pos + 1);+ break;+ default:+ return pos;+ }+ break;+ }+ }+ return pos;+}++/* convert global variable accesses to local variables or closure+ variables when necessary */+static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s)+{+ int pos, pos_next, bc_len, op, len, i, idx, line_num;+ uint8_t *bc_buf;+ JSAtom var_name;+ DynBuf bc_out;+ CodeContext cc;+ int scope;++ cc.bc_buf = bc_buf = s->byte_code.buf;+ cc.bc_len = bc_len = s->byte_code.size;+ js_dbuf_init(ctx, &bc_out);++ /* first pass for runtime checks (must be done before the+ variables are created) */+ for(i = 0; i < s->global_var_count; i++) {+ JSGlobalVar *hf = &s->global_vars[i];+ int flags;+ + /* check if global variable (XXX: simplify) */+ for(idx = 0; idx < s->closure_var_count; idx++) {+ JSClosureVar *cv = &s->closure_var[idx];+ if (cv->var_name == hf->var_name) {+ if (s->eval_type == JS_EVAL_TYPE_DIRECT &&+ cv->is_lexical) {+ /* Check if a lexical variable is+ redefined as 'var'. XXX: Could abort+ compilation here, but for consistency+ with the other checks, we delay the+ error generation. */+ dbuf_putc(&bc_out, OP_throw_error);+ dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name));+ dbuf_putc(&bc_out, JS_THROW_VAR_REDECL);+ }+ goto next;+ }+ if (cv->var_name == JS_ATOM__var_ ||+ cv->var_name == JS_ATOM__arg_var_)+ goto next;+ }+ + dbuf_putc(&bc_out, OP_check_define_var);+ dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name));+ flags = 0;+ if (hf->is_lexical)+ flags |= DEFINE_GLOBAL_LEX_VAR;+ if (hf->cpool_idx >= 0)+ flags |= DEFINE_GLOBAL_FUNC_VAR;+ dbuf_putc(&bc_out, flags);+ next: ;+ }++ line_num = 0; /* avoid warning */+ for (pos = 0; pos < bc_len; pos = pos_next) {+ op = bc_buf[pos];+ len = opcode_info[op].size;+ pos_next = pos + len;+ switch(op) {+ case OP_line_num:+ line_num = get_u32(bc_buf + pos + 1);+ s->line_number_size++;+ goto no_change;++ case OP_eval: /* convert scope index to adjusted variable index */+ {+ int call_argc = get_u16(bc_buf + pos + 1);+ scope = get_u16(bc_buf + pos + 1 + 2);+ mark_eval_captured_variables(ctx, s, scope);+ dbuf_putc(&bc_out, op);+ dbuf_put_u16(&bc_out, call_argc);+ dbuf_put_u16(&bc_out, s->scopes[scope].first + 1);+ }+ break;+ case OP_apply_eval: /* convert scope index to adjusted variable index */+ scope = get_u16(bc_buf + pos + 1);+ mark_eval_captured_variables(ctx, s, scope);+ dbuf_putc(&bc_out, op);+ dbuf_put_u16(&bc_out, s->scopes[scope].first + 1);+ break;+ case OP_scope_get_var_undef:+ case OP_scope_get_var:+ case OP_scope_put_var:+ case OP_scope_delete_var:+ case OP_scope_get_ref:+ case OP_scope_put_var_init:+ var_name = get_u32(bc_buf + pos + 1);+ scope = get_u16(bc_buf + pos + 5);+ pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out,+ NULL, NULL, pos_next);+ JS_FreeAtom(ctx, var_name);+ break;+ case OP_scope_make_ref:+ {+ int label;+ LabelSlot *ls;+ var_name = get_u32(bc_buf + pos + 1);+ label = get_u32(bc_buf + pos + 5);+ scope = get_u16(bc_buf + pos + 9);+ ls = &s->label_slots[label];+ ls->ref_count--; /* always remove label reference */+ pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out,+ bc_buf, ls, pos_next);+ JS_FreeAtom(ctx, var_name);+ }+ break;+ case OP_scope_get_private_field:+ case OP_scope_get_private_field2:+ case OP_scope_put_private_field:+ {+ int ret;+ var_name = get_u32(bc_buf + pos + 1);+ scope = get_u16(bc_buf + pos + 5);+ ret = resolve_scope_private_field(ctx, s, var_name, scope, op, &bc_out);+ if (ret < 0)+ goto fail;+ JS_FreeAtom(ctx, var_name);+ }+ break;+ case OP_gosub:+ s->jump_size++;+ if (OPTIMIZE) {+ /* remove calls to empty finalizers */+ int label;+ LabelSlot *ls;++ label = get_u32(bc_buf + pos + 1);+ assert(label >= 0 && label < s->label_count);+ ls = &s->label_slots[label];+ if (code_match(&cc, ls->pos, OP_ret, -1)) {+ ls->ref_count--;+ break;+ }+ }+ goto no_change;+ case OP_drop:+ if (0) {+ /* remove drops before return_undef */+ /* do not perform this optimization in pass2 because+ it breaks patterns recognised in resolve_labels */+ int pos1 = pos_next;+ int line1 = line_num;+ while (code_match(&cc, pos1, OP_drop, -1)) {+ if (cc.line_num >= 0) line1 = cc.line_num;+ pos1 = cc.pos;+ }+ if (code_match(&cc, pos1, OP_return_undef, -1)) {+ pos_next = pos1;+ if (line1 != -1 && line1 != line_num) {+ line_num = line1;+ s->line_number_size++;+ dbuf_putc(&bc_out, OP_line_num);+ dbuf_put_u32(&bc_out, line_num);+ }+ break;+ }+ }+ goto no_change;+ case OP_insert3:+ if (OPTIMIZE) {+ /* Transformation: insert3 put_array_el|put_ref_value drop -> put_array_el|put_ref_value */+ if (code_match(&cc, pos_next, M2(OP_put_array_el, OP_put_ref_value), OP_drop, -1)) {+ dbuf_putc(&bc_out, cc.op);+ pos_next = cc.pos;+ if (cc.line_num != -1 && cc.line_num != line_num) {+ line_num = cc.line_num;+ s->line_number_size++;+ dbuf_putc(&bc_out, OP_line_num);+ dbuf_put_u32(&bc_out, line_num);+ }+ break;+ }+ }+ goto no_change;++ case OP_goto:+ s->jump_size++;+ /* fall thru */+ case OP_tail_call:+ case OP_tail_call_method:+ case OP_return:+ case OP_return_undef:+ case OP_throw:+ case OP_throw_error:+ case OP_ret:+ if (OPTIMIZE) {+ /* remove dead code */+ int line = -1;+ dbuf_put(&bc_out, bc_buf + pos, len);+ pos = skip_dead_code(s, bc_buf, bc_len, pos + len, &line);+ pos_next = pos;+ if (pos < bc_len && line >= 0 && line_num != line) {+ line_num = line;+ s->line_number_size++;+ dbuf_putc(&bc_out, OP_line_num);+ dbuf_put_u32(&bc_out, line_num);+ }+ break;+ }+ goto no_change;++ case OP_label:+ {+ int label;+ LabelSlot *ls;++ label = get_u32(bc_buf + pos + 1);+ assert(label >= 0 && label < s->label_count);+ ls = &s->label_slots[label];+ ls->pos2 = bc_out.size + opcode_info[op].size;+ }+ goto no_change;++ case OP_enter_scope:+ {+ int scope_idx, scope = get_u16(bc_buf + pos + 1);++ if (scope == s->body_scope) {+ instantiate_hoisted_definitions(ctx, s, &bc_out);+ }++ for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) {+ JSVarDef *vd = &s->vars[scope_idx];+ if (vd->scope_level == scope) {+ if (scope_idx != s->arguments_arg_idx) {+ if (vd->var_kind == JS_VAR_FUNCTION_DECL ||+ vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) {+ /* Initialize lexical variable upon entering scope */+ dbuf_putc(&bc_out, OP_fclosure);+ dbuf_put_u32(&bc_out, vd->func_pool_idx);+ dbuf_putc(&bc_out, OP_put_loc);+ dbuf_put_u16(&bc_out, scope_idx);+ } else {+ /* XXX: should check if variable can be used+ before initialization */+ dbuf_putc(&bc_out, OP_set_loc_uninitialized);+ dbuf_put_u16(&bc_out, scope_idx);+ }+ }+ scope_idx = vd->scope_next;+ } else {+ break;+ }+ }+ }+ break;++ case OP_leave_scope:+ {+ int scope_idx, scope = get_u16(bc_buf + pos + 1);++ for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) {+ JSVarDef *vd = &s->vars[scope_idx];+ if (vd->scope_level == scope) {+ if (vd->is_captured) {+ dbuf_putc(&bc_out, OP_close_loc);+ dbuf_put_u16(&bc_out, scope_idx);+ }+ scope_idx = vd->scope_next;+ } else {+ break;+ }+ }+ }+ break;++ case OP_set_name:+ {+ /* remove dummy set_name opcodes */+ JSAtom name = get_u32(bc_buf + pos + 1);+ if (name == JS_ATOM_NULL)+ break;+ }+ goto no_change;++ case OP_if_false:+ case OP_if_true:+ case OP_catch:+ s->jump_size++;+ goto no_change;++ case OP_dup:+ if (OPTIMIZE) {+ /* Transformation: dup if_false(l1) drop, l1: if_false(l2) -> if_false(l2) */+ /* Transformation: dup if_true(l1) drop, l1: if_true(l2) -> if_true(l2) */+ if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), OP_drop, -1)) {+ int lab0, lab1, op1, pos1, line1, pos2;+ lab0 = lab1 = cc.label;+ assert(lab1 >= 0 && lab1 < s->label_count);+ op1 = cc.op;+ pos1 = cc.pos;+ line1 = cc.line_num;+ while (code_match(&cc, (pos2 = get_label_pos(s, lab1)), OP_dup, op1, OP_drop, -1)) {+ lab1 = cc.label;+ }+ if (code_match(&cc, pos2, op1, -1)) {+ s->jump_size++;+ update_label(s, lab0, -1);+ update_label(s, cc.label, +1);+ dbuf_putc(&bc_out, op1);+ dbuf_put_u32(&bc_out, cc.label);+ pos_next = pos1;+ if (line1 != -1 && line1 != line_num) {+ line_num = line1;+ s->line_number_size++;+ dbuf_putc(&bc_out, OP_line_num);+ dbuf_put_u32(&bc_out, line_num);+ }+ break;+ }+ }+ }+ goto no_change;++ case OP_nop:+ /* remove erased code */+ break;+ case OP_set_class_name:+ /* only used during parsing */+ break;+ + default:+ no_change:+ dbuf_put(&bc_out, bc_buf + pos, len);+ break;+ }+ }++ /* set the new byte code */+ dbuf_free(&s->byte_code);+ s->byte_code = bc_out;+ if (dbuf_error(&s->byte_code)) {+ JS_ThrowOutOfMemory(ctx);+ return -1;+ }+ return 0;+ fail:+ /* continue the copy to keep the atom refcounts consistent */+ /* XXX: find a better solution ? */+ for (; pos < bc_len; pos = pos_next) {+ op = bc_buf[pos];+ len = opcode_info[op].size;+ pos_next = pos + len;+ dbuf_put(&bc_out, bc_buf + pos, len);+ }+ dbuf_free(&s->byte_code);+ s->byte_code = bc_out;+ return -1;+}++/* the pc2line table gives a line number for each PC value */+static void add_pc2line_info(JSFunctionDef *s, uint32_t pc, int line_num)+{+ if (s->line_number_slots != NULL+ && s->line_number_count < s->line_number_size+ && pc >= s->line_number_last_pc+ && line_num != s->line_number_last) {+ s->line_number_slots[s->line_number_count].pc = pc;+ s->line_number_slots[s->line_number_count].line_num = line_num;+ s->line_number_count++;+ s->line_number_last_pc = pc;+ s->line_number_last = line_num;+ }+}++static void compute_pc2line_info(JSFunctionDef *s)+{+ if (!(s->js_mode & JS_MODE_STRIP) && s->line_number_slots) {+ int last_line_num = s->line_num;+ uint32_t last_pc = 0;+ int i;++ js_dbuf_init(s->ctx, &s->pc2line);+ for (i = 0; i < s->line_number_count; i++) {+ uint32_t pc = s->line_number_slots[i].pc;+ int line_num = s->line_number_slots[i].line_num;+ int diff_pc, diff_line;++ if (line_num < 0)+ continue;++ diff_pc = pc - last_pc;+ diff_line = line_num - last_line_num;+ if (diff_line == 0 || diff_pc < 0)+ continue;++ if (diff_line >= PC2LINE_BASE &&+ diff_line < PC2LINE_BASE + PC2LINE_RANGE &&+ diff_pc <= PC2LINE_DIFF_PC_MAX) {+ dbuf_putc(&s->pc2line, (diff_line - PC2LINE_BASE) ++ diff_pc * PC2LINE_RANGE + PC2LINE_OP_FIRST);+ } else {+ /* longer encoding */+ dbuf_putc(&s->pc2line, 0);+ dbuf_put_leb128(&s->pc2line, diff_pc);+ dbuf_put_sleb128(&s->pc2line, diff_line);+ }+ last_pc = pc;+ last_line_num = line_num;+ }+ }+}++static RelocEntry *add_reloc(JSContext *ctx, LabelSlot *ls, uint32_t addr, int size)+{+ RelocEntry *re;+ re = js_malloc(ctx, sizeof(*re));+ if (!re)+ return NULL;+ re->addr = addr;+ re->size = size;+ re->next = ls->first_reloc;+ ls->first_reloc = re;+ return re;+}++static BOOL code_has_label(CodeContext *s, int pos, int label)+{+ while (pos < s->bc_len) {+ int op = s->bc_buf[pos];+ if (op == OP_line_num) {+ pos += 5;+ continue;+ }+ if (op == OP_label) {+ int lab = get_u32(s->bc_buf + pos + 1);+ if (lab == label)+ return TRUE;+ pos += 5;+ continue;+ }+ if (op == OP_goto) {+ int lab = get_u32(s->bc_buf + pos + 1);+ if (lab == label)+ return TRUE;+ }+ break;+ }+ return FALSE;+}++/* return the target label, following the OP_goto jumps+ the first opcode at destination is stored in *pop+ */+static int find_jump_target(JSFunctionDef *s, int label, int *pop, int *pline)+{+ int i, pos, op;++ update_label(s, label, -1);+ for (i = 0; i < 10; i++) {+ assert(label >= 0 && label < s->label_count);+ pos = s->label_slots[label].pos2;+ for (;;) {+ switch(op = s->byte_code.buf[pos]) {+ case OP_line_num:+ if (pline)+ *pline = get_u32(s->byte_code.buf + pos + 1);+ /* fall thru */+ case OP_label:+ pos += opcode_info[op].size;+ continue;+ case OP_goto:+ label = get_u32(s->byte_code.buf + pos + 1);+ break;+ case OP_drop:+ /* ignore drop opcodes if followed by OP_return_undef */+ while (s->byte_code.buf[++pos] == OP_drop)+ continue;+ if (s->byte_code.buf[pos] == OP_return_undef)+ op = OP_return_undef;+ /* fall thru */+ default:+ goto done;+ }+ break;+ }+ }+ /* cycle detected, could issue a warning */+ done:+ *pop = op;+ update_label(s, label, +1);+ return label;+}++static void push_short_int(DynBuf *bc_out, int val)+{+#if SHORT_OPCODES+ if (val >= -1 && val <= 7) {+ dbuf_putc(bc_out, OP_push_0 + val);+ return;+ }+ if (val == (int8_t)val) {+ dbuf_putc(bc_out, OP_push_i8);+ dbuf_putc(bc_out, val);+ return;+ }+ if (val == (int16_t)val) {+ dbuf_putc(bc_out, OP_push_i16);+ dbuf_put_u16(bc_out, val);+ return;+ }+#endif+ dbuf_putc(bc_out, OP_push_i32);+ dbuf_put_u32(bc_out, val);+}++static void put_short_code(DynBuf *bc_out, int op, int idx)+{+#if SHORT_OPCODES+ if (idx < 4) {+ switch (op) {+ case OP_get_loc:+ dbuf_putc(bc_out, OP_get_loc0 + idx);+ return;+ case OP_put_loc:+ dbuf_putc(bc_out, OP_put_loc0 + idx);+ return;+ case OP_set_loc:+ dbuf_putc(bc_out, OP_set_loc0 + idx);+ return;+ case OP_get_arg:+ dbuf_putc(bc_out, OP_get_arg0 + idx);+ return;+ case OP_put_arg:+ dbuf_putc(bc_out, OP_put_arg0 + idx);+ return;+ case OP_set_arg:+ dbuf_putc(bc_out, OP_set_arg0 + idx);+ return;+ case OP_get_var_ref:+ dbuf_putc(bc_out, OP_get_var_ref0 + idx);+ return;+ case OP_put_var_ref:+ dbuf_putc(bc_out, OP_put_var_ref0 + idx);+ return;+ case OP_set_var_ref:+ dbuf_putc(bc_out, OP_set_var_ref0 + idx);+ return;+ case OP_call:+ dbuf_putc(bc_out, OP_call0 + idx);+ return;+ }+ }+ if (idx < 256) {+ switch (op) {+ case OP_get_loc:+ dbuf_putc(bc_out, OP_get_loc8);+ dbuf_putc(bc_out, idx);+ return;+ case OP_put_loc:+ dbuf_putc(bc_out, OP_put_loc8);+ dbuf_putc(bc_out, idx);+ return;+ case OP_set_loc:+ dbuf_putc(bc_out, OP_set_loc8);+ dbuf_putc(bc_out, idx);+ return;+ }+ }+#endif+ dbuf_putc(bc_out, op);+ dbuf_put_u16(bc_out, idx);+}++/* peephole optimizations and resolve goto/labels */+static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)+{+ int pos, pos_next, bc_len, op, op1, len, i, line_num;+ const uint8_t *bc_buf;+ DynBuf bc_out;+ LabelSlot *label_slots, *ls;+ RelocEntry *re, *re_next;+ CodeContext cc;+ int label;+#if SHORT_OPCODES+ JumpSlot *jp;+#endif++ label_slots = s->label_slots;++ line_num = s->line_num;++ cc.bc_buf = bc_buf = s->byte_code.buf;+ cc.bc_len = bc_len = s->byte_code.size;+ js_dbuf_init(ctx, &bc_out);++#if SHORT_OPCODES+ if (s->jump_size) {+ s->jump_slots = js_mallocz(s->ctx, sizeof(*s->jump_slots) * s->jump_size);+ if (s->jump_slots == NULL)+ return -1;+ }+#endif+ /* XXX: Should skip this phase if not generating SHORT_OPCODES */+ if (s->line_number_size && !(s->js_mode & JS_MODE_STRIP)) {+ s->line_number_slots = js_mallocz(s->ctx, sizeof(*s->line_number_slots) * s->line_number_size);+ if (s->line_number_slots == NULL)+ return -1;+ s->line_number_last = s->line_num;+ s->line_number_last_pc = 0;+ }++ /* initialize the 'home_object' variable if needed */+ if (s->home_object_var_idx >= 0) {+ dbuf_putc(&bc_out, OP_special_object);+ dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_HOME_OBJECT);+ put_short_code(&bc_out, OP_put_loc, s->home_object_var_idx);+ }+ /* initialize the 'this.active_func' variable if needed */+ if (s->this_active_func_var_idx >= 0) {+ dbuf_putc(&bc_out, OP_special_object);+ dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC);+ put_short_code(&bc_out, OP_put_loc, s->this_active_func_var_idx);+ }+ /* initialize the 'new.target' variable if needed */+ if (s->new_target_var_idx >= 0) {+ dbuf_putc(&bc_out, OP_special_object);+ dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_NEW_TARGET);+ put_short_code(&bc_out, OP_put_loc, s->new_target_var_idx);+ }+ /* initialize the 'this' variable if needed. In a derived class+ constructor, this is initially uninitialized. */+ if (s->this_var_idx >= 0) {+ if (s->is_derived_class_constructor) {+ dbuf_putc(&bc_out, OP_set_loc_uninitialized);+ dbuf_put_u16(&bc_out, s->this_var_idx);+ } else {+ dbuf_putc(&bc_out, OP_push_this);+ put_short_code(&bc_out, OP_put_loc, s->this_var_idx);+ }+ }+ /* initialize the 'arguments' variable if needed */+ if (s->arguments_var_idx >= 0) {+ if ((s->js_mode & JS_MODE_STRICT) || !s->has_simple_parameter_list) {+ dbuf_putc(&bc_out, OP_special_object);+ dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_ARGUMENTS);+ } else {+ dbuf_putc(&bc_out, OP_special_object);+ dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS);+ }+ if (s->arguments_arg_idx >= 0)+ put_short_code(&bc_out, OP_set_loc, s->arguments_arg_idx);+ put_short_code(&bc_out, OP_put_loc, s->arguments_var_idx);+ }+ /* initialize a reference to the current function if needed */+ if (s->func_var_idx >= 0) {+ dbuf_putc(&bc_out, OP_special_object);+ dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC);+ put_short_code(&bc_out, OP_put_loc, s->func_var_idx);+ }+ /* initialize the variable environment object if needed */+ if (s->var_object_idx >= 0) {+ dbuf_putc(&bc_out, OP_special_object);+ dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT);+ put_short_code(&bc_out, OP_put_loc, s->var_object_idx);+ }+ if (s->arg_var_object_idx >= 0) {+ dbuf_putc(&bc_out, OP_special_object);+ dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT);+ put_short_code(&bc_out, OP_put_loc, s->arg_var_object_idx);+ }++ for (pos = 0; pos < bc_len; pos = pos_next) {+ int val;+ op = bc_buf[pos];+ len = opcode_info[op].size;+ pos_next = pos + len;+ switch(op) {+ case OP_line_num:+ /* line number info (for debug). We put it in a separate+ compressed table to reduce memory usage and get better+ performance */+ line_num = get_u32(bc_buf + pos + 1);+ break;++ case OP_label:+ {+ label = get_u32(bc_buf + pos + 1);+ assert(label >= 0 && label < s->label_count);+ ls = &label_slots[label];+ assert(ls->addr == -1);+ ls->addr = bc_out.size;+ /* resolve the relocation entries */+ for(re = ls->first_reloc; re != NULL; re = re_next) {+ int diff = ls->addr - re->addr;+ re_next = re->next;+ switch (re->size) {+ case 4:+ put_u32(bc_out.buf + re->addr, diff);+ break;+ case 2:+ assert(diff == (int16_t)diff);+ put_u16(bc_out.buf + re->addr, diff);+ break;+ case 1:+ assert(diff == (int8_t)diff);+ put_u8(bc_out.buf + re->addr, diff);+ break;+ }+ js_free(ctx, re);+ }+ ls->first_reloc = NULL;+ }+ break;++ case OP_call:+ case OP_call_method:+ {+ /* detect and transform tail calls */+ int argc;+ argc = get_u16(bc_buf + pos + 1);+ if (code_match(&cc, pos_next, OP_return, -1)) {+ if (cc.line_num >= 0) line_num = cc.line_num;+ add_pc2line_info(s, bc_out.size, line_num);+ put_short_code(&bc_out, op + 1, argc);+ pos_next = skip_dead_code(s, bc_buf, bc_len, cc.pos, &line_num);+ break;+ }+ add_pc2line_info(s, bc_out.size, line_num);+ put_short_code(&bc_out, op, argc);+ break;+ }+ goto no_change;++ case OP_return:+ case OP_return_undef:+ case OP_return_async:+ case OP_throw:+ case OP_throw_error:+ pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);+ goto no_change;++ case OP_goto:+ label = get_u32(bc_buf + pos + 1);+ has_goto:+ if (OPTIMIZE) {+ int line1 = -1;+ /* Use custom matcher because multiple labels can follow */+ label = find_jump_target(s, label, &op1, &line1);+ if (code_has_label(&cc, pos_next, label)) {+ /* jump to next instruction: remove jump */+ update_label(s, label, -1);+ break;+ }+ if (op1 == OP_return || op1 == OP_return_undef || op1 == OP_throw) {+ /* jump to return/throw: remove jump, append return/throw */+ /* updating the line number obfuscates assembly listing */+ //if (line1 >= 0) line_num = line1;+ update_label(s, label, -1);+ add_pc2line_info(s, bc_out.size, line_num);+ dbuf_putc(&bc_out, op1);+ pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);+ break;+ }+ /* XXX: should duplicate single instructions followed by goto or return */+ /* For example, can match one of these followed by return:+ push_i32 / push_const / push_atom_value / get_var /+ undefined / null / push_false / push_true / get_ref_value /+ get_loc / get_arg / get_var_ref+ */+ }+ goto has_label;++ case OP_gosub:+ label = get_u32(bc_buf + pos + 1);+ if (0 && OPTIMIZE) {+ label = find_jump_target(s, label, &op1, NULL);+ if (op1 == OP_ret) {+ update_label(s, label, -1);+ /* empty finally clause: remove gosub */+ break;+ }+ }+ goto has_label;++ case OP_catch:+ label = get_u32(bc_buf + pos + 1);+ goto has_label;++ case OP_if_true:+ case OP_if_false:+ label = get_u32(bc_buf + pos + 1);+ if (OPTIMIZE) {+ label = find_jump_target(s, label, &op1, NULL);+ /* transform if_false/if_true(l1) label(l1) -> drop label(l1) */+ if (code_has_label(&cc, pos_next, label)) {+ update_label(s, label, -1);+ dbuf_putc(&bc_out, OP_drop);+ break;+ }+ /* transform if_false(l1) goto(l2) label(l1) -> if_false(l2) label(l1) */+ if (code_match(&cc, pos_next, OP_goto, -1)) {+ int pos1 = cc.pos;+ int line1 = cc.line_num;+ if (code_has_label(&cc, pos1, label)) {+ if (line1 >= 0) line_num = line1;+ pos_next = pos1;+ update_label(s, label, -1);+ label = cc.label;+ op ^= OP_if_true ^ OP_if_false;+ }+ }+ }+ has_label:+ add_pc2line_info(s, bc_out.size, line_num);+ if (op == OP_goto) {+ pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);+ }+ assert(label >= 0 && label < s->label_count);+ ls = &label_slots[label];+#if SHORT_OPCODES+ jp = &s->jump_slots[s->jump_count++];+ jp->op = op;+ jp->size = 4;+ jp->pos = bc_out.size + 1;+ jp->label = label;++ if (ls->addr == -1) {+ int diff = ls->pos2 - pos - 1;+ if (diff < 128 && (op == OP_if_false || op == OP_if_true || op == OP_goto)) {+ jp->size = 1;+ jp->op = OP_if_false8 + (op - OP_if_false);+ dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false));+ dbuf_putc(&bc_out, 0);+ if (!add_reloc(ctx, ls, bc_out.size - 1, 1))+ goto fail;+ break;+ }+ if (diff < 32768 && op == OP_goto) {+ jp->size = 2;+ jp->op = OP_goto16;+ dbuf_putc(&bc_out, OP_goto16);+ dbuf_put_u16(&bc_out, 0);+ if (!add_reloc(ctx, ls, bc_out.size - 2, 2))+ goto fail;+ break;+ }+ } else {+ int diff = ls->addr - bc_out.size - 1;+ if (diff == (int8_t)diff && (op == OP_if_false || op == OP_if_true || op == OP_goto)) {+ jp->size = 1;+ jp->op = OP_if_false8 + (op - OP_if_false);+ dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false));+ dbuf_putc(&bc_out, diff);+ break;+ }+ if (diff == (int16_t)diff && op == OP_goto) {+ jp->size = 2;+ jp->op = OP_goto16;+ dbuf_putc(&bc_out, OP_goto16);+ dbuf_put_u16(&bc_out, diff);+ break;+ }+ }+#endif+ dbuf_putc(&bc_out, op);+ dbuf_put_u32(&bc_out, ls->addr - bc_out.size);+ if (ls->addr == -1) {+ /* unresolved yet: create a new relocation entry */+ if (!add_reloc(ctx, ls, bc_out.size - 4, 4))+ goto fail;+ }+ break;+ case OP_with_get_var:+ case OP_with_put_var:+ case OP_with_delete_var:+ case OP_with_make_ref:+ case OP_with_get_ref:+ case OP_with_get_ref_undef:+ {+ JSAtom atom;+ int is_with;++ atom = get_u32(bc_buf + pos + 1);+ label = get_u32(bc_buf + pos + 5);+ is_with = bc_buf[pos + 9];+ if (OPTIMIZE) {+ label = find_jump_target(s, label, &op1, NULL);+ }+ assert(label >= 0 && label < s->label_count);+ ls = &label_slots[label];+ add_pc2line_info(s, bc_out.size, line_num);+#if SHORT_OPCODES+ jp = &s->jump_slots[s->jump_count++];+ jp->op = op;+ jp->size = 4;+ jp->pos = bc_out.size + 5;+ jp->label = label;+#endif+ dbuf_putc(&bc_out, op);+ dbuf_put_u32(&bc_out, atom);+ dbuf_put_u32(&bc_out, ls->addr - bc_out.size);+ if (ls->addr == -1) {+ /* unresolved yet: create a new relocation entry */+ if (!add_reloc(ctx, ls, bc_out.size - 4, 4))+ goto fail;+ }+ dbuf_putc(&bc_out, is_with);+ }+ break;++ case OP_drop:+ if (OPTIMIZE) {+ /* remove useless drops before return */+ if (code_match(&cc, pos_next, OP_return_undef, -1)) {+ if (cc.line_num >= 0) line_num = cc.line_num;+ break;+ }+ }+ goto no_change;++ case OP_null:+#if SHORT_OPCODES+ if (OPTIMIZE) {+ /* transform null strict_eq into is_null */+ if (code_match(&cc, pos_next, OP_strict_eq, -1)) {+ if (cc.line_num >= 0) line_num = cc.line_num;+ add_pc2line_info(s, bc_out.size, line_num);+ dbuf_putc(&bc_out, OP_is_null);+ pos_next = cc.pos;+ break;+ }+ /* transform null strict_neq if_false/if_true -> is_null if_true/if_false */+ if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) {+ if (cc.line_num >= 0) line_num = cc.line_num;+ add_pc2line_info(s, bc_out.size, line_num);+ dbuf_putc(&bc_out, OP_is_null);+ pos_next = cc.pos;+ label = cc.label;+ op = cc.op ^ OP_if_false ^ OP_if_true;+ goto has_label;+ }+ }+#endif+ /* fall thru */+ case OP_push_false:+ case OP_push_true:+ if (OPTIMIZE) {+ val = (op == OP_push_true);+ if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {+ has_constant_test:+ if (cc.line_num >= 0) line_num = cc.line_num;+ if (val == cc.op - OP_if_false) {+ /* transform null if_false(l1) -> goto l1 */+ /* transform false if_false(l1) -> goto l1 */+ /* transform true if_true(l1) -> goto l1 */+ pos_next = cc.pos;+ op = OP_goto;+ label = cc.label;+ goto has_goto;+ } else {+ /* transform null if_true(l1) -> nop */+ /* transform false if_true(l1) -> nop */+ /* transform true if_false(l1) -> nop */+ pos_next = cc.pos;+ update_label(s, cc.label, -1);+ break;+ }+ }+ }+ goto no_change;++ case OP_push_i32:+ if (OPTIMIZE) {+ /* transform i32(val) neg -> i32(-val) */+ val = get_i32(bc_buf + pos + 1);+ if ((val != INT32_MIN && val != 0)+ && code_match(&cc, pos_next, OP_neg, -1)) {+ if (cc.line_num >= 0) line_num = cc.line_num;+ if (code_match(&cc, cc.pos, OP_drop, -1)) {+ if (cc.line_num >= 0) line_num = cc.line_num;+ } else {+ add_pc2line_info(s, bc_out.size, line_num);+ push_short_int(&bc_out, -val);+ }+ pos_next = cc.pos;+ break;+ }+ /* remove push/drop pairs generated by the parser */+ if (code_match(&cc, pos_next, OP_drop, -1)) {+ if (cc.line_num >= 0) line_num = cc.line_num;+ pos_next = cc.pos;+ break;+ }+ /* Optimize constant tests: `if (0)`, `if (1)`, `if (!0)`... */+ if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {+ val = (val != 0);+ goto has_constant_test;+ }+ add_pc2line_info(s, bc_out.size, line_num);+ push_short_int(&bc_out, val);+ break;+ }+ goto no_change;++#if SHORT_OPCODES+ case OP_push_const:+ case OP_fclosure:+ if (OPTIMIZE) {+ int idx = get_u32(bc_buf + pos + 1);+ if (idx < 256) {+ add_pc2line_info(s, bc_out.size, line_num);+ dbuf_putc(&bc_out, OP_push_const8 + op - OP_push_const);+ dbuf_putc(&bc_out, idx);+ break;+ }+ }+ goto no_change;++ case OP_get_field:+ if (OPTIMIZE) {+ JSAtom atom = get_u32(bc_buf + pos + 1);+ if (atom == JS_ATOM_length) {+ JS_FreeAtom(ctx, atom);+ add_pc2line_info(s, bc_out.size, line_num);+ dbuf_putc(&bc_out, OP_get_length);+ break;+ }+ }+ goto no_change;+#endif+ case OP_push_atom_value:+ if (OPTIMIZE) {+ JSAtom atom = get_u32(bc_buf + pos + 1);+ /* remove push/drop pairs generated by the parser */+ if (code_match(&cc, pos_next, OP_drop, -1)) {+ JS_FreeAtom(ctx, atom);+ if (cc.line_num >= 0) line_num = cc.line_num;+ pos_next = cc.pos;+ break;+ }+#if SHORT_OPCODES+ if (atom == JS_ATOM_empty_string) {+ JS_FreeAtom(ctx, atom);+ add_pc2line_info(s, bc_out.size, line_num);+ dbuf_putc(&bc_out, OP_push_empty_string);+ break;+ }+#endif+ }+ goto no_change;++ case OP_to_propkey:+ case OP_to_propkey2:+ if (OPTIMIZE) {+ /* remove redundant to_propkey/to_propkey2 opcodes when storing simple data */+ if (code_match(&cc, pos_next, M3(OP_get_loc, OP_get_arg, OP_get_var_ref), -1, OP_put_array_el, -1)+ || code_match(&cc, pos_next, M3(OP_push_i32, OP_push_const, OP_push_atom_value), OP_put_array_el, -1)+ || code_match(&cc, pos_next, M4(OP_undefined, OP_null, OP_push_true, OP_push_false), OP_put_array_el, -1)) {+ break;+ }+ }+ goto no_change;++ case OP_undefined:+ if (OPTIMIZE) {+ /* remove push/drop pairs generated by the parser */+ if (code_match(&cc, pos_next, OP_drop, -1)) {+ if (cc.line_num >= 0) line_num = cc.line_num;+ pos_next = cc.pos;+ break;+ }+ /* transform undefined return -> return_undefined */+ if (code_match(&cc, pos_next, OP_return, -1)) {+ if (cc.line_num >= 0) line_num = cc.line_num;+ add_pc2line_info(s, bc_out.size, line_num);+ dbuf_putc(&bc_out, OP_return_undef);+ pos_next = cc.pos;+ break;+ }+ /* transform undefined if_true(l1)/if_false(l1) -> nop/goto(l1) */+ if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {+ val = 0;+ goto has_constant_test;+ }+#if SHORT_OPCODES+ /* transform undefined strict_eq -> is_undefined */+ if (code_match(&cc, pos_next, OP_strict_eq, -1)) {+ if (cc.line_num >= 0) line_num = cc.line_num;+ add_pc2line_info(s, bc_out.size, line_num);+ dbuf_putc(&bc_out, OP_is_undefined);+ pos_next = cc.pos;+ break;+ }+ /* transform undefined strict_neq if_false/if_true -> is_undefined if_true/if_false */+ if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) {+ if (cc.line_num >= 0) line_num = cc.line_num;+ add_pc2line_info(s, bc_out.size, line_num);+ dbuf_putc(&bc_out, OP_is_undefined);+ pos_next = cc.pos;+ label = cc.label;+ op = cc.op ^ OP_if_false ^ OP_if_true;+ goto has_label;+ }+#endif+ }+ goto no_change;++ case OP_insert2:+ if (OPTIMIZE) {+ /* Transformation:+ insert2 put_field(a) drop -> put_field(a)+ insert2 put_var_strict(a) drop -> put_var_strict(a)+ */+ if (code_match(&cc, pos_next, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) {+ if (cc.line_num >= 0) line_num = cc.line_num;+ add_pc2line_info(s, bc_out.size, line_num);+ dbuf_putc(&bc_out, cc.op);+ dbuf_put_u32(&bc_out, cc.atom);+ pos_next = cc.pos;+ break;+ }+ }+ goto no_change;++ case OP_dup:+ if (OPTIMIZE) {+ /* Transformation: dup put_x(n) drop -> put_x(n) */+ int op1, line2 = -1;+ /* Transformation: dup put_x(n) -> set_x(n) */+ if (code_match(&cc, pos_next, M3(OP_put_loc, OP_put_arg, OP_put_var_ref), -1, -1)) {+ if (cc.line_num >= 0) line_num = cc.line_num;+ op1 = cc.op + 1; /* put_x -> set_x */+ pos_next = cc.pos;+ if (code_match(&cc, cc.pos, OP_drop, -1)) {+ if (cc.line_num >= 0) line_num = cc.line_num;+ op1 -= 1; /* set_x drop -> put_x */+ pos_next = cc.pos;+ if (code_match(&cc, cc.pos, op1 - 1, cc.idx, -1)) {+ line2 = cc.line_num; /* delay line number update */+ op1 += 1; /* put_x(n) get_x(n) -> set_x(n) */+ pos_next = cc.pos;+ }+ }+ add_pc2line_info(s, bc_out.size, line_num);+ put_short_code(&bc_out, op1, cc.idx);+ if (line2 >= 0) line_num = line2;+ break;+ }+ }+ goto no_change;++ case OP_get_loc:+ if (OPTIMIZE) {+ /* transformation:+ get_loc(n) post_dec put_loc(n) drop -> dec_loc(n)+ get_loc(n) post_inc put_loc(n) drop -> inc_loc(n)+ get_loc(n) dec dup put_loc(n) drop -> dec_loc(n)+ get_loc(n) inc dup put_loc(n) drop -> inc_loc(n)+ */+ int idx;+ idx = get_u16(bc_buf + pos + 1);+ if (idx >= 256)+ goto no_change;+ if (code_match(&cc, pos_next, M2(OP_post_dec, OP_post_inc), OP_put_loc, idx, OP_drop, -1) ||+ code_match(&cc, pos_next, M2(OP_dec, OP_inc), OP_dup, OP_put_loc, idx, OP_drop, -1)) {+ if (cc.line_num >= 0) line_num = cc.line_num;+ add_pc2line_info(s, bc_out.size, line_num);+ dbuf_putc(&bc_out, (cc.op == OP_inc || cc.op == OP_post_inc) ? OP_inc_loc : OP_dec_loc);+ dbuf_putc(&bc_out, idx);+ pos_next = cc.pos;+ break;+ }+ /* transformation:+ get_loc(n) push_atom_value(x) add dup put_loc(n) drop -> push_atom_value(x) add_loc(n)+ */+ if (code_match(&cc, pos_next, OP_push_atom_value, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) {+ if (cc.line_num >= 0) line_num = cc.line_num;+ add_pc2line_info(s, bc_out.size, line_num);+#if SHORT_OPCODES+ if (cc.atom == JS_ATOM_empty_string) {+ JS_FreeAtom(ctx, cc.atom);+ dbuf_putc(&bc_out, OP_push_empty_string);+ } else+#endif+ {+ dbuf_putc(&bc_out, OP_push_atom_value);+ dbuf_put_u32(&bc_out, cc.atom);+ }+ dbuf_putc(&bc_out, OP_add_loc);+ dbuf_putc(&bc_out, idx);+ pos_next = cc.pos;+ break;+ }+ /* transformation:+ get_loc(n) push_i32(x) add dup put_loc(n) drop -> push_i32(x) add_loc(n)+ */+ if (code_match(&cc, pos_next, OP_push_i32, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) {+ if (cc.line_num >= 0) line_num = cc.line_num;+ add_pc2line_info(s, bc_out.size, line_num);+ push_short_int(&bc_out, cc.label);+ dbuf_putc(&bc_out, OP_add_loc);+ dbuf_putc(&bc_out, idx);+ pos_next = cc.pos;+ break;+ }+ /* transformation: XXX: also do these:+ get_loc(n) get_loc(x) add dup put_loc(n) drop -> get_loc(x) add_loc(n)+ get_loc(n) get_arg(x) add dup put_loc(n) drop -> get_arg(x) add_loc(n)+ get_loc(n) get_var_ref(x) add dup put_loc(n) drop -> get_var_ref(x) add_loc(n)+ */+ 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)) {+ if (cc.line_num >= 0) line_num = cc.line_num;+ add_pc2line_info(s, bc_out.size, line_num);+ put_short_code(&bc_out, cc.op, cc.idx);+ dbuf_putc(&bc_out, OP_add_loc);+ dbuf_putc(&bc_out, idx);+ pos_next = cc.pos;+ break;+ }+ add_pc2line_info(s, bc_out.size, line_num);+ put_short_code(&bc_out, op, idx);+ break;+ }+ goto no_change;+#if SHORT_OPCODES+ case OP_get_arg:+ case OP_get_var_ref:+ if (OPTIMIZE) {+ int idx;+ idx = get_u16(bc_buf + pos + 1);+ add_pc2line_info(s, bc_out.size, line_num);+ put_short_code(&bc_out, op, idx);+ break;+ }+ goto no_change;+#endif+ case OP_put_loc:+ case OP_put_arg:+ case OP_put_var_ref:+ if (OPTIMIZE) {+ /* transformation: put_x(n) get_x(n) -> set_x(n) */+ int idx;+ idx = get_u16(bc_buf + pos + 1);+ if (code_match(&cc, pos_next, op - 1, idx, -1)) {+ if (cc.line_num >= 0) line_num = cc.line_num;+ add_pc2line_info(s, bc_out.size, line_num);+ put_short_code(&bc_out, op + 1, idx);+ pos_next = cc.pos;+ break;+ }+ add_pc2line_info(s, bc_out.size, line_num);+ put_short_code(&bc_out, op, idx);+ break;+ }+ goto no_change;++ case OP_post_inc:+ case OP_post_dec:+ if (OPTIMIZE) {+ /* transformation:+ post_inc put_x drop -> inc put_x+ post_inc perm3 put_field drop -> inc put_field+ post_inc perm3 put_var_strict drop -> inc put_var_strict+ post_inc perm4 put_array_el drop -> inc put_array_el+ */+ int op1, idx;+ if (code_match(&cc, pos_next, M3(OP_put_loc, OP_put_arg, OP_put_var_ref), -1, OP_drop, -1)) {+ if (cc.line_num >= 0) line_num = cc.line_num;+ op1 = cc.op;+ idx = cc.idx;+ pos_next = cc.pos;+ if (code_match(&cc, cc.pos, op1 - 1, idx, -1)) {+ if (cc.line_num >= 0) line_num = cc.line_num;+ op1 += 1; /* put_x(n) get_x(n) -> set_x(n) */+ pos_next = cc.pos;+ }+ add_pc2line_info(s, bc_out.size, line_num);+ dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));+ put_short_code(&bc_out, op1, idx);+ break;+ }+ if (code_match(&cc, pos_next, OP_perm3, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) {+ if (cc.line_num >= 0) line_num = cc.line_num;+ add_pc2line_info(s, bc_out.size, line_num);+ dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));+ dbuf_putc(&bc_out, cc.op);+ dbuf_put_u32(&bc_out, cc.atom);+ pos_next = cc.pos;+ break;+ }+ if (code_match(&cc, pos_next, OP_perm4, OP_put_array_el, OP_drop, -1)) {+ if (cc.line_num >= 0) line_num = cc.line_num;+ add_pc2line_info(s, bc_out.size, line_num);+ dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));+ dbuf_putc(&bc_out, OP_put_array_el);+ pos_next = cc.pos;+ break;+ }+ }+ goto no_change;++#if SHORT_OPCODES+ case OP_typeof:+ if (OPTIMIZE) {+ /* simplify typeof tests */+ if (code_match(&cc, pos_next, OP_push_atom_value, M4(OP_strict_eq, OP_strict_neq, OP_eq, OP_neq), -1)) {+ if (cc.line_num >= 0) line_num = cc.line_num;+ int op1 = (cc.op == OP_strict_eq || cc.op == OP_eq) ? OP_strict_eq : OP_strict_neq;+ int op2 = -1;+ switch (cc.atom) {+ case JS_ATOM_undefined:+ op2 = OP_typeof_is_undefined;+ break;+ case JS_ATOM_function:+ op2 = OP_typeof_is_function;+ break;+ }+ if (op2 >= 0) {+ /* transform typeof(s) == "<type>" into is_<type> */+ if (op1 == OP_strict_eq) {+ add_pc2line_info(s, bc_out.size, line_num);+ dbuf_putc(&bc_out, op2);+ JS_FreeAtom(ctx, cc.atom);+ pos_next = cc.pos;+ break;+ }+ if (op1 == OP_strict_neq && code_match(&cc, cc.pos, OP_if_false, -1)) {+ /* transform typeof(s) != "<type>" if_false into is_<type> if_true */+ if (cc.line_num >= 0) line_num = cc.line_num;+ add_pc2line_info(s, bc_out.size, line_num);+ dbuf_putc(&bc_out, op2);+ JS_FreeAtom(ctx, cc.atom);+ pos_next = cc.pos;+ label = cc.label;+ op = OP_if_true;+ goto has_label;+ }+ }+ }+ }+ goto no_change;+#endif++ default:+ no_change:+ add_pc2line_info(s, bc_out.size, line_num);+ dbuf_put(&bc_out, bc_buf + pos, len);+ break;+ }+ }++ /* check that there were no missing labels */+ for(i = 0; i < s->label_count; i++) {+ assert(label_slots[i].first_reloc == NULL);+ }+#if SHORT_OPCODES+ if (OPTIMIZE) {+ /* more jump optimizations */+ int patch_offsets = 0;+ for (i = 0, jp = s->jump_slots; i < s->jump_count; i++, jp++) {+ LabelSlot *ls;+ JumpSlot *jp1;+ int j, pos, diff, delta;++ delta = 3;+ switch (op = jp->op) {+ case OP_goto16:+ delta = 1;+ /* fall thru */+ case OP_if_false:+ case OP_if_true:+ case OP_goto:+ pos = jp->pos;+ diff = s->label_slots[jp->label].addr - pos;+ if (diff >= -128 && diff <= 127 + delta) {+ //put_u8(bc_out.buf + pos, diff);+ jp->size = 1;+ if (op == OP_goto16) {+ bc_out.buf[pos - 1] = jp->op = OP_goto8;+ } else {+ bc_out.buf[pos - 1] = jp->op = OP_if_false8 + (op - OP_if_false);+ }+ goto shrink;+ } else+ if (diff == (int16_t)diff && op == OP_goto) {+ //put_u16(bc_out.buf + pos, diff);+ jp->size = 2;+ delta = 2;+ bc_out.buf[pos - 1] = jp->op = OP_goto16;+ shrink:+ /* XXX: should reduce complexity, using 2 finger copy scheme */+ memmove(bc_out.buf + pos + jp->size, bc_out.buf + pos + jp->size + delta,+ bc_out.size - pos - jp->size - delta);+ bc_out.size -= delta;+ patch_offsets++;+ for (j = 0, ls = s->label_slots; j < s->label_count; j++, ls++) {+ if (ls->addr > pos)+ ls->addr -= delta;+ }+ for (j = i + 1, jp1 = jp + 1; j < s->jump_count; j++, jp1++) {+ if (jp1->pos > pos)+ jp1->pos -= delta;+ }+ for (j = 0; j < s->line_number_count; j++) {+ if (s->line_number_slots[j].pc > pos)+ s->line_number_slots[j].pc -= delta;+ }+ continue;+ }+ break;+ }+ }+ if (patch_offsets) {+ JumpSlot *jp1;+ int j;+ for (j = 0, jp1 = s->jump_slots; j < s->jump_count; j++, jp1++) {+ int diff1 = s->label_slots[jp1->label].addr - jp1->pos;+ switch (jp1->size) {+ case 1:+ put_u8(bc_out.buf + jp1->pos, diff1);+ break;+ case 2:+ put_u16(bc_out.buf + jp1->pos, diff1);+ break;+ case 4:+ put_u32(bc_out.buf + jp1->pos, diff1);+ break;+ }+ }+ }+ }+ js_free(ctx, s->jump_slots);+ s->jump_slots = NULL;+#endif+ js_free(ctx, s->label_slots);+ s->label_slots = NULL;+ /* XXX: should delay until copying to runtime bytecode function */+ compute_pc2line_info(s);+ js_free(ctx, s->line_number_slots);+ s->line_number_slots = NULL;+ /* set the new byte code */+ dbuf_free(&s->byte_code);+ s->byte_code = bc_out;+ s->use_short_opcodes = TRUE;+ if (dbuf_error(&s->byte_code)) {+ JS_ThrowOutOfMemory(ctx);+ return -1;+ }+ return 0;+ fail:+ /* XXX: not safe */+ dbuf_free(&bc_out);+ return -1;+}++/* compute the maximum stack size needed by the function */++typedef struct StackSizeState {+ int bc_len;+ int stack_len_max;+ uint16_t *stack_level_tab;+ int *pc_stack;+ int pc_stack_len;+ int pc_stack_size;+} StackSizeState;++/* 'op' is only used for error indication */+static __exception int ss_check(JSContext *ctx, StackSizeState *s,+ int pos, int op, int stack_len)+{+ if ((unsigned)pos >= s->bc_len) {+ JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);+ return -1;+ }+ if (stack_len > s->stack_len_max) {+ s->stack_len_max = stack_len;+ if (s->stack_len_max > JS_STACK_SIZE_MAX) {+ JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos);+ return -1;+ }+ }+ if (s->stack_level_tab[pos] != 0xffff) {+ /* already explored: check that the stack size is consistent */+ if (s->stack_level_tab[pos] != stack_len) {+ JS_ThrowInternalError(ctx, "unconsistent stack size: %d %d (pc=%d)",+ s->stack_level_tab[pos], stack_len, pos);+ return -1;+ } else {+ return 0;+ }+ }++ /* mark as explored and store the stack size */+ s->stack_level_tab[pos] = stack_len;++ /* queue the new PC to explore */+ if (js_resize_array(ctx, (void **)&s->pc_stack, sizeof(s->pc_stack[0]),+ &s->pc_stack_size, s->pc_stack_len + 1))+ return -1;+ s->pc_stack[s->pc_stack_len++] = pos;+ return 0;+}++static __exception int compute_stack_size(JSContext *ctx,+ JSFunctionDef *fd,+ int *pstack_size)+{+ StackSizeState s_s, *s = &s_s;+ int i, diff, n_pop, pos_next, stack_len, pos, op;+ const JSOpCode *oi;+ const uint8_t *bc_buf;++ bc_buf = fd->byte_code.buf;+ s->bc_len = fd->byte_code.size;+ /* bc_len > 0 */+ s->stack_level_tab = js_malloc(ctx, sizeof(s->stack_level_tab[0]) *+ s->bc_len);+ if (!s->stack_level_tab)+ return -1;+ for(i = 0; i < s->bc_len; i++)+ s->stack_level_tab[i] = 0xffff;+ s->stack_len_max = 0;+ s->pc_stack = NULL;+ s->pc_stack_len = 0;+ s->pc_stack_size = 0;++ /* breadth-first graph exploration */+ if (ss_check(ctx, s, 0, OP_invalid, 0))+ goto fail;++ while (s->pc_stack_len > 0) {+ pos = s->pc_stack[--s->pc_stack_len];+ stack_len = s->stack_level_tab[pos];+ op = bc_buf[pos];+ if (op == 0 || op >= OP_COUNT) {+ JS_ThrowInternalError(ctx, "invalid opcode (op=%d, pc=%d)", op, pos);+ goto fail;+ }+ oi = &short_opcode_info(op);+ pos_next = pos + oi->size;+ if (pos_next > s->bc_len) {+ JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);+ goto fail;+ }+ n_pop = oi->n_pop;+ /* call pops a variable number of arguments */+ if (oi->fmt == OP_FMT_npop || oi->fmt == OP_FMT_npop_u16) {+ n_pop += get_u16(bc_buf + pos + 1);+ } else {+#if SHORT_OPCODES+ if (oi->fmt == OP_FMT_npopx) {+ n_pop += op - OP_call0;+ }+#endif+ }++ if (stack_len < n_pop) {+ JS_ThrowInternalError(ctx, "stack underflow (op=%d, pc=%d)", op, pos);+ goto fail;+ }+ stack_len += oi->n_push - n_pop;+ if (stack_len > s->stack_len_max) {+ s->stack_len_max = stack_len;+ if (s->stack_len_max > JS_STACK_SIZE_MAX) {+ JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos);+ goto fail;+ }+ }+ switch(op) {+ case OP_tail_call:+ case OP_tail_call_method:+ case OP_return:+ case OP_return_undef:+ case OP_return_async:+ case OP_throw:+ case OP_throw_error:+ case OP_ret:+ goto done_insn;+ case OP_goto:+ diff = get_u32(bc_buf + pos + 1);+ pos_next = pos + 1 + diff;+ break;+#if SHORT_OPCODES+ case OP_goto16:+ diff = (int16_t)get_u16(bc_buf + pos + 1);+ pos_next = pos + 1 + diff;+ break;+ case OP_goto8:+ diff = (int8_t)bc_buf[pos + 1];+ pos_next = pos + 1 + diff;+ break;+ case OP_if_true8:+ case OP_if_false8:+ diff = (int8_t)bc_buf[pos + 1];+ if (ss_check(ctx, s, pos + 1 + diff, op, stack_len))+ goto fail;+ break;+#endif+ case OP_if_true:+ case OP_if_false:+ case OP_catch:+ diff = get_u32(bc_buf + pos + 1);+ if (ss_check(ctx, s, pos + 1 + diff, op, stack_len))+ goto fail;+ break;+ case OP_gosub:+ diff = get_u32(bc_buf + pos + 1);+ if (ss_check(ctx, s, pos + 1 + diff, op, stack_len + 1))+ goto fail;+ break;+ case OP_with_get_var:+ case OP_with_delete_var:+ diff = get_u32(bc_buf + pos + 5);+ if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 1))+ goto fail;+ break;+ case OP_with_make_ref:+ case OP_with_get_ref:+ case OP_with_get_ref_undef:+ diff = get_u32(bc_buf + pos + 5);+ if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 2))+ goto fail;+ break;+ case OP_with_put_var:+ diff = get_u32(bc_buf + pos + 5);+ if (ss_check(ctx, s, pos + 5 + diff, op, stack_len - 1))+ goto fail;+ break;++ default:+ break;+ }+ if (ss_check(ctx, s, pos_next, op, stack_len))+ goto fail;+ done_insn: ;+ }+ js_free(ctx, s->stack_level_tab);+ js_free(ctx, s->pc_stack);+ *pstack_size = s->stack_len_max;+ return 0;+ fail:+ js_free(ctx, s->stack_level_tab);+ js_free(ctx, s->pc_stack);+ *pstack_size = 0;+ return -1;+}++static int add_module_variables(JSContext *ctx, JSFunctionDef *fd)+{+ int i, idx;+ JSModuleDef *m = fd->module;+ JSExportEntry *me;+ JSGlobalVar *hf;++ /* The imported global variables were added as closure variables+ in js_parse_import(). We add here the module global+ variables. */++ for(i = 0; i < fd->global_var_count; i++) {+ hf = &fd->global_vars[i];+ if (add_closure_var(ctx, fd, TRUE, FALSE, i, hf->var_name, hf->is_const,+ hf->is_lexical, FALSE) < 0)+ return -1;+ }++ /* resolve the variable names of the local exports */+ for(i = 0; i < m->export_entries_count; i++) {+ me = &m->export_entries[i];+ if (me->export_type == JS_EXPORT_TYPE_LOCAL) {+ idx = find_closure_var(ctx, fd, me->local_name);+ if (idx < 0) {+ JS_ThrowSyntaxErrorAtom(ctx, "exported variable '%s' does not exist",+ me->local_name);+ return -1;+ }+ me->u.local.var_idx = idx;+ }+ }+ return 0;+}++/* create a function object from a function definition. The function+ definition is freed. All the child functions are also created. It+ must be done this way to resolve all the variables. */+static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)+{+ JSValue func_obj;+ JSFunctionBytecode *b;+ struct list_head *el, *el1;+ int stack_size, scope, idx;+ int function_size, byte_code_offset, cpool_offset;+ int closure_var_offset, vardefs_offset;++ /* recompute scope linkage */+ for (scope = 0; scope < fd->scope_count; scope++) {+ fd->scopes[scope].first = -1;+ }+ if (fd->has_parameter_expressions) {+ /* special end of variable list marker for the argument scope */+ fd->scopes[ARG_SCOPE_INDEX].first = ARG_SCOPE_END;+ }+ for (idx = 0; idx < fd->var_count; idx++) {+ JSVarDef *vd = &fd->vars[idx];+ vd->scope_next = fd->scopes[vd->scope_level].first;+ fd->scopes[vd->scope_level].first = idx;+ }+ for (scope = 2; scope < fd->scope_count; scope++) {+ JSVarScope *sd = &fd->scopes[scope];+ if (sd->first < 0)+ sd->first = fd->scopes[sd->parent].first;+ }+ for (idx = 0; idx < fd->var_count; idx++) {+ JSVarDef *vd = &fd->vars[idx];+ if (vd->scope_next < 0 && vd->scope_level > 1) {+ scope = fd->scopes[vd->scope_level].parent;+ vd->scope_next = fd->scopes[scope].first;+ }+ }++ /* if the function contains an eval call, the closure variables+ are used to compile the eval and they must be ordered by scope,+ so it is necessary to create the closure variables before any+ other variable lookup is done. */+ if (fd->has_eval_call)+ add_eval_variables(ctx, fd);++ /* add the module global variables in the closure */+ if (fd->module) {+ if (add_module_variables(ctx, fd))+ goto fail;+ }++ /* first create all the child functions */+ list_for_each_safe(el, el1, &fd->child_list) {+ JSFunctionDef *fd1;+ int cpool_idx;++ fd1 = list_entry(el, JSFunctionDef, link);+ cpool_idx = fd1->parent_cpool_idx;+ func_obj = js_create_function(ctx, fd1);+ if (JS_IsException(func_obj))+ goto fail;+ /* save it in the constant pool */+ assert(cpool_idx >= 0);+ fd->cpool[cpool_idx] = func_obj;+ }++#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 4)+ if (!(fd->js_mode & JS_MODE_STRIP)) {+ printf("pass 1\n");+ dump_byte_code(ctx, 1, fd->byte_code.buf, fd->byte_code.size,+ fd->args, fd->arg_count, fd->vars, fd->var_count,+ fd->closure_var, fd->closure_var_count,+ fd->cpool, fd->cpool_count, fd->source, fd->line_num,+ fd->label_slots, NULL);+ printf("\n");+ }+#endif++ if (resolve_variables(ctx, fd))+ goto fail;++#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 2)+ if (!(fd->js_mode & JS_MODE_STRIP)) {+ printf("pass 2\n");+ dump_byte_code(ctx, 2, fd->byte_code.buf, fd->byte_code.size,+ fd->args, fd->arg_count, fd->vars, fd->var_count,+ fd->closure_var, fd->closure_var_count,+ fd->cpool, fd->cpool_count, fd->source, fd->line_num,+ fd->label_slots, NULL);+ printf("\n");+ }+#endif++ if (resolve_labels(ctx, fd))+ goto fail;++ if (compute_stack_size(ctx, fd, &stack_size) < 0)+ goto fail;++ if (fd->js_mode & JS_MODE_STRIP) {+ function_size = offsetof(JSFunctionBytecode, debug);+ } else {+ function_size = sizeof(*b);+ }+ cpool_offset = function_size;+ function_size += fd->cpool_count * sizeof(*fd->cpool);+ vardefs_offset = function_size;+ if (!(fd->js_mode & JS_MODE_STRIP) || fd->has_eval_call) {+ function_size += (fd->arg_count + fd->var_count) * sizeof(*b->vardefs);+ }+ closure_var_offset = function_size;+ function_size += fd->closure_var_count * sizeof(*fd->closure_var);+ byte_code_offset = function_size;+ function_size += fd->byte_code.size;++ b = js_mallocz(ctx, function_size);+ if (!b)+ goto fail;+ b->header.ref_count = 1;++ b->byte_code_buf = (void *)((uint8_t*)b + byte_code_offset);+ b->byte_code_len = fd->byte_code.size;+ memcpy(b->byte_code_buf, fd->byte_code.buf, fd->byte_code.size);+ js_free(ctx, fd->byte_code.buf);+ fd->byte_code.buf = NULL;++ b->func_name = fd->func_name;+ if (fd->arg_count + fd->var_count > 0) {+ if ((fd->js_mode & JS_MODE_STRIP) && !fd->has_eval_call) {+ /* Strip variable definitions not needed at runtime */+ int i;+ for(i = 0; i < fd->var_count; i++) {+ JS_FreeAtom(ctx, fd->vars[i].var_name);+ }+ for(i = 0; i < fd->arg_count; i++) {+ JS_FreeAtom(ctx, fd->args[i].var_name);+ }+ for(i = 0; i < fd->closure_var_count; i++) {+ JS_FreeAtom(ctx, fd->closure_var[i].var_name);+ fd->closure_var[i].var_name = JS_ATOM_NULL;+ }+ } else {+ b->vardefs = (void *)((uint8_t*)b + vardefs_offset);+ memcpy(b->vardefs, fd->args, fd->arg_count * sizeof(fd->args[0]));+ memcpy(b->vardefs + fd->arg_count, fd->vars, fd->var_count * sizeof(fd->vars[0]));+ }+ b->var_count = fd->var_count;+ b->arg_count = fd->arg_count;+ b->defined_arg_count = fd->defined_arg_count;+ js_free(ctx, fd->args);+ js_free(ctx, fd->vars);+ }+ b->cpool_count = fd->cpool_count;+ if (b->cpool_count) {+ b->cpool = (void *)((uint8_t*)b + cpool_offset);+ memcpy(b->cpool, fd->cpool, b->cpool_count * sizeof(*b->cpool));+ }+ js_free(ctx, fd->cpool);+ fd->cpool = NULL;++ b->stack_size = stack_size;++ if (fd->js_mode & JS_MODE_STRIP) {+ JS_FreeAtom(ctx, fd->filename);+ dbuf_free(&fd->pc2line); // probably useless+ } else {+ /* XXX: source and pc2line info should be packed at the end of the+ JSFunctionBytecode structure, avoiding allocation overhead+ */+ b->has_debug = 1;+ b->debug.filename = fd->filename;+ b->debug.line_num = fd->line_num;++ //DynBuf pc2line;+ //compute_pc2line_info(fd, &pc2line);+ //js_free(ctx, fd->line_number_slots)+ b->debug.pc2line_buf = js_realloc(ctx, fd->pc2line.buf, fd->pc2line.size);+ if (!b->debug.pc2line_buf)+ b->debug.pc2line_buf = fd->pc2line.buf;+ b->debug.pc2line_len = fd->pc2line.size;+ b->debug.source = fd->source;+ b->debug.source_len = fd->source_len;+ }+ if (fd->scopes != fd->def_scope_array)+ js_free(ctx, fd->scopes);++ b->closure_var_count = fd->closure_var_count;+ if (b->closure_var_count) {+ b->closure_var = (void *)((uint8_t*)b + closure_var_offset);+ memcpy(b->closure_var, fd->closure_var, b->closure_var_count * sizeof(*b->closure_var));+ }+ js_free(ctx, fd->closure_var);+ fd->closure_var = NULL;++ b->has_prototype = fd->has_prototype;+ b->has_simple_parameter_list = fd->has_simple_parameter_list;+ b->js_mode = fd->js_mode;+ b->is_derived_class_constructor = fd->is_derived_class_constructor;+ b->func_kind = fd->func_kind;+ b->need_home_object = (fd->home_object_var_idx >= 0 ||+ fd->need_home_object);+ b->new_target_allowed = fd->new_target_allowed;+ b->super_call_allowed = fd->super_call_allowed;+ b->super_allowed = fd->super_allowed;+ b->arguments_allowed = fd->arguments_allowed;+ b->backtrace_barrier = fd->backtrace_barrier;+ b->realm = JS_DupContext(ctx);++ add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);+ +#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 1)+ if (!(fd->js_mode & JS_MODE_STRIP)) {+ js_dump_function_bytecode(ctx, b);+ }+#endif++ if (fd->parent) {+ /* remove from parent list */+ list_del(&fd->link);+ }++ js_free(ctx, fd);+ return JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b);+ fail:+ js_free_function_def(ctx, fd);+ return JS_EXCEPTION;+}++static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b)+{+ int i;++#if 0+ {+ char buf[ATOM_GET_STR_BUF_SIZE];+ printf("freeing %s\n",+ JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));+ }+#endif+ free_bytecode_atoms(rt, b->byte_code_buf, b->byte_code_len, TRUE);++ if (b->vardefs) {+ for(i = 0; i < b->arg_count + b->var_count; i++) {+ JS_FreeAtomRT(rt, b->vardefs[i].var_name);+ }+ }+ for(i = 0; i < b->cpool_count; i++)+ JS_FreeValueRT(rt, b->cpool[i]);++ for(i = 0; i < b->closure_var_count; i++) {+ JSClosureVar *cv = &b->closure_var[i];+ JS_FreeAtomRT(rt, cv->var_name);+ }+ if (b->realm)+ JS_FreeContext(b->realm);++ JS_FreeAtomRT(rt, b->func_name);+ if (b->has_debug) {+ JS_FreeAtomRT(rt, b->debug.filename);+ js_free_rt(rt, b->debug.pc2line_buf);+ js_free_rt(rt, b->debug.source);+ }++ remove_gc_object(&b->header);+ if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && b->header.ref_count != 0) {+ list_add_tail(&b->header.link, &rt->gc_zero_ref_count_list);+ } else {+ js_free_rt(rt, b);+ }+}++static __exception int js_parse_directives(JSParseState *s)+{+ char str[20];+ JSParsePos pos;+ BOOL has_semi;++ if (s->token.val != TOK_STRING)+ return 0;++ js_parse_get_pos(s, &pos);++ while(s->token.val == TOK_STRING) {+ /* Copy actual source string representation */+ snprintf(str, sizeof str, "%.*s",+ (int)(s->buf_ptr - s->token.ptr - 2), s->token.ptr + 1);++ if (next_token(s))+ return -1;++ has_semi = FALSE;+ switch (s->token.val) {+ case ';':+ if (next_token(s))+ return -1;+ has_semi = TRUE;+ break;+ case '}':+ case TOK_EOF:+ has_semi = TRUE;+ break;+ case TOK_NUMBER:+ case TOK_STRING:+ case TOK_TEMPLATE:+ case TOK_IDENT:+ case TOK_REGEXP:+ case TOK_DEC:+ case TOK_INC:+ case TOK_NULL:+ case TOK_FALSE:+ case TOK_TRUE:+ case TOK_IF:+ case TOK_RETURN:+ case TOK_VAR:+ case TOK_THIS:+ case TOK_DELETE:+ case TOK_TYPEOF:+ case TOK_NEW:+ case TOK_DO:+ case TOK_WHILE:+ case TOK_FOR:+ case TOK_SWITCH:+ case TOK_THROW:+ case TOK_TRY:+ case TOK_FUNCTION:+ case TOK_DEBUGGER:+ case TOK_WITH:+ case TOK_CLASS:+ case TOK_CONST:+ case TOK_ENUM:+ case TOK_EXPORT:+ case TOK_IMPORT:+ case TOK_SUPER:+ case TOK_INTERFACE:+ case TOK_LET:+ case TOK_PACKAGE:+ case TOK_PRIVATE:+ case TOK_PROTECTED:+ case TOK_PUBLIC:+ case TOK_STATIC:+ /* automatic insertion of ';' */+ if (s->got_lf)+ has_semi = TRUE;+ break;+ default:+ break;+ }+ if (!has_semi)+ break;+ if (!strcmp(str, "use strict")) {+ s->cur_func->has_use_strict = TRUE;+ s->cur_func->js_mode |= JS_MODE_STRICT;+ }+#if !defined(DUMP_BYTECODE) || !(DUMP_BYTECODE & 8)+ else if (!strcmp(str, "use strip")) {+ s->cur_func->js_mode |= JS_MODE_STRIP;+ }+#endif+#ifdef CONFIG_BIGNUM+ else if (s->ctx->bignum_ext && !strcmp(str, "use math")) {+ s->cur_func->js_mode |= JS_MODE_MATH;+ }+#endif+ }+ return js_parse_seek_token(s, &pos);+}++static int js_parse_function_check_names(JSParseState *s, JSFunctionDef *fd,+ JSAtom func_name)+{+ JSAtom name;+ int i, idx;++ if (fd->js_mode & JS_MODE_STRICT) {+ if (!fd->has_simple_parameter_list && fd->has_use_strict) {+ return js_parse_error(s, "\"use strict\" not allowed in function with default or destructuring parameter");+ }+ if (func_name == JS_ATOM_eval || func_name == JS_ATOM_arguments) {+ return js_parse_error(s, "invalid function name in strict code");+ }+ for (idx = 0; idx < fd->arg_count; idx++) {+ name = fd->args[idx].var_name;++ if (name == JS_ATOM_eval || name == JS_ATOM_arguments) {+ return js_parse_error(s, "invalid argument name in strict code");+ }+ }+ }+ /* check async_generator case */+ if ((fd->js_mode & JS_MODE_STRICT)+ || !fd->has_simple_parameter_list+ || (fd->func_type == JS_PARSE_FUNC_METHOD && fd->func_kind == JS_FUNC_ASYNC)+ || fd->func_type == JS_PARSE_FUNC_ARROW+ || fd->func_type == JS_PARSE_FUNC_METHOD) {+ for (idx = 0; idx < fd->arg_count; idx++) {+ name = fd->args[idx].var_name;+ if (name != JS_ATOM_NULL) {+ for (i = 0; i < idx; i++) {+ if (fd->args[i].var_name == name)+ goto duplicate;+ }+ /* Check if argument name duplicates a destructuring parameter */+ /* XXX: should have a flag for such variables */+ for (i = 0; i < fd->var_count; i++) {+ if (fd->vars[i].var_name == name &&+ fd->vars[i].scope_level == 0)+ goto duplicate;+ }+ }+ }+ }+ return 0;++duplicate:+ return js_parse_error(s, "duplicate argument names not allowed in this context");+}++/* create a function to initialize class fields */+static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s)+{+ JSFunctionDef *fd;+ + fd = js_new_function_def(s->ctx, s->cur_func, FALSE, FALSE,+ s->filename, 0);+ if (!fd)+ return NULL;+ fd->func_name = JS_ATOM_NULL;+ fd->has_prototype = FALSE;+ fd->has_home_object = TRUE;+ + fd->has_arguments_binding = FALSE;+ fd->has_this_binding = TRUE;+ fd->is_derived_class_constructor = FALSE;+ fd->new_target_allowed = TRUE;+ fd->super_call_allowed = FALSE;+ fd->super_allowed = fd->has_home_object;+ fd->arguments_allowed = FALSE;+ + fd->func_kind = JS_FUNC_NORMAL;+ fd->func_type = JS_PARSE_FUNC_METHOD;+ return fd;+}++/* func_name must be JS_ATOM_NULL for JS_PARSE_FUNC_STATEMENT and+ JS_PARSE_FUNC_EXPR, JS_PARSE_FUNC_ARROW and JS_PARSE_FUNC_VAR */+static __exception int js_parse_function_decl2(JSParseState *s,+ JSParseFunctionEnum func_type,+ JSFunctionKindEnum func_kind,+ JSAtom func_name,+ const uint8_t *ptr,+ int function_line_num,+ JSParseExportEnum export_flag,+ JSFunctionDef **pfd)+{+ JSContext *ctx = s->ctx;+ JSFunctionDef *fd = s->cur_func;+ BOOL is_expr;+ int func_idx, lexical_func_idx = -1;+ BOOL has_opt_arg;+ BOOL create_func_var = FALSE;++ is_expr = (func_type != JS_PARSE_FUNC_STATEMENT &&+ func_type != JS_PARSE_FUNC_VAR);++ if (func_type == JS_PARSE_FUNC_STATEMENT ||+ func_type == JS_PARSE_FUNC_VAR ||+ func_type == JS_PARSE_FUNC_EXPR) {+ if (func_kind == JS_FUNC_NORMAL &&+ token_is_pseudo_keyword(s, JS_ATOM_async) &&+ peek_token(s, TRUE) != '\n') {+ if (next_token(s))+ return -1;+ func_kind = JS_FUNC_ASYNC;+ }+ if (next_token(s))+ return -1;+ if (s->token.val == '*') {+ if (next_token(s))+ return -1;+ func_kind |= JS_FUNC_GENERATOR;+ }++ if (s->token.val == TOK_IDENT) {+ if (s->token.u.ident.is_reserved ||+ (s->token.u.ident.atom == JS_ATOM_yield &&+ func_type == JS_PARSE_FUNC_EXPR &&+ (func_kind & JS_FUNC_GENERATOR)) ||+ (s->token.u.ident.atom == JS_ATOM_await &&+ func_type == JS_PARSE_FUNC_EXPR &&+ (func_kind & JS_FUNC_ASYNC))) {+ return js_parse_error_reserved_identifier(s);+ }+ }+ if (s->token.val == TOK_IDENT ||+ (((s->token.val == TOK_YIELD && !(fd->js_mode & JS_MODE_STRICT)) ||+ (s->token.val == TOK_AWAIT && !s->is_module)) &&+ func_type == JS_PARSE_FUNC_EXPR)) {+ func_name = JS_DupAtom(ctx, s->token.u.ident.atom);+ if (next_token(s)) {+ JS_FreeAtom(ctx, func_name);+ return -1;+ }+ } else {+ if (func_type != JS_PARSE_FUNC_EXPR &&+ export_flag != JS_PARSE_EXPORT_DEFAULT) {+ return js_parse_error(s, "function name expected");+ }+ }+ } else if (func_type != JS_PARSE_FUNC_ARROW) {+ func_name = JS_DupAtom(ctx, func_name);+ }++ if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_MODULE &&+ (func_type == JS_PARSE_FUNC_STATEMENT || func_type == JS_PARSE_FUNC_VAR)) {+ JSGlobalVar *hf;+ hf = find_global_var(fd, func_name);+ /* XXX: should check scope chain */+ if (hf && hf->scope_level == fd->scope_level) {+ js_parse_error(s, "invalid redefinition of global identifier in module code");+ JS_FreeAtom(ctx, func_name);+ return -1;+ }+ }++ if (func_type == JS_PARSE_FUNC_VAR) {+ if (!(fd->js_mode & JS_MODE_STRICT)+ && func_kind == JS_FUNC_NORMAL+ && find_lexical_decl(ctx, fd, func_name, fd->scope_first, FALSE) < 0+ && !((func_idx = find_var(ctx, fd, func_name)) >= 0 && (func_idx & ARGUMENT_VAR_OFFSET))+ && !(func_name == JS_ATOM_arguments && fd->has_arguments_binding)) {+ create_func_var = TRUE;+ }+ /* Create the lexical name here so that the function closure+ contains it */+ if (fd->is_eval &&+ (fd->eval_type == JS_EVAL_TYPE_GLOBAL ||+ fd->eval_type == JS_EVAL_TYPE_MODULE) &&+ fd->scope_level == fd->body_scope) {+ /* avoid creating a lexical variable in the global+ scope. XXX: check annex B */+ JSGlobalVar *hf;+ hf = find_global_var(fd, func_name);+ /* XXX: should check scope chain */+ if (hf && hf->scope_level == fd->scope_level) {+ js_parse_error(s, "invalid redefinition of global identifier");+ JS_FreeAtom(ctx, func_name);+ return -1;+ }+ } else {+ /* Always create a lexical name, fail if at the same scope as+ existing name */+ /* Lexical variable will be initialized upon entering scope */+ lexical_func_idx = define_var(s, fd, func_name,+ func_kind != JS_FUNC_NORMAL ?+ JS_VAR_DEF_NEW_FUNCTION_DECL :+ JS_VAR_DEF_FUNCTION_DECL);+ if (lexical_func_idx < 0) {+ JS_FreeAtom(ctx, func_name);+ return -1;+ }+ }+ }++ fd = js_new_function_def(ctx, fd, FALSE, is_expr,+ s->filename, function_line_num);+ if (!fd) {+ JS_FreeAtom(ctx, func_name);+ return -1;+ }+ if (pfd)+ *pfd = fd;+ s->cur_func = fd;+ fd->func_name = func_name;+ /* XXX: test !fd->is_generator is always false */+ fd->has_prototype = (func_type == JS_PARSE_FUNC_STATEMENT ||+ func_type == JS_PARSE_FUNC_VAR ||+ func_type == JS_PARSE_FUNC_EXPR) &&+ func_kind == JS_FUNC_NORMAL;+ fd->has_home_object = (func_type == JS_PARSE_FUNC_METHOD ||+ func_type == JS_PARSE_FUNC_GETTER ||+ func_type == JS_PARSE_FUNC_SETTER ||+ func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR ||+ func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR);+ fd->has_arguments_binding = (func_type != JS_PARSE_FUNC_ARROW);+ fd->has_this_binding = fd->has_arguments_binding;+ fd->is_derived_class_constructor = (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR);+ if (func_type == JS_PARSE_FUNC_ARROW) {+ fd->new_target_allowed = fd->parent->new_target_allowed;+ fd->super_call_allowed = fd->parent->super_call_allowed;+ fd->super_allowed = fd->parent->super_allowed;+ fd->arguments_allowed = fd->parent->arguments_allowed;+ } else {+ fd->new_target_allowed = TRUE;+ fd->super_call_allowed = fd->is_derived_class_constructor;+ fd->super_allowed = fd->has_home_object;+ fd->arguments_allowed = TRUE;+ }++ /* fd->in_function_body == FALSE prevents yield/await during the parsing+ of the arguments in generator/async functions. They are parsed as+ regular identifiers for other function kinds. */+ fd->func_kind = func_kind;+ fd->func_type = func_type;++ if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR ||+ func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) {+ /* error if not invoked as a constructor */+ emit_op(s, OP_check_ctor);+ }++ if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) {+ emit_class_field_init(s);+ }+ + /* parse arguments */+ fd->has_simple_parameter_list = TRUE;+ fd->has_parameter_expressions = FALSE;+ has_opt_arg = FALSE;+ if (func_type == JS_PARSE_FUNC_ARROW && s->token.val == TOK_IDENT) {+ JSAtom name;+ if (s->token.u.ident.is_reserved) {+ js_parse_error_reserved_identifier(s);+ goto fail;+ }+ name = s->token.u.ident.atom;+ if (add_arg(ctx, fd, name) < 0)+ goto fail;+ fd->defined_arg_count = 1;+ } else {+ if (s->token.val == '(') {+ int skip_bits;+ /* if there is an '=' inside the parameter list, we+ consider there is a parameter expression inside */+ js_parse_skip_parens_token(s, &skip_bits, FALSE);+ if (skip_bits & SKIP_HAS_ASSIGNMENT)+ fd->has_parameter_expressions = TRUE;+ if (next_token(s))+ goto fail;+ } else {+ if (js_parse_expect(s, '('))+ goto fail;+ }++ if (fd->has_parameter_expressions) {+ fd->scope_level = -1; /* force no parent scope */+ if (push_scope(s) < 0)+ return -1;+ }+ + while (s->token.val != ')') {+ JSAtom name;+ BOOL rest = FALSE;+ int idx, has_initializer;++ if (s->token.val == TOK_ELLIPSIS) {+ fd->has_simple_parameter_list = FALSE;+ rest = TRUE;+ if (next_token(s))+ goto fail;+ }+ if (s->token.val == '[' || s->token.val == '{') {+ fd->has_simple_parameter_list = FALSE;+ if (rest) {+ emit_op(s, OP_rest);+ emit_u16(s, fd->arg_count);+ } else {+ /* unnamed arg for destructuring */+ idx = add_arg(ctx, fd, JS_ATOM_NULL);+ emit_op(s, OP_get_arg);+ emit_u16(s, idx);+ }+ has_initializer = js_parse_destructuring_element(s, fd->has_parameter_expressions ? TOK_LET : TOK_VAR, 1, TRUE, -1, TRUE);+ if (has_initializer < 0)+ goto fail;+ if (has_initializer)+ has_opt_arg = TRUE;+ if (!has_opt_arg)+ fd->defined_arg_count++;+ } else if (s->token.val == TOK_IDENT) {+ if (s->token.u.ident.is_reserved) {+ js_parse_error_reserved_identifier(s);+ goto fail;+ }+ name = s->token.u.ident.atom;+ if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) {+ js_parse_error_reserved_identifier(s);+ goto fail;+ }+ if (fd->has_parameter_expressions) {+ if (define_var(s, fd, name, JS_VAR_DEF_LET) < 0)+ goto fail;+ }+ /* XXX: could avoid allocating an argument if rest is true */+ idx = add_arg(ctx, fd, name);+ if (idx < 0)+ goto fail;+ if (next_token(s))+ goto fail;+ if (rest) {+ emit_op(s, OP_rest);+ emit_u16(s, idx);+ if (fd->has_parameter_expressions) {+ emit_op(s, OP_dup);+ emit_op(s, OP_scope_put_var_init);+ emit_atom(s, name);+ emit_u16(s, fd->scope_level);+ }+ emit_op(s, OP_put_arg);+ emit_u16(s, idx);+ fd->has_simple_parameter_list = FALSE;+ has_opt_arg = TRUE;+ } else if (s->token.val == '=') {+ int label;+ + fd->has_simple_parameter_list = FALSE;+ has_opt_arg = TRUE;++ if (next_token(s))+ goto fail;++ label = new_label(s);+ emit_op(s, OP_get_arg);+ emit_u16(s, idx);+ emit_op(s, OP_dup);+ emit_op(s, OP_undefined);+ emit_op(s, OP_strict_eq);+ emit_goto(s, OP_if_false, label);+ emit_op(s, OP_drop);+ if (js_parse_assign_expr(s))+ goto fail;+ set_object_name(s, name);+ emit_op(s, OP_dup);+ emit_op(s, OP_put_arg);+ emit_u16(s, idx);+ emit_label(s, label);+ emit_op(s, OP_scope_put_var_init);+ emit_atom(s, name);+ emit_u16(s, fd->scope_level);+ } else {+ if (!has_opt_arg) {+ fd->defined_arg_count++;+ }+ if (fd->has_parameter_expressions) {+ /* copy the argument to the argument scope */+ emit_op(s, OP_get_arg);+ emit_u16(s, idx);+ emit_op(s, OP_scope_put_var_init);+ emit_atom(s, name);+ emit_u16(s, fd->scope_level);+ }+ }+ } else {+ js_parse_error(s, "missing formal parameter");+ goto fail;+ }+ if (rest && s->token.val != ')') {+ js_parse_expect(s, ')');+ goto fail;+ }+ if (s->token.val == ')')+ break;+ if (js_parse_expect(s, ','))+ goto fail;+ }+ if ((func_type == JS_PARSE_FUNC_GETTER && fd->arg_count != 0) ||+ (func_type == JS_PARSE_FUNC_SETTER && fd->arg_count != 1)) {+ js_parse_error(s, "invalid number of arguments for getter or setter");+ goto fail;+ }+ }++ if (fd->has_parameter_expressions) {+ int idx;++ /* Copy the variables in the argument scope to the variable+ scope (see FunctionDeclarationInstantiation() in spec). The+ normal arguments are already present, so no need to copy+ them. */+ idx = fd->scopes[fd->scope_level].first;+ while (idx >= 0) {+ JSVarDef *vd = &fd->vars[idx];+ if (vd->scope_level != fd->scope_level)+ break;+ if (find_var(ctx, fd, vd->var_name) < 0) {+ if (add_var(ctx, fd, vd->var_name) < 0)+ goto fail;+ vd = &fd->vars[idx]; /* fd->vars may have been reallocated */+ emit_op(s, OP_scope_get_var);+ emit_atom(s, vd->var_name);+ emit_u16(s, fd->scope_level);+ emit_op(s, OP_scope_put_var);+ emit_atom(s, vd->var_name);+ emit_u16(s, 0);+ }+ idx = vd->scope_next;+ }+ + /* the argument scope has no parent, hence we don't use pop_scope(s) */+ emit_op(s, OP_leave_scope);+ emit_u16(s, fd->scope_level);++ /* set the variable scope as the current scope */+ fd->scope_level = 0;+ fd->scope_first = fd->scopes[fd->scope_level].first;+ }+ + if (next_token(s))+ goto fail;++ /* generator function: yield after the parameters are evaluated */+ if (func_kind == JS_FUNC_GENERATOR ||+ func_kind == JS_FUNC_ASYNC_GENERATOR)+ emit_op(s, OP_initial_yield);++ /* in generators, yield expression is forbidden during the parsing+ of the arguments */+ fd->in_function_body = TRUE;+ push_scope(s); /* enter body scope */+ fd->body_scope = fd->scope_level;++ if (s->token.val == TOK_ARROW) {+ if (next_token(s))+ goto fail;++ if (s->token.val != '{') {+ if (js_parse_function_check_names(s, fd, func_name))+ goto fail;++ if (js_parse_assign_expr(s))+ goto fail;++ if (func_kind != JS_FUNC_NORMAL)+ emit_op(s, OP_return_async);+ else+ emit_op(s, OP_return);++ if (!(fd->js_mode & JS_MODE_STRIP)) {+ /* save the function source code */+ /* the end of the function source code is after the last+ token of the function source stored into s->last_ptr */+ fd->source_len = s->last_ptr - ptr;+ fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len);+ if (!fd->source)+ goto fail;+ }+ goto done;+ }+ }++ if (js_parse_expect(s, '{'))+ goto fail;++ if (js_parse_directives(s))+ goto fail;++ /* in strict_mode, check function and argument names */+ if (js_parse_function_check_names(s, fd, func_name))+ goto fail;++ while (s->token.val != '}') {+ if (js_parse_source_element(s))+ goto fail;+ }+ if (!(fd->js_mode & JS_MODE_STRIP)) {+ /* save the function source code */+ fd->source_len = s->buf_ptr - ptr;+ fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len);+ if (!fd->source)+ goto fail;+ }++ if (next_token(s)) {+ /* consume the '}' */+ goto fail;+ }++ /* in case there is no return, add one */+ if (js_is_live_code(s)) {+ emit_return(s, FALSE);+ }+done:+ s->cur_func = fd->parent;++ /* create the function object */+ {+ int idx;+ JSAtom func_name = fd->func_name;++ /* the real object will be set at the end of the compilation */+ idx = cpool_add(s, JS_NULL);+ fd->parent_cpool_idx = idx;++ if (is_expr) {+ /* for constructors, no code needs to be generated here */+ if (func_type != JS_PARSE_FUNC_CLASS_CONSTRUCTOR &&+ func_type != JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) {+ /* OP_fclosure creates the function object from the bytecode+ and adds the scope information */+ emit_op(s, OP_fclosure);+ emit_u32(s, idx);+ if (func_name == JS_ATOM_NULL) {+ emit_op(s, OP_set_name);+ emit_u32(s, JS_ATOM_NULL);+ }+ }+ } else if (func_type == JS_PARSE_FUNC_VAR) {+ emit_op(s, OP_fclosure);+ emit_u32(s, idx);+ if (create_func_var) {+ if (s->cur_func->is_global_var) {+ JSGlobalVar *hf;+ /* the global variable must be defined at the start of the+ function */+ hf = add_global_var(ctx, s->cur_func, func_name);+ if (!hf)+ goto fail;+ /* it is considered as defined at the top level+ (needed for annex B.3.3.4 and B.3.3.5+ checks) */+ hf->scope_level = 0; + hf->force_init = ((s->cur_func->js_mode & JS_MODE_STRICT) != 0);+ /* store directly into global var, bypass lexical scope */+ emit_op(s, OP_dup);+ emit_op(s, OP_scope_put_var);+ emit_atom(s, func_name);+ emit_u16(s, 0);+ } else {+ /* do not call define_var to bypass lexical scope check */+ func_idx = find_var(ctx, s->cur_func, func_name);+ if (func_idx < 0) {+ func_idx = add_var(ctx, s->cur_func, func_name);+ if (func_idx < 0)+ goto fail;+ }+ /* store directly into local var, bypass lexical catch scope */+ emit_op(s, OP_dup);+ emit_op(s, OP_scope_put_var);+ emit_atom(s, func_name);+ emit_u16(s, 0);+ }+ }+ if (lexical_func_idx >= 0) {+ /* lexical variable will be initialized upon entering scope */+ s->cur_func->vars[lexical_func_idx].func_pool_idx = idx;+ emit_op(s, OP_drop);+ } else {+ /* store function object into its lexical name */+ /* XXX: could use OP_put_loc directly */+ emit_op(s, OP_scope_put_var_init);+ emit_atom(s, func_name);+ emit_u16(s, s->cur_func->scope_level);+ }+ } else {+ if (!s->cur_func->is_global_var) {+ int var_idx = define_var(s, s->cur_func, func_name, JS_VAR_DEF_VAR);++ if (var_idx < 0)+ goto fail;+ /* the variable will be assigned at the top of the function */+ if (var_idx & ARGUMENT_VAR_OFFSET) {+ s->cur_func->args[var_idx - ARGUMENT_VAR_OFFSET].func_pool_idx = idx;+ } else {+ s->cur_func->vars[var_idx].func_pool_idx = idx;+ }+ } else {+ JSAtom func_var_name;+ JSGlobalVar *hf;+ if (func_name == JS_ATOM_NULL)+ func_var_name = JS_ATOM__default_; /* export default */+ else+ func_var_name = func_name;+ /* the variable will be assigned at the top of the function */+ hf = add_global_var(ctx, s->cur_func, func_var_name);+ if (!hf)+ goto fail;+ hf->cpool_idx = idx;+ if (export_flag != JS_PARSE_EXPORT_NONE) {+ if (!add_export_entry(s, s->cur_func->module, func_var_name,+ export_flag == JS_PARSE_EXPORT_NAMED ? func_var_name : JS_ATOM_default, JS_EXPORT_TYPE_LOCAL))+ goto fail;+ }+ }+ }+ }+ return 0;+ fail:+ s->cur_func = fd->parent;+ js_free_function_def(ctx, fd);+ if (pfd)+ *pfd = NULL;+ return -1;+}++static __exception int js_parse_function_decl(JSParseState *s,+ JSParseFunctionEnum func_type,+ JSFunctionKindEnum func_kind,+ JSAtom func_name,+ const uint8_t *ptr,+ int function_line_num)+{+ return js_parse_function_decl2(s, func_type, func_kind, func_name, ptr,+ function_line_num, JS_PARSE_EXPORT_NONE,+ NULL);+}++static __exception int js_parse_program(JSParseState *s)+{+ JSFunctionDef *fd = s->cur_func;+ int idx;++ if (next_token(s))+ return -1;++ if (js_parse_directives(s))+ return -1;++ fd->is_global_var = (fd->eval_type == JS_EVAL_TYPE_GLOBAL) ||+ (fd->eval_type == JS_EVAL_TYPE_MODULE) ||+ !(fd->js_mode & JS_MODE_STRICT);++ if (!s->is_module) {+ /* hidden variable for the return value */+ fd->eval_ret_idx = idx = add_var(s->ctx, fd, JS_ATOM__ret_);+ if (idx < 0)+ return -1;+ }++ while (s->token.val != TOK_EOF) {+ if (js_parse_source_element(s))+ return -1;+ }++ if (!s->is_module) {+ /* return the value of the hidden variable eval_ret_idx */+ emit_op(s, OP_get_loc);+ emit_u16(s, fd->eval_ret_idx);++ emit_op(s, OP_return);+ } else {+ emit_op(s, OP_return_undef);+ }++ return 0;+}++static void js_parse_init(JSContext *ctx, JSParseState *s,+ const char *input, size_t input_len,+ const char *filename)+{+ memset(s, 0, sizeof(*s));+ s->ctx = ctx;+ s->filename = filename;+ s->line_num = 1;+ s->buf_ptr = (const uint8_t *)input;+ s->buf_end = s->buf_ptr + input_len;+ s->token.val = ' ';+ s->token.line_num = 1;+}++static JSValue JS_EvalFunctionInternal(JSContext *ctx, JSValue fun_obj,+ JSValueConst this_obj,+ JSVarRef **var_refs, JSStackFrame *sf)+{+ JSValue ret_val;+ uint32_t tag;++ tag = JS_VALUE_GET_TAG(fun_obj);+ if (tag == JS_TAG_FUNCTION_BYTECODE) {+ fun_obj = js_closure(ctx, fun_obj, var_refs, sf);+ ret_val = JS_CallFree(ctx, fun_obj, this_obj, 0, NULL);+ } else if (tag == JS_TAG_MODULE) {+ JSModuleDef *m;+ m = JS_VALUE_GET_PTR(fun_obj);+ /* the module refcount should be >= 2 */+ JS_FreeValue(ctx, fun_obj);+ if (js_create_module_function(ctx, m) < 0)+ goto fail;+ if (js_link_module(ctx, m) < 0)+ goto fail;+ ret_val = js_evaluate_module(ctx, m);+ if (JS_IsException(ret_val)) {+ fail:+ js_free_modules(ctx, JS_FREE_MODULE_NOT_EVALUATED);+ return JS_EXCEPTION;+ }+ } else {+ JS_FreeValue(ctx, fun_obj);+ ret_val = JS_ThrowTypeError(ctx, "bytecode function expected");+ }+ return ret_val;+}++JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj)+{+ return JS_EvalFunctionInternal(ctx, fun_obj, ctx->global_obj, NULL, NULL);+}++static void skip_shebang(JSParseState *s)+{+ const uint8_t *p = s->buf_ptr;+ int c;++ if (p[0] == '#' && p[1] == '!') {+ p += 2;+ while (p < s->buf_end) {+ if (*p == '\n' || *p == '\r') {+ break;+ } else if (*p >= 0x80) {+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);+ if (c == CP_LS || c == CP_PS) {+ break;+ } else if (c == -1) {+ p++; /* skip invalid UTF-8 */+ }+ } else {+ p++;+ }+ }+ s->buf_ptr = p;+ }+}++/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */+static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,+ const char *input, size_t input_len,+ const char *filename, int flags, int scope_idx)+{+ JSParseState s1, *s = &s1;+ int err, js_mode, eval_type;+ JSValue fun_obj, ret_val;+ JSStackFrame *sf;+ JSVarRef **var_refs;+ JSFunctionBytecode *b;+ JSFunctionDef *fd;+ JSModuleDef *m;++ js_parse_init(ctx, s, input, input_len, filename);+ skip_shebang(s);++ eval_type = flags & JS_EVAL_TYPE_MASK;+ m = NULL;+ if (eval_type == JS_EVAL_TYPE_DIRECT) {+ JSObject *p;+ sf = ctx->rt->current_stack_frame;+ assert(sf != NULL);+ assert(JS_VALUE_GET_TAG(sf->cur_func) == JS_TAG_OBJECT);+ p = JS_VALUE_GET_OBJ(sf->cur_func);+ assert(js_class_has_bytecode(p->class_id));+ b = p->u.func.function_bytecode;+ var_refs = p->u.func.var_refs;+ js_mode = b->js_mode;+ } else {+ sf = NULL;+ b = NULL;+ var_refs = NULL;+ js_mode = 0;+ if (flags & JS_EVAL_FLAG_STRICT)+ js_mode |= JS_MODE_STRICT;+ if (flags & JS_EVAL_FLAG_STRIP)+ js_mode |= JS_MODE_STRIP;+ if (eval_type == JS_EVAL_TYPE_MODULE) {+ JSAtom module_name = JS_NewAtom(ctx, filename);+ if (module_name == JS_ATOM_NULL)+ return JS_EXCEPTION;+ m = js_new_module_def(ctx, module_name);+ if (!m)+ return JS_EXCEPTION;+ js_mode |= JS_MODE_STRICT;+ }+ }+ fd = js_new_function_def(ctx, NULL, TRUE, FALSE, filename, 1);+ if (!fd)+ goto fail1;+ s->cur_func = fd;+ fd->eval_type = eval_type;+ fd->has_this_binding = (eval_type != JS_EVAL_TYPE_DIRECT);+ fd->backtrace_barrier = ((flags & JS_EVAL_FLAG_BACKTRACE_BARRIER) != 0);+ if (eval_type == JS_EVAL_TYPE_DIRECT) {+ fd->new_target_allowed = b->new_target_allowed;+ fd->super_call_allowed = b->super_call_allowed;+ fd->super_allowed = b->super_allowed;+ fd->arguments_allowed = b->arguments_allowed;+ } else {+ fd->new_target_allowed = FALSE;+ fd->super_call_allowed = FALSE;+ fd->super_allowed = FALSE;+ fd->arguments_allowed = TRUE;+ }+ fd->js_mode = js_mode;+ fd->func_name = JS_DupAtom(ctx, JS_ATOM__eval_);+ if (b) {+ if (add_closure_variables(ctx, fd, b, scope_idx))+ goto fail;+ }+ fd->module = m;+ s->is_module = (m != NULL);+ s->allow_html_comments = !s->is_module;++ push_scope(s); /* body scope */+ fd->body_scope = fd->scope_level;+ + err = js_parse_program(s);+ if (err) {+ fail:+ free_token(s, &s->token);+ js_free_function_def(ctx, fd);+ goto fail1;+ }++ /* create the function object and all the enclosed functions */+ fun_obj = js_create_function(ctx, fd);+ if (JS_IsException(fun_obj))+ goto fail1;+ /* Could add a flag to avoid resolution if necessary */+ if (m) {+ m->func_obj = fun_obj;+ if (js_resolve_module(ctx, m) < 0)+ goto fail1;+ fun_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));+ }+ if (flags & JS_EVAL_FLAG_COMPILE_ONLY) {+ ret_val = fun_obj;+ } else {+ ret_val = JS_EvalFunctionInternal(ctx, fun_obj, this_obj, var_refs, sf);+ }+ return ret_val;+ fail1:+ /* XXX: should free all the unresolved dependencies */+ if (m)+ js_free_module_def(ctx, m);+ return JS_EXCEPTION;+}++/* the indirection is needed to make 'eval' optional */+static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,+ const char *input, size_t input_len,+ const char *filename, int flags, int scope_idx)+{+ if (unlikely(!ctx->eval_internal)) {+ return JS_ThrowTypeError(ctx, "eval is not supported");+ }+ return ctx->eval_internal(ctx, this_obj, input, input_len, filename,+ flags, scope_idx);+}++static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,+ JSValueConst val, int flags, int scope_idx)+{+ JSValue ret;+ const char *str;+ size_t len;++ if (!JS_IsString(val))+ return JS_DupValue(ctx, val);+ str = JS_ToCStringLen(ctx, &len, val);+ if (!str)+ return JS_EXCEPTION;+ ret = JS_EvalInternal(ctx, this_obj, str, len, "<input>", flags, scope_idx);+ JS_FreeCString(ctx, str);+ return ret;++}++JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj,+ const char *input, size_t input_len,+ const char *filename, int eval_flags)+{+ int eval_type = eval_flags & JS_EVAL_TYPE_MASK;+ JSValue ret;++ assert(eval_type == JS_EVAL_TYPE_GLOBAL ||+ eval_type == JS_EVAL_TYPE_MODULE);+ ret = JS_EvalInternal(ctx, this_obj, input, input_len, filename,+ eval_flags, -1);+ return ret;+}++JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len,+ const char *filename, int eval_flags)+{+ return JS_EvalThis(ctx, ctx->global_obj, input, input_len, filename,+ eval_flags);+}++int JS_ResolveModule(JSContext *ctx, JSValueConst obj)+{+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) {+ JSModuleDef *m = JS_VALUE_GET_PTR(obj);+ if (js_resolve_module(ctx, m) < 0) {+ js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED);+ return -1;+ }+ }+ return 0;+}++/*******************************************************************/+/* object list */++typedef struct {+ JSObject *obj;+ uint32_t hash_next; /* -1 if no next entry */+} JSObjectListEntry;++/* XXX: reuse it to optimize weak references */+typedef struct {+ JSObjectListEntry *object_tab;+ int object_count;+ int object_size;+ uint32_t *hash_table;+ uint32_t hash_size;+} JSObjectList;++static void js_object_list_init(JSObjectList *s)+{+ memset(s, 0, sizeof(*s));+}++static uint32_t js_object_list_get_hash(JSObject *p, uint32_t hash_size)+{+ return ((uintptr_t)p * 3163) & (hash_size - 1);+}++static int js_object_list_resize_hash(JSContext *ctx, JSObjectList *s,+ uint32_t new_hash_size)+{+ JSObjectListEntry *e;+ uint32_t i, h, *new_hash_table;++ new_hash_table = js_malloc(ctx, sizeof(new_hash_table[0]) * new_hash_size);+ if (!new_hash_table)+ return -1;+ js_free(ctx, s->hash_table);+ s->hash_table = new_hash_table;+ s->hash_size = new_hash_size;+ + for(i = 0; i < s->hash_size; i++) {+ s->hash_table[i] = -1;+ }+ for(i = 0; i < s->object_count; i++) {+ e = &s->object_tab[i];+ h = js_object_list_get_hash(e->obj, s->hash_size); + e->hash_next = s->hash_table[h];+ s->hash_table[h] = i;+ }+ return 0;+}++/* the reference count of 'obj' is not modified. Return 0 if OK, -1 if+ memory error */+static int js_object_list_add(JSContext *ctx, JSObjectList *s, JSObject *obj)+{+ JSObjectListEntry *e;+ uint32_t h, new_hash_size;+ + if (js_resize_array(ctx, (void *)&s->object_tab,+ sizeof(s->object_tab[0]),+ &s->object_size, s->object_count + 1))+ return -1;+ if (unlikely((s->object_count + 1) >= s->hash_size)) {+ new_hash_size = max_uint32(s->hash_size, 4);+ while (new_hash_size <= s->object_count)+ new_hash_size *= 2;+ if (js_object_list_resize_hash(ctx, s, new_hash_size))+ return -1;+ }+ e = &s->object_tab[s->object_count++];+ h = js_object_list_get_hash(obj, s->hash_size); + e->obj = obj;+ e->hash_next = s->hash_table[h];+ s->hash_table[h] = s->object_count - 1;+ return 0;+}++/* return -1 if not present or the object index */+static int js_object_list_find(JSContext *ctx, JSObjectList *s, JSObject *obj)+{+ JSObjectListEntry *e;+ uint32_t h, p;++ /* must test empty size because there is no hash table */+ if (s->object_count == 0)+ return -1;+ h = js_object_list_get_hash(obj, s->hash_size); + p = s->hash_table[h];+ while (p != -1) {+ e = &s->object_tab[p];+ if (e->obj == obj)+ return p;+ p = e->hash_next;+ }+ return -1;+}++static void js_object_list_end(JSContext *ctx, JSObjectList *s)+{+ js_free(ctx, s->object_tab);+ js_free(ctx, s->hash_table);+}++/*******************************************************************/+/* binary object writer & reader */++typedef enum BCTagEnum {+ BC_TAG_NULL = 1,+ BC_TAG_UNDEFINED,+ BC_TAG_BOOL_FALSE,+ BC_TAG_BOOL_TRUE,+ BC_TAG_INT32,+ BC_TAG_FLOAT64,+ BC_TAG_STRING,+ BC_TAG_OBJECT,+ BC_TAG_ARRAY,+ BC_TAG_BIG_INT,+ BC_TAG_BIG_FLOAT,+ BC_TAG_BIG_DECIMAL,+ BC_TAG_TEMPLATE_OBJECT,+ BC_TAG_FUNCTION_BYTECODE,+ BC_TAG_MODULE,+ BC_TAG_TYPED_ARRAY,+ BC_TAG_ARRAY_BUFFER,+ BC_TAG_SHARED_ARRAY_BUFFER,+ BC_TAG_DATE,+ BC_TAG_OBJECT_VALUE,+ BC_TAG_OBJECT_REFERENCE,+} BCTagEnum;++#ifdef CONFIG_BIGNUM+#define BC_BASE_VERSION 2+#else+#define BC_BASE_VERSION 1+#endif+#define BC_BE_VERSION 0x40+#ifdef WORDS_BIGENDIAN+#define BC_VERSION (BC_BASE_VERSION | BC_BE_VERSION)+#else+#define BC_VERSION BC_BASE_VERSION+#endif++typedef struct BCWriterState {+ JSContext *ctx;+ DynBuf dbuf;+ BOOL byte_swap : 8;+ BOOL allow_bytecode : 8;+ BOOL allow_sab : 8;+ BOOL allow_reference : 8;+ uint32_t first_atom;+ uint32_t *atom_to_idx;+ int atom_to_idx_size;+ JSAtom *idx_to_atom;+ int idx_to_atom_count;+ int idx_to_atom_size;+ uint8_t **sab_tab;+ int sab_tab_len;+ int sab_tab_size;+ /* list of referenced objects (used if allow_reference = TRUE) */+ JSObjectList object_list;+} BCWriterState;++#ifdef DUMP_READ_OBJECT+static const char * const bc_tag_str[] = {+ "invalid",+ "null",+ "undefined",+ "false",+ "true",+ "int32",+ "float64",+ "string",+ "object",+ "array",+ "bigint",+ "bigfloat",+ "bigdecimal",+ "template",+ "function",+ "module",+ "TypedArray",+ "ArrayBuffer",+ "SharedArrayBuffer",+ "Date",+ "ObjectValue",+ "ObjectReference",+};+#endif++static void bc_put_u8(BCWriterState *s, uint8_t v)+{+ dbuf_putc(&s->dbuf, v);+}++static void bc_put_u16(BCWriterState *s, uint16_t v)+{+ if (s->byte_swap)+ v = bswap16(v);+ dbuf_put_u16(&s->dbuf, v);+}++static __maybe_unused void bc_put_u32(BCWriterState *s, uint32_t v)+{+ if (s->byte_swap)+ v = bswap32(v);+ dbuf_put_u32(&s->dbuf, v);+}++static void bc_put_u64(BCWriterState *s, uint64_t v)+{+ if (s->byte_swap)+ v = bswap64(v);+ dbuf_put(&s->dbuf, (uint8_t *)&v, sizeof(v));+}++static void bc_put_leb128(BCWriterState *s, uint32_t v)+{+ dbuf_put_leb128(&s->dbuf, v);+}++static void bc_put_sleb128(BCWriterState *s, int32_t v)+{+ dbuf_put_sleb128(&s->dbuf, v);+}++static void bc_set_flags(uint32_t *pflags, int *pidx, uint32_t val, int n)+{+ *pflags = *pflags | (val << *pidx);+ *pidx += n;+}++static int bc_atom_to_idx(BCWriterState *s, uint32_t *pres, JSAtom atom)+{+ uint32_t v;++ if (atom < s->first_atom || __JS_AtomIsTaggedInt(atom)) {+ *pres = atom;+ return 0;+ }+ atom -= s->first_atom;+ if (atom < s->atom_to_idx_size && s->atom_to_idx[atom] != 0) {+ *pres = s->atom_to_idx[atom];+ return 0;+ }+ if (atom >= s->atom_to_idx_size) {+ int old_size, i;+ old_size = s->atom_to_idx_size;+ if (js_resize_array(s->ctx, (void **)&s->atom_to_idx,+ sizeof(s->atom_to_idx[0]), &s->atom_to_idx_size,+ atom + 1))+ return -1;+ /* XXX: could add a specific js_resize_array() function to do it */+ for(i = old_size; i < s->atom_to_idx_size; i++)+ s->atom_to_idx[i] = 0;+ }+ if (js_resize_array(s->ctx, (void **)&s->idx_to_atom,+ sizeof(s->idx_to_atom[0]),+ &s->idx_to_atom_size, s->idx_to_atom_count + 1))+ goto fail;++ v = s->idx_to_atom_count++;+ s->idx_to_atom[v] = atom + s->first_atom;+ v += s->first_atom;+ s->atom_to_idx[atom] = v;+ *pres = v;+ return 0;+ fail:+ *pres = 0;+ return -1;+}++static int bc_put_atom(BCWriterState *s, JSAtom atom)+{+ uint32_t v;++ if (__JS_AtomIsTaggedInt(atom)) {+ v = (__JS_AtomToUInt32(atom) << 1) | 1;+ } else {+ if (bc_atom_to_idx(s, &v, atom))+ return -1;+ v <<= 1;+ }+ bc_put_leb128(s, v);+ return 0;+}++static void bc_byte_swap(uint8_t *bc_buf, int bc_len)+{+ int pos, len, op, fmt;++ pos = 0;+ while (pos < bc_len) {+ op = bc_buf[pos];+ len = short_opcode_info(op).size;+ fmt = short_opcode_info(op).fmt;+ switch(fmt) {+ case OP_FMT_u16:+ case OP_FMT_i16:+ case OP_FMT_label16:+ case OP_FMT_npop:+ case OP_FMT_loc:+ case OP_FMT_arg:+ case OP_FMT_var_ref:+ put_u16(bc_buf + pos + 1,+ bswap16(get_u16(bc_buf + pos + 1)));+ break;+ case OP_FMT_i32:+ case OP_FMT_u32:+ case OP_FMT_const:+ case OP_FMT_label:+ case OP_FMT_atom:+ case OP_FMT_atom_u8:+ put_u32(bc_buf + pos + 1,+ bswap32(get_u32(bc_buf + pos + 1)));+ break;+ case OP_FMT_atom_u16:+ case OP_FMT_label_u16:+ put_u32(bc_buf + pos + 1,+ bswap32(get_u32(bc_buf + pos + 1)));+ put_u16(bc_buf + pos + 1 + 4,+ bswap16(get_u16(bc_buf + pos + 1 + 4)));+ break;+ case OP_FMT_atom_label_u8:+ case OP_FMT_atom_label_u16:+ put_u32(bc_buf + pos + 1,+ bswap32(get_u32(bc_buf + pos + 1)));+ put_u32(bc_buf + pos + 1 + 4,+ bswap32(get_u32(bc_buf + pos + 1 + 4)));+ if (fmt == OP_FMT_atom_label_u16) {+ put_u16(bc_buf + pos + 1 + 4 + 4,+ bswap16(get_u16(bc_buf + pos + 1 + 4 + 4)));+ }+ break;+ case OP_FMT_npop_u16:+ put_u16(bc_buf + pos + 1,+ bswap16(get_u16(bc_buf + pos + 1)));+ put_u16(bc_buf + pos + 1 + 2,+ bswap16(get_u16(bc_buf + pos + 1 + 2)));+ break;+ default:+ break;+ }+ pos += len;+ }+}++static int JS_WriteFunctionBytecode(BCWriterState *s,+ const uint8_t *bc_buf1, int bc_len)+{+ int pos, len, op;+ JSAtom atom;+ uint8_t *bc_buf;+ uint32_t val;++ bc_buf = js_malloc(s->ctx, bc_len);+ if (!bc_buf)+ return -1;+ memcpy(bc_buf, bc_buf1, bc_len);++ pos = 0;+ while (pos < bc_len) {+ op = bc_buf[pos];+ len = short_opcode_info(op).size;+ switch(short_opcode_info(op).fmt) {+ case OP_FMT_atom:+ case OP_FMT_atom_u8:+ case OP_FMT_atom_u16:+ case OP_FMT_atom_label_u8:+ case OP_FMT_atom_label_u16:+ atom = get_u32(bc_buf + pos + 1);+ if (bc_atom_to_idx(s, &val, atom))+ goto fail;+ put_u32(bc_buf + pos + 1, val);+ break;+ default:+ break;+ }+ pos += len;+ }++ if (s->byte_swap)+ bc_byte_swap(bc_buf, bc_len);++ dbuf_put(&s->dbuf, bc_buf, bc_len);++ js_free(s->ctx, bc_buf);+ return 0;+ fail:+ js_free(s->ctx, bc_buf);+ return -1;+}++static void JS_WriteString(BCWriterState *s, JSString *p)+{+ int i;+ bc_put_leb128(s, ((uint32_t)p->len << 1) | p->is_wide_char);+ if (p->is_wide_char) {+ for(i = 0; i < p->len; i++)+ bc_put_u16(s, p->u.str16[i]);+ } else {+ dbuf_put(&s->dbuf, p->u.str8, p->len);+ }+}++#ifdef CONFIG_BIGNUM+static int JS_WriteBigNum(BCWriterState *s, JSValueConst obj)+{+ uint32_t tag, tag1;+ int64_t e;+ JSBigFloat *bf = JS_VALUE_GET_PTR(obj);+ bf_t *a = &bf->num;+ size_t len, i, n1, j;+ limb_t v;++ tag = JS_VALUE_GET_TAG(obj);+ switch(tag) {+ case JS_TAG_BIG_INT:+ tag1 = BC_TAG_BIG_INT;+ break;+ case JS_TAG_BIG_FLOAT:+ tag1 = BC_TAG_BIG_FLOAT;+ break;+ case JS_TAG_BIG_DECIMAL:+ tag1 = BC_TAG_BIG_DECIMAL;+ break;+ default:+ abort();+ }+ bc_put_u8(s, tag1);++ /* sign + exponent */+ if (a->expn == BF_EXP_ZERO)+ e = 0;+ else if (a->expn == BF_EXP_INF)+ e = 1;+ else if (a->expn == BF_EXP_NAN)+ e = 2;+ else if (a->expn >= 0)+ e = a->expn + 3;+ else+ e = a->expn;+ e = (e << 1) | a->sign;+ if (e < INT32_MIN || e > INT32_MAX) {+ JS_ThrowInternalError(s->ctx, "bignum exponent is too large");+ return -1;+ }+ bc_put_sleb128(s, e);++ /* mantissa */+ if (a->len != 0) {+ if (tag != JS_TAG_BIG_DECIMAL) {+ i = 0;+ while (i < a->len && a->tab[i] == 0)+ i++;+ assert(i < a->len);+ v = a->tab[i];+ n1 = sizeof(limb_t);+ while ((v & 0xff) == 0) {+ n1--;+ v >>= 8;+ }+ i++;+ len = (a->len - i) * sizeof(limb_t) + n1;+ if (len > INT32_MAX) {+ JS_ThrowInternalError(s->ctx, "bignum is too large");+ return -1;+ }+ bc_put_leb128(s, len);+ /* always saved in byte based little endian representation */+ for(j = 0; j < n1; j++) {+ dbuf_putc(&s->dbuf, v >> (j * 8));+ }+ for(; i < a->len; i++) {+ limb_t v = a->tab[i];+#if LIMB_BITS == 32+#ifdef WORDS_BIGENDIAN+ v = bswap32(v);+#endif+ dbuf_put_u32(&s->dbuf, v);+#else+#ifdef WORDS_BIGENDIAN+ v = bswap64(v);+#endif+ dbuf_put_u64(&s->dbuf, v);+#endif+ }+ } else {+ int bpos, d;+ uint8_t v8;+ size_t i0;+ + /* little endian BCD */+ i = 0;+ while (i < a->len && a->tab[i] == 0)+ i++;+ assert(i < a->len);+ len = a->len * LIMB_DIGITS;+ v = a->tab[i];+ j = 0;+ while ((v % 10) == 0) {+ j++;+ v /= 10;+ }+ len -= j;+ assert(len > 0);+ if (len > INT32_MAX) {+ JS_ThrowInternalError(s->ctx, "bignum is too large");+ return -1;+ }+ bc_put_leb128(s, len);+ + bpos = 0;+ v8 = 0;+ i0 = i;+ for(; i < a->len; i++) {+ if (i != i0) {+ v = a->tab[i];+ j = 0;+ }+ for(; j < LIMB_DIGITS; j++) {+ d = v % 10;+ v /= 10;+ if (bpos == 0) {+ v8 = d;+ bpos = 1;+ } else {+ dbuf_putc(&s->dbuf, v8 | (d << 4));+ bpos = 0;+ }+ }+ }+ /* flush the last digit */+ if (bpos) {+ dbuf_putc(&s->dbuf, v8);+ }+ }+ }+ return 0;+}+#endif /* CONFIG_BIGNUM */++static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj);++static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj)+{+ JSFunctionBytecode *b = JS_VALUE_GET_PTR(obj);+ uint32_t flags;+ int idx, i;+ + bc_put_u8(s, BC_TAG_FUNCTION_BYTECODE);+ flags = idx = 0;+ bc_set_flags(&flags, &idx, b->has_prototype, 1);+ bc_set_flags(&flags, &idx, b->has_simple_parameter_list, 1);+ bc_set_flags(&flags, &idx, b->is_derived_class_constructor, 1);+ bc_set_flags(&flags, &idx, b->need_home_object, 1);+ bc_set_flags(&flags, &idx, b->func_kind, 2);+ bc_set_flags(&flags, &idx, b->new_target_allowed, 1);+ bc_set_flags(&flags, &idx, b->super_call_allowed, 1);+ bc_set_flags(&flags, &idx, b->super_allowed, 1);+ bc_set_flags(&flags, &idx, b->arguments_allowed, 1);+ bc_set_flags(&flags, &idx, b->has_debug, 1);+ bc_set_flags(&flags, &idx, b->backtrace_barrier, 1);+ assert(idx <= 16);+ bc_put_u16(s, flags);+ bc_put_u8(s, b->js_mode);+ bc_put_atom(s, b->func_name);+ + bc_put_leb128(s, b->arg_count);+ bc_put_leb128(s, b->var_count);+ bc_put_leb128(s, b->defined_arg_count);+ bc_put_leb128(s, b->stack_size);+ bc_put_leb128(s, b->closure_var_count);+ bc_put_leb128(s, b->cpool_count);+ bc_put_leb128(s, b->byte_code_len);+ if (b->vardefs) {+ bc_put_leb128(s, b->arg_count + b->var_count);+ for(i = 0; i < b->arg_count + b->var_count; i++) {+ JSVarDef *vd = &b->vardefs[i];+ bc_put_atom(s, vd->var_name);+ bc_put_leb128(s, vd->scope_level);+ bc_put_leb128(s, vd->scope_next + 1);+ flags = idx = 0;+ bc_set_flags(&flags, &idx, vd->var_kind, 4);+ bc_set_flags(&flags, &idx, vd->is_const, 1);+ bc_set_flags(&flags, &idx, vd->is_lexical, 1);+ bc_set_flags(&flags, &idx, vd->is_captured, 1);+ assert(idx <= 8);+ bc_put_u8(s, flags);+ }+ } else {+ bc_put_leb128(s, 0);+ }+ + for(i = 0; i < b->closure_var_count; i++) {+ JSClosureVar *cv = &b->closure_var[i];+ bc_put_atom(s, cv->var_name);+ bc_put_leb128(s, cv->var_idx);+ flags = idx = 0;+ bc_set_flags(&flags, &idx, cv->is_local, 1);+ bc_set_flags(&flags, &idx, cv->is_arg, 1);+ bc_set_flags(&flags, &idx, cv->is_const, 1);+ bc_set_flags(&flags, &idx, cv->is_lexical, 1);+ bc_set_flags(&flags, &idx, cv->var_kind, 4);+ assert(idx <= 8);+ bc_put_u8(s, flags);+ }+ + if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len))+ goto fail;+ + if (b->has_debug) {+ bc_put_atom(s, b->debug.filename);+ bc_put_leb128(s, b->debug.line_num);+ bc_put_leb128(s, b->debug.pc2line_len);+ dbuf_put(&s->dbuf, b->debug.pc2line_buf, b->debug.pc2line_len);+ }+ + for(i = 0; i < b->cpool_count; i++) {+ if (JS_WriteObjectRec(s, b->cpool[i]))+ goto fail;+ }+ return 0;+ fail:+ return -1;+}++static int JS_WriteModule(BCWriterState *s, JSValueConst obj)+{+ JSModuleDef *m = JS_VALUE_GET_PTR(obj);+ int i;+ + bc_put_u8(s, BC_TAG_MODULE);+ bc_put_atom(s, m->module_name);+ + bc_put_leb128(s, m->req_module_entries_count);+ for(i = 0; i < m->req_module_entries_count; i++) {+ JSReqModuleEntry *rme = &m->req_module_entries[i];+ bc_put_atom(s, rme->module_name);+ }+ + bc_put_leb128(s, m->export_entries_count);+ for(i = 0; i < m->export_entries_count; i++) {+ JSExportEntry *me = &m->export_entries[i];+ bc_put_u8(s, me->export_type);+ if (me->export_type == JS_EXPORT_TYPE_LOCAL) {+ bc_put_leb128(s, me->u.local.var_idx);+ } else {+ bc_put_leb128(s, me->u.req_module_idx);+ bc_put_atom(s, me->local_name);+ }+ bc_put_atom(s, me->export_name);+ }+ + bc_put_leb128(s, m->star_export_entries_count);+ for(i = 0; i < m->star_export_entries_count; i++) {+ JSStarExportEntry *se = &m->star_export_entries[i];+ bc_put_leb128(s, se->req_module_idx);+ }+ + bc_put_leb128(s, m->import_entries_count);+ for(i = 0; i < m->import_entries_count; i++) {+ JSImportEntry *mi = &m->import_entries[i];+ bc_put_leb128(s, mi->var_idx);+ bc_put_atom(s, mi->import_name);+ bc_put_leb128(s, mi->req_module_idx);+ }+ + if (JS_WriteObjectRec(s, m->func_obj))+ goto fail;+ return 0;+ fail:+ return -1;+}++static int JS_WriteArray(BCWriterState *s, JSValueConst obj)+{+ JSObject *p = JS_VALUE_GET_OBJ(obj);+ uint32_t i, len;+ JSValue val;+ int ret;+ BOOL is_template;+ + if (s->allow_bytecode && !p->extensible) {+ /* not extensible array: we consider it is a+ template when we are saving bytecode */+ bc_put_u8(s, BC_TAG_TEMPLATE_OBJECT);+ is_template = TRUE;+ } else {+ bc_put_u8(s, BC_TAG_ARRAY);+ is_template = FALSE;+ }+ if (js_get_length32(s->ctx, &len, obj))+ goto fail1;+ bc_put_leb128(s, len);+ for(i = 0; i < len; i++) {+ val = JS_GetPropertyUint32(s->ctx, obj, i);+ if (JS_IsException(val))+ goto fail1;+ ret = JS_WriteObjectRec(s, val);+ JS_FreeValue(s->ctx, val);+ if (ret)+ goto fail1;+ }+ if (is_template) {+ val = JS_GetProperty(s->ctx, obj, JS_ATOM_raw);+ if (JS_IsException(val))+ goto fail1;+ ret = JS_WriteObjectRec(s, val);+ JS_FreeValue(s->ctx, val);+ if (ret)+ goto fail1;+ }+ return 0;+ fail1:+ return -1;+}++static int JS_WriteObjectTag(BCWriterState *s, JSValueConst obj)+{+ JSObject *p = JS_VALUE_GET_OBJ(obj);+ uint32_t i, prop_count;+ JSShape *sh;+ JSShapeProperty *pr;+ int pass;+ JSAtom atom;++ bc_put_u8(s, BC_TAG_OBJECT);+ prop_count = 0;+ sh = p->shape;+ for(pass = 0; pass < 2; pass++) {+ if (pass == 1)+ bc_put_leb128(s, prop_count);+ for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {+ atom = pr->atom;+ if (atom != JS_ATOM_NULL &&+ JS_AtomIsString(s->ctx, atom) &&+ (pr->flags & JS_PROP_ENUMERABLE)) {+ if (pr->flags & JS_PROP_TMASK) {+ JS_ThrowTypeError(s->ctx, "only value properties are supported");+ goto fail;+ }+ if (pass == 0) {+ prop_count++;+ } else {+ bc_put_atom(s, atom);+ if (JS_WriteObjectRec(s, p->prop[i].u.value))+ goto fail;+ }+ }+ }+ }+ return 0;+ fail:+ return -1;+}++static int JS_WriteTypedArray(BCWriterState *s, JSValueConst obj)+{+ JSObject *p = JS_VALUE_GET_OBJ(obj);+ JSTypedArray *ta = p->u.typed_array;++ bc_put_u8(s, BC_TAG_TYPED_ARRAY);+ bc_put_u8(s, p->class_id - JS_CLASS_UINT8C_ARRAY);+ bc_put_leb128(s, p->u.array.count);+ bc_put_leb128(s, ta->offset);+ if (JS_WriteObjectRec(s, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)))+ return -1;+ return 0;+}++static int JS_WriteArrayBuffer(BCWriterState *s, JSValueConst obj)+{+ JSObject *p = JS_VALUE_GET_OBJ(obj);+ JSArrayBuffer *abuf = p->u.array_buffer;+ if (abuf->detached) {+ JS_ThrowTypeErrorDetachedArrayBuffer(s->ctx);+ return -1;+ }+ bc_put_u8(s, BC_TAG_ARRAY_BUFFER);+ bc_put_leb128(s, abuf->byte_length);+ dbuf_put(&s->dbuf, abuf->data, abuf->byte_length);+ return 0;+}++static int JS_WriteSharedArrayBuffer(BCWriterState *s, JSValueConst obj)+{+ JSObject *p = JS_VALUE_GET_OBJ(obj);+ JSArrayBuffer *abuf = p->u.array_buffer;+ assert(!abuf->detached); /* SharedArrayBuffer are never detached */+ bc_put_u8(s, BC_TAG_SHARED_ARRAY_BUFFER);+ bc_put_leb128(s, abuf->byte_length);+ bc_put_u64(s, (uintptr_t)abuf->data);+ if (js_resize_array(s->ctx, (void **)&s->sab_tab, sizeof(s->sab_tab[0]),+ &s->sab_tab_size, s->sab_tab_len + 1))+ return -1;+ /* keep the SAB pointer so that the user can clone it or free it */+ s->sab_tab[s->sab_tab_len++] = abuf->data;+ return 0;+}++static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj)+{+ uint32_t tag;++ if (js_check_stack_overflow(s->ctx->rt, 0)) {+ JS_ThrowStackOverflow(s->ctx);+ return -1;+ }++ tag = JS_VALUE_GET_NORM_TAG(obj);+ switch(tag) {+ case JS_TAG_NULL:+ bc_put_u8(s, BC_TAG_NULL);+ break;+ case JS_TAG_UNDEFINED:+ bc_put_u8(s, BC_TAG_UNDEFINED);+ break;+ case JS_TAG_BOOL:+ bc_put_u8(s, BC_TAG_BOOL_FALSE + JS_VALUE_GET_INT(obj));+ break;+ case JS_TAG_INT:+ bc_put_u8(s, BC_TAG_INT32);+ bc_put_sleb128(s, JS_VALUE_GET_INT(obj));+ break;+ case JS_TAG_FLOAT64:+ {+ JSFloat64Union u;+ bc_put_u8(s, BC_TAG_FLOAT64);+ u.d = JS_VALUE_GET_FLOAT64(obj);+ bc_put_u64(s, u.u64);+ }+ break;+ case JS_TAG_STRING:+ {+ JSString *p = JS_VALUE_GET_STRING(obj);+ bc_put_u8(s, BC_TAG_STRING);+ JS_WriteString(s, p);+ }+ break;+ case JS_TAG_FUNCTION_BYTECODE:+ if (!s->allow_bytecode)+ goto invalid_tag;+ if (JS_WriteFunctionTag(s, obj))+ goto fail;+ break;+ case JS_TAG_MODULE:+ if (!s->allow_bytecode)+ goto invalid_tag;+ if (JS_WriteModule(s, obj))+ goto fail;+ break;+ case JS_TAG_OBJECT:+ {+ JSObject *p = JS_VALUE_GET_OBJ(obj);+ int ret, idx;+ + if (s->allow_reference) {+ idx = js_object_list_find(s->ctx, &s->object_list, p);+ if (idx >= 0) {+ bc_put_u8(s, BC_TAG_OBJECT_REFERENCE);+ bc_put_leb128(s, idx);+ break;+ } else {+ if (js_object_list_add(s->ctx, &s->object_list, p))+ goto fail;+ }+ } else {+ if (p->tmp_mark) {+ JS_ThrowTypeError(s->ctx, "circular reference");+ goto fail;+ }+ p->tmp_mark = 1;+ }+ switch(p->class_id) {+ case JS_CLASS_ARRAY:+ ret = JS_WriteArray(s, obj);+ break;+ case JS_CLASS_OBJECT:+ ret = JS_WriteObjectTag(s, obj);+ break;+ case JS_CLASS_ARRAY_BUFFER:+ ret = JS_WriteArrayBuffer(s, obj);+ break;+ case JS_CLASS_SHARED_ARRAY_BUFFER:+ if (!s->allow_sab)+ goto invalid_tag;+ ret = JS_WriteSharedArrayBuffer(s, obj);+ break;+ case JS_CLASS_DATE:+ bc_put_u8(s, BC_TAG_DATE);+ ret = JS_WriteObjectRec(s, p->u.object_data);+ break;+ case JS_CLASS_NUMBER:+ case JS_CLASS_STRING:+ case JS_CLASS_BOOLEAN:+#ifdef CONFIG_BIGNUM+ case JS_CLASS_BIG_INT:+ case JS_CLASS_BIG_FLOAT:+ case JS_CLASS_BIG_DECIMAL:+#endif+ bc_put_u8(s, BC_TAG_OBJECT_VALUE);+ ret = JS_WriteObjectRec(s, p->u.object_data);+ break;+ default:+ if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {+ ret = JS_WriteTypedArray(s, obj);+ } else {+ JS_ThrowTypeError(s->ctx, "unsupported object class");+ ret = -1;+ }+ break;+ }+ p->tmp_mark = 0;+ if (ret)+ goto fail;+ }+ break;+#ifdef CONFIG_BIGNUM+ case JS_TAG_BIG_INT:+ case JS_TAG_BIG_FLOAT:+ case JS_TAG_BIG_DECIMAL:+ if (JS_WriteBigNum(s, obj))+ goto fail;+ break;+#endif+ default:+ invalid_tag:+ JS_ThrowInternalError(s->ctx, "unsupported tag (%d)", tag);+ goto fail;+ }+ return 0;++ fail:+ return -1;+}++/* create the atom table */+static int JS_WriteObjectAtoms(BCWriterState *s)+{+ JSRuntime *rt = s->ctx->rt;+ DynBuf dbuf1;+ int i, atoms_size;+ uint8_t version;++ dbuf1 = s->dbuf;+ js_dbuf_init(s->ctx, &s->dbuf);++ version = BC_VERSION;+ if (s->byte_swap)+ version ^= BC_BE_VERSION;+ bc_put_u8(s, version);++ bc_put_leb128(s, s->idx_to_atom_count);+ for(i = 0; i < s->idx_to_atom_count; i++) {+ JSAtomStruct *p = rt->atom_array[s->idx_to_atom[i]];+ JS_WriteString(s, p);+ }+ /* XXX: should check for OOM in above phase */++ /* move the atoms at the start */+ /* XXX: could just append dbuf1 data, but it uses more memory if+ dbuf1 is larger than dbuf */+ atoms_size = s->dbuf.size;+ if (dbuf_realloc(&dbuf1, dbuf1.size + atoms_size))+ goto fail;+ memmove(dbuf1.buf + atoms_size, dbuf1.buf, dbuf1.size);+ memcpy(dbuf1.buf, s->dbuf.buf, atoms_size);+ dbuf1.size += atoms_size;+ dbuf_free(&s->dbuf);+ s->dbuf = dbuf1;+ return 0;+ fail:+ dbuf_free(&dbuf1);+ return -1;+}++uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj,+ int flags, uint8_t ***psab_tab, size_t *psab_tab_len)+{+ BCWriterState ss, *s = &ss;++ memset(s, 0, sizeof(*s));+ s->ctx = ctx;+ /* XXX: byte swapped output is untested */+ s->byte_swap = ((flags & JS_WRITE_OBJ_BSWAP) != 0);+ s->allow_bytecode = ((flags & JS_WRITE_OBJ_BYTECODE) != 0);+ s->allow_sab = ((flags & JS_WRITE_OBJ_SAB) != 0);+ s->allow_reference = ((flags & JS_WRITE_OBJ_REFERENCE) != 0);+ /* XXX: could use a different version when bytecode is included */+ if (s->allow_bytecode)+ s->first_atom = JS_ATOM_END;+ else+ s->first_atom = 1;+ js_dbuf_init(ctx, &s->dbuf);+ js_object_list_init(&s->object_list);+ + if (JS_WriteObjectRec(s, obj))+ goto fail;+ if (JS_WriteObjectAtoms(s))+ goto fail;+ js_object_list_end(ctx, &s->object_list);+ js_free(ctx, s->atom_to_idx);+ js_free(ctx, s->idx_to_atom);+ *psize = s->dbuf.size;+ if (psab_tab)+ *psab_tab = s->sab_tab;+ if (psab_tab_len)+ *psab_tab_len = s->sab_tab_len;+ return s->dbuf.buf;+ fail:+ js_object_list_end(ctx, &s->object_list);+ js_free(ctx, s->atom_to_idx);+ js_free(ctx, s->idx_to_atom);+ dbuf_free(&s->dbuf);+ *psize = 0;+ if (psab_tab)+ *psab_tab = NULL;+ if (psab_tab_len)+ *psab_tab_len = 0;+ return NULL;+}++uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj,+ int flags)+{+ return JS_WriteObject2(ctx, psize, obj, flags, NULL, NULL);+}++typedef struct BCReaderState {+ JSContext *ctx;+ const uint8_t *buf_start, *ptr, *buf_end;+ uint32_t first_atom;+ uint32_t idx_to_atom_count;+ JSAtom *idx_to_atom;+ int error_state;+ BOOL allow_sab : 8;+ BOOL allow_bytecode : 8;+ BOOL is_rom_data : 8;+ BOOL allow_reference : 8;+ /* object references */+ JSObject **objects;+ int objects_count;+ int objects_size;+ +#ifdef DUMP_READ_OBJECT+ const uint8_t *ptr_last;+ int level;+#endif+} BCReaderState;++#ifdef DUMP_READ_OBJECT+static void __attribute__((format(printf, 2, 3))) bc_read_trace(BCReaderState *s, const char *fmt, ...) {+ va_list ap;+ int i, n, n0;++ if (!s->ptr_last)+ s->ptr_last = s->buf_start;++ n = n0 = 0;+ if (s->ptr > s->ptr_last || s->ptr == s->buf_start) {+ n0 = printf("%04x: ", (int)(s->ptr_last - s->buf_start));+ n += n0;+ }+ for (i = 0; s->ptr_last < s->ptr; i++) {+ if ((i & 7) == 0 && i > 0) {+ printf("\n%*s", n0, "");+ n = n0;+ }+ n += printf(" %02x", *s->ptr_last++);+ }+ if (*fmt == '}')+ s->level--;+ if (n < 32 + s->level * 2) {+ printf("%*s", 32 + s->level * 2 - n, "");+ }+ va_start(ap, fmt);+ vfprintf(stdout, fmt, ap);+ va_end(ap);+ if (strchr(fmt, '{'))+ s->level++;+}+#else+#define bc_read_trace(...)+#endif++static int bc_read_error_end(BCReaderState *s)+{+ if (!s->error_state) {+ JS_ThrowSyntaxError(s->ctx, "read after the end of the buffer");+ }+ return s->error_state = -1;+}++static int bc_get_u8(BCReaderState *s, uint8_t *pval)+{+ if (unlikely(s->buf_end - s->ptr < 1)) {+ *pval = 0; /* avoid warning */+ return bc_read_error_end(s);+ }+ *pval = *s->ptr++;+ return 0;+}++static int bc_get_u16(BCReaderState *s, uint16_t *pval)+{+ if (unlikely(s->buf_end - s->ptr < 2)) {+ *pval = 0; /* avoid warning */+ return bc_read_error_end(s);+ }+ *pval = get_u16(s->ptr);+ s->ptr += 2;+ return 0;+}++static __maybe_unused int bc_get_u32(BCReaderState *s, uint32_t *pval)+{+ if (unlikely(s->buf_end - s->ptr < 4)) {+ *pval = 0; /* avoid warning */+ return bc_read_error_end(s);+ }+ *pval = get_u32(s->ptr);+ s->ptr += 4;+ return 0;+}++static int bc_get_u64(BCReaderState *s, uint64_t *pval)+{+ if (unlikely(s->buf_end - s->ptr < 8)) {+ *pval = 0; /* avoid warning */+ return bc_read_error_end(s);+ }+ *pval = get_u64(s->ptr);+ s->ptr += 8;+ return 0;+}++static int bc_get_leb128(BCReaderState *s, uint32_t *pval)+{+ int ret;+ ret = get_leb128(pval, s->ptr, s->buf_end);+ if (unlikely(ret < 0))+ return bc_read_error_end(s);+ s->ptr += ret;+ return 0;+}++static int bc_get_sleb128(BCReaderState *s, int32_t *pval)+{+ int ret;+ ret = get_sleb128(pval, s->ptr, s->buf_end);+ if (unlikely(ret < 0))+ return bc_read_error_end(s);+ s->ptr += ret;+ return 0;+}++/* XXX: used to read an `int` with a positive value */+static int bc_get_leb128_int(BCReaderState *s, int *pval)+{+ return bc_get_leb128(s, (uint32_t *)pval);+}++static int bc_get_leb128_u16(BCReaderState *s, uint16_t *pval)+{+ uint32_t val;+ if (bc_get_leb128(s, &val)) {+ *pval = 0;+ return -1;+ }+ *pval = val;+ return 0;+}++static int bc_get_buf(BCReaderState *s, uint8_t *buf, uint32_t buf_len)+{+ if (buf_len != 0) {+ if (unlikely(!buf || s->buf_end - s->ptr < buf_len))+ return bc_read_error_end(s);+ memcpy(buf, s->ptr, buf_len);+ s->ptr += buf_len;+ }+ return 0;+}++static int bc_idx_to_atom(BCReaderState *s, JSAtom *patom, uint32_t idx)+{+ JSAtom atom;++ if (__JS_AtomIsTaggedInt(idx)) {+ atom = idx;+ } else if (idx < s->first_atom) {+ atom = JS_DupAtom(s->ctx, idx);+ } else {+ idx -= s->first_atom;+ if (idx >= s->idx_to_atom_count) {+ JS_ThrowSyntaxError(s->ctx, "invalid atom index (pos=%u)",+ (unsigned int)(s->ptr - s->buf_start));+ *patom = JS_ATOM_NULL;+ return s->error_state = -1;+ }+ atom = JS_DupAtom(s->ctx, s->idx_to_atom[idx]);+ }+ *patom = atom;+ return 0;+}++static int bc_get_atom(BCReaderState *s, JSAtom *patom)+{+ uint32_t v;+ if (bc_get_leb128(s, &v))+ return -1;+ if (v & 1) {+ *patom = __JS_AtomFromUInt32(v >> 1);+ return 0;+ } else {+ return bc_idx_to_atom(s, patom, v >> 1);+ }+}++static JSString *JS_ReadString(BCReaderState *s)+{+ uint32_t len;+ size_t size;+ BOOL is_wide_char;+ JSString *p;++ if (bc_get_leb128(s, &len))+ return NULL;+ is_wide_char = len & 1;+ len >>= 1;+ p = js_alloc_string(s->ctx, len, is_wide_char);+ if (!p) {+ s->error_state = -1;+ return NULL;+ }+ size = (size_t)len << is_wide_char;+ if ((s->buf_end - s->ptr) < size) {+ bc_read_error_end(s);+ js_free_string(s->ctx->rt, p);+ return NULL;+ }+ memcpy(p->u.str8, s->ptr, size);+ s->ptr += size;+ if (!is_wide_char) {+ p->u.str8[size] = '\0'; /* add the trailing zero for 8 bit strings */+ }+#ifdef DUMP_READ_OBJECT+ JS_DumpString(s->ctx->rt, p); printf("\n");+#endif+ return p;+}++static uint32_t bc_get_flags(uint32_t flags, int *pidx, int n)+{+ uint32_t val;+ /* XXX: this does not work for n == 32 */+ val = (flags >> *pidx) & ((1U << n) - 1);+ *pidx += n;+ return val;+}++static int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b,+ int byte_code_offset, uint32_t bc_len)+{+ uint8_t *bc_buf;+ int pos, len, op;+ JSAtom atom;+ uint32_t idx;++ if (s->is_rom_data) {+ /* directly use the input buffer */+ if (unlikely(s->buf_end - s->ptr < bc_len))+ return bc_read_error_end(s);+ bc_buf = (uint8_t *)s->ptr;+ s->ptr += bc_len;+ } else {+ bc_buf = (void *)((uint8_t*)b + byte_code_offset);+ if (bc_get_buf(s, bc_buf, bc_len))+ return -1;+ }+ b->byte_code_buf = bc_buf;++ pos = 0;+ while (pos < bc_len) {+ op = bc_buf[pos];+ len = short_opcode_info(op).size;+ switch(short_opcode_info(op).fmt) {+ case OP_FMT_atom:+ case OP_FMT_atom_u8:+ case OP_FMT_atom_u16:+ case OP_FMT_atom_label_u8:+ case OP_FMT_atom_label_u16:+ idx = get_u32(bc_buf + pos + 1);+ if (s->is_rom_data) {+ /* just increment the reference count of the atom */+ JS_DupAtom(s->ctx, (JSAtom)idx);+ } else {+ if (bc_idx_to_atom(s, &atom, idx)) {+ /* Note: the atoms will be freed up to this position */+ b->byte_code_len = pos;+ return -1;+ }+ put_u32(bc_buf + pos + 1, atom);+#ifdef DUMP_READ_OBJECT+ bc_read_trace(s, "at %d, fixup atom: ", pos + 1); print_atom(s->ctx, atom); printf("\n");+#endif+ }+ break;+ default:+ break;+ }+ pos += len;+ }+ return 0;+}++#ifdef CONFIG_BIGNUM+static JSValue JS_ReadBigNum(BCReaderState *s, int tag)+{+ JSValue obj = JS_UNDEFINED;+ uint8_t v8;+ int32_t e;+ uint32_t len;+ limb_t l, i, n, j;+ JSBigFloat *p;+ limb_t v;+ bf_t *a;+ int bpos, d;+ + p = js_new_bf(s->ctx);+ if (!p)+ goto fail;+ switch(tag) {+ case BC_TAG_BIG_INT:+ obj = JS_MKPTR(JS_TAG_BIG_INT, p);+ break;+ case BC_TAG_BIG_FLOAT:+ obj = JS_MKPTR(JS_TAG_BIG_FLOAT, p);+ break;+ case BC_TAG_BIG_DECIMAL:+ obj = JS_MKPTR(JS_TAG_BIG_DECIMAL, p);+ break;+ default:+ abort();+ }++ /* sign + exponent */+ if (bc_get_sleb128(s, &e))+ goto fail;++ a = &p->num;+ a->sign = e & 1;+ e >>= 1;+ if (e == 0)+ a->expn = BF_EXP_ZERO;+ else if (e == 1)+ a->expn = BF_EXP_INF;+ else if (e == 2)+ a->expn = BF_EXP_NAN;+ else if (e >= 3)+ a->expn = e - 3;+ else+ a->expn = e;++ /* mantissa */+ if (a->expn != BF_EXP_ZERO &&+ a->expn != BF_EXP_INF &&+ a->expn != BF_EXP_NAN) {+ if (bc_get_leb128(s, &len))+ goto fail;+ bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len);+ if (len == 0) {+ JS_ThrowInternalError(s->ctx, "invalid bignum length");+ goto fail;+ }+ if (tag != BC_TAG_BIG_DECIMAL)+ l = (len + sizeof(limb_t) - 1) / sizeof(limb_t);+ else+ l = (len + LIMB_DIGITS - 1) / LIMB_DIGITS;+ if (bf_resize(a, l)) {+ JS_ThrowOutOfMemory(s->ctx);+ goto fail;+ }+ if (tag != BC_TAG_BIG_DECIMAL) {+ n = len & (sizeof(limb_t) - 1);+ if (n != 0) {+ v = 0;+ for(i = 0; i < n; i++) {+ if (bc_get_u8(s, &v8))+ goto fail;+ v |= (limb_t)v8 << ((sizeof(limb_t) - n + i) * 8);+ }+ a->tab[0] = v;+ i = 1;+ } else {+ i = 0;+ }+ for(; i < l; i++) {+#if LIMB_BITS == 32+ if (bc_get_u32(s, &v))+ goto fail;+#ifdef WORDS_BIGENDIAN+ v = bswap32(v);+#endif+#else+ if (bc_get_u64(s, &v))+ goto fail;+#ifdef WORDS_BIGENDIAN+ v = bswap64(v);+#endif+#endif+ a->tab[i] = v;+ }+ } else {+ bpos = 0;+ for(i = 0; i < l; i++) {+ if (i == 0 && (n = len % LIMB_DIGITS) != 0) {+ j = LIMB_DIGITS - n;+ } else {+ j = 0;+ }+ v = 0;+ for(; j < LIMB_DIGITS; j++) {+ if (bpos == 0) {+ if (bc_get_u8(s, &v8))+ goto fail;+ d = v8 & 0xf;+ bpos = 1;+ } else {+ d = v8 >> 4;+ bpos = 0;+ }+ if (d >= 10) {+ JS_ThrowInternalError(s->ctx, "invalid digit");+ goto fail;+ }+ v += mp_pow_dec[j] * d;+ }+ a->tab[i] = v;+ }+ }+ }+ bc_read_trace(s, "}\n");+ return obj;+ fail:+ JS_FreeValue(s->ctx, obj);+ return JS_EXCEPTION;+}+#endif /* CONFIG_BIGNUM */++static JSValue JS_ReadObjectRec(BCReaderState *s);++static int BC_add_object_ref1(BCReaderState *s, JSObject *p)+{+ if (s->allow_reference) {+ if (js_resize_array(s->ctx, (void *)&s->objects,+ sizeof(s->objects[0]),+ &s->objects_size, s->objects_count + 1))+ return -1;+ s->objects[s->objects_count++] = p;+ }+ return 0;+}++static int BC_add_object_ref(BCReaderState *s, JSValueConst obj)+{+ return BC_add_object_ref1(s, JS_VALUE_GET_OBJ(obj));+}++static JSValue JS_ReadFunctionTag(BCReaderState *s)+{+ JSContext *ctx = s->ctx;+ JSFunctionBytecode bc, *b;+ JSValue obj = JS_UNDEFINED;+ uint16_t v16;+ uint8_t v8;+ int idx, i, local_count;+ int function_size, cpool_offset, byte_code_offset;+ int closure_var_offset, vardefs_offset;++ memset(&bc, 0, sizeof(bc));+ bc.header.ref_count = 1;+ //bc.gc_header.mark = 0;++ if (bc_get_u16(s, &v16))+ goto fail;+ idx = 0;+ bc.has_prototype = bc_get_flags(v16, &idx, 1);+ bc.has_simple_parameter_list = bc_get_flags(v16, &idx, 1);+ bc.is_derived_class_constructor = bc_get_flags(v16, &idx, 1);+ bc.need_home_object = bc_get_flags(v16, &idx, 1);+ bc.func_kind = bc_get_flags(v16, &idx, 2);+ bc.new_target_allowed = bc_get_flags(v16, &idx, 1);+ bc.super_call_allowed = bc_get_flags(v16, &idx, 1);+ bc.super_allowed = bc_get_flags(v16, &idx, 1);+ bc.arguments_allowed = bc_get_flags(v16, &idx, 1);+ bc.has_debug = bc_get_flags(v16, &idx, 1);+ bc.backtrace_barrier = bc_get_flags(v16, &idx, 1);+ bc.read_only_bytecode = s->is_rom_data;+ if (bc_get_u8(s, &v8))+ goto fail;+ bc.js_mode = v8;+ if (bc_get_atom(s, &bc.func_name)) //@ atom leak if failure+ goto fail;+ if (bc_get_leb128_u16(s, &bc.arg_count))+ goto fail;+ if (bc_get_leb128_u16(s, &bc.var_count))+ goto fail;+ if (bc_get_leb128_u16(s, &bc.defined_arg_count))+ goto fail;+ if (bc_get_leb128_u16(s, &bc.stack_size))+ goto fail;+ if (bc_get_leb128_int(s, &bc.closure_var_count))+ goto fail;+ if (bc_get_leb128_int(s, &bc.cpool_count))+ goto fail;+ if (bc_get_leb128_int(s, &bc.byte_code_len))+ goto fail;+ if (bc_get_leb128_int(s, &local_count))+ goto fail;++ if (bc.has_debug) {+ function_size = sizeof(*b);+ } else {+ function_size = offsetof(JSFunctionBytecode, debug);+ }+ cpool_offset = function_size;+ function_size += bc.cpool_count * sizeof(*bc.cpool);+ vardefs_offset = function_size;+ function_size += local_count * sizeof(*bc.vardefs);+ closure_var_offset = function_size;+ function_size += bc.closure_var_count * sizeof(*bc.closure_var);+ byte_code_offset = function_size;+ if (!bc.read_only_bytecode) {+ function_size += bc.byte_code_len;+ }++ b = js_mallocz(ctx, function_size);+ if (!b)+ return JS_EXCEPTION;+ + memcpy(b, &bc, offsetof(JSFunctionBytecode, debug));+ b->header.ref_count = 1;+ add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);+ + obj = JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b);++#ifdef DUMP_READ_OBJECT+ bc_read_trace(s, "name: "); print_atom(s->ctx, b->func_name); printf("\n");+#endif+ bc_read_trace(s, "args=%d vars=%d defargs=%d closures=%d cpool=%d\n",+ b->arg_count, b->var_count, b->defined_arg_count,+ b->closure_var_count, b->cpool_count);+ bc_read_trace(s, "stack=%d bclen=%d locals=%d\n",+ b->stack_size, b->byte_code_len, local_count);++ if (local_count != 0) {+ bc_read_trace(s, "vars {\n");+ b->vardefs = (void *)((uint8_t*)b + vardefs_offset);+ for(i = 0; i < local_count; i++) {+ JSVarDef *vd = &b->vardefs[i];+ if (bc_get_atom(s, &vd->var_name))+ goto fail;+ if (bc_get_leb128_int(s, &vd->scope_level))+ goto fail;+ if (bc_get_leb128_int(s, &vd->scope_next))+ goto fail;+ vd->scope_next--;+ if (bc_get_u8(s, &v8))+ goto fail;+ idx = 0;+ vd->var_kind = bc_get_flags(v8, &idx, 4);+ vd->is_const = bc_get_flags(v8, &idx, 1);+ vd->is_lexical = bc_get_flags(v8, &idx, 1);+ vd->is_captured = bc_get_flags(v8, &idx, 1);+#ifdef DUMP_READ_OBJECT+ bc_read_trace(s, "name: "); print_atom(s->ctx, vd->var_name); printf("\n");+#endif+ }+ bc_read_trace(s, "}\n");+ }+ if (b->closure_var_count != 0) {+ bc_read_trace(s, "closure vars {\n");+ b->closure_var = (void *)((uint8_t*)b + closure_var_offset);+ for(i = 0; i < b->closure_var_count; i++) {+ JSClosureVar *cv = &b->closure_var[i];+ int var_idx;+ if (bc_get_atom(s, &cv->var_name))+ goto fail;+ if (bc_get_leb128_int(s, &var_idx))+ goto fail;+ cv->var_idx = var_idx;+ if (bc_get_u8(s, &v8))+ goto fail;+ idx = 0;+ cv->is_local = bc_get_flags(v8, &idx, 1);+ cv->is_arg = bc_get_flags(v8, &idx, 1);+ cv->is_const = bc_get_flags(v8, &idx, 1);+ cv->is_lexical = bc_get_flags(v8, &idx, 1);+ cv->var_kind = bc_get_flags(v8, &idx, 4);+#ifdef DUMP_READ_OBJECT+ bc_read_trace(s, "name: "); print_atom(s->ctx, cv->var_name); printf("\n");+#endif+ }+ bc_read_trace(s, "}\n");+ }+ {+ bc_read_trace(s, "bytecode {\n");+ if (JS_ReadFunctionBytecode(s, b, byte_code_offset, b->byte_code_len))+ goto fail;+ bc_read_trace(s, "}\n");+ }+ if (b->has_debug) {+ /* read optional debug information */+ bc_read_trace(s, "debug {\n");+ if (bc_get_atom(s, &b->debug.filename))+ goto fail;+ if (bc_get_leb128_int(s, &b->debug.line_num))+ goto fail;+ if (bc_get_leb128_int(s, &b->debug.pc2line_len))+ goto fail;+ if (b->debug.pc2line_len) {+ b->debug.pc2line_buf = js_mallocz(ctx, b->debug.pc2line_len);+ if (!b->debug.pc2line_buf)+ goto fail;+ if (bc_get_buf(s, b->debug.pc2line_buf, b->debug.pc2line_len))+ goto fail;+ }+#ifdef DUMP_READ_OBJECT+ bc_read_trace(s, "filename: "); print_atom(s->ctx, b->debug.filename); printf("\n");+#endif+ bc_read_trace(s, "}\n");+ }+ if (b->cpool_count != 0) {+ bc_read_trace(s, "cpool {\n");+ b->cpool = (void *)((uint8_t*)b + cpool_offset);+ for(i = 0; i < b->cpool_count; i++) {+ JSValue val;+ val = JS_ReadObjectRec(s);+ if (JS_IsException(val))+ goto fail;+ b->cpool[i] = val;+ }+ bc_read_trace(s, "}\n");+ }+ b->realm = JS_DupContext(ctx);+ return obj;+ fail:+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++static JSValue JS_ReadModule(BCReaderState *s)+{+ JSContext *ctx = s->ctx;+ JSValue obj;+ JSModuleDef *m = NULL;+ JSAtom module_name;+ int i;+ uint8_t v8;+ + if (bc_get_atom(s, &module_name))+ goto fail;+#ifdef DUMP_READ_OBJECT+ bc_read_trace(s, "name: "); print_atom(s->ctx, module_name); printf("\n");+#endif+ m = js_new_module_def(ctx, module_name);+ if (!m)+ goto fail;+ obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));+ if (bc_get_leb128_int(s, &m->req_module_entries_count))+ goto fail;+ if (m->req_module_entries_count != 0) {+ m->req_module_entries_size = m->req_module_entries_count;+ m->req_module_entries = js_mallocz(ctx, sizeof(m->req_module_entries[0]) * m->req_module_entries_size);+ if (!m->req_module_entries)+ goto fail;+ for(i = 0; i < m->req_module_entries_count; i++) {+ JSReqModuleEntry *rme = &m->req_module_entries[i];+ if (bc_get_atom(s, &rme->module_name))+ goto fail;+ }+ }++ if (bc_get_leb128_int(s, &m->export_entries_count))+ goto fail;+ if (m->export_entries_count != 0) {+ m->export_entries_size = m->export_entries_count;+ m->export_entries = js_mallocz(ctx, sizeof(m->export_entries[0]) * m->export_entries_size);+ if (!m->export_entries)+ goto fail;+ for(i = 0; i < m->export_entries_count; i++) {+ JSExportEntry *me = &m->export_entries[i];+ if (bc_get_u8(s, &v8))+ goto fail;+ me->export_type = v8;+ if (me->export_type == JS_EXPORT_TYPE_LOCAL) {+ if (bc_get_leb128_int(s, &me->u.local.var_idx))+ goto fail;+ } else {+ if (bc_get_leb128_int(s, &me->u.req_module_idx))+ goto fail;+ if (bc_get_atom(s, &me->local_name))+ goto fail;+ }+ if (bc_get_atom(s, &me->export_name))+ goto fail;+ }+ }++ if (bc_get_leb128_int(s, &m->star_export_entries_count))+ goto fail;+ if (m->star_export_entries_count != 0) {+ m->star_export_entries_size = m->star_export_entries_count;+ m->star_export_entries = js_mallocz(ctx, sizeof(m->star_export_entries[0]) * m->star_export_entries_size);+ if (!m->star_export_entries)+ goto fail;+ for(i = 0; i < m->star_export_entries_count; i++) {+ JSStarExportEntry *se = &m->star_export_entries[i];+ if (bc_get_leb128_int(s, &se->req_module_idx))+ goto fail;+ }+ }++ if (bc_get_leb128_int(s, &m->import_entries_count))+ goto fail;+ if (m->import_entries_count != 0) {+ m->import_entries_size = m->import_entries_count;+ m->import_entries = js_mallocz(ctx, sizeof(m->import_entries[0]) * m->import_entries_size);+ if (!m->import_entries)+ goto fail;+ for(i = 0; i < m->import_entries_count; i++) {+ JSImportEntry *mi = &m->import_entries[i];+ if (bc_get_leb128_int(s, &mi->var_idx))+ goto fail;+ if (bc_get_atom(s, &mi->import_name))+ goto fail;+ if (bc_get_leb128_int(s, &mi->req_module_idx))+ goto fail;+ }+ }++ m->func_obj = JS_ReadObjectRec(s);+ if (JS_IsException(m->func_obj))+ goto fail;+ return obj;+ fail:+ if (m) {+ js_free_module_def(ctx, m);+ }+ return JS_EXCEPTION;+}++static JSValue JS_ReadObjectTag(BCReaderState *s)+{+ JSContext *ctx = s->ctx;+ JSValue obj;+ uint32_t prop_count, i;+ JSAtom atom;+ JSValue val;+ int ret;+ + obj = JS_NewObject(ctx);+ if (BC_add_object_ref(s, obj))+ goto fail;+ if (bc_get_leb128(s, &prop_count))+ goto fail;+ for(i = 0; i < prop_count; i++) {+ if (bc_get_atom(s, &atom))+ goto fail;+#ifdef DUMP_READ_OBJECT+ bc_read_trace(s, "propname: "); print_atom(s->ctx, atom); printf("\n");+#endif+ val = JS_ReadObjectRec(s);+ if (JS_IsException(val)) {+ JS_FreeAtom(ctx, atom);+ goto fail;+ }+ ret = JS_DefinePropertyValue(ctx, obj, atom, val, JS_PROP_C_W_E);+ JS_FreeAtom(ctx, atom);+ if (ret < 0)+ goto fail;+ }+ return obj;+ fail:+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++static JSValue JS_ReadArray(BCReaderState *s, int tag)+{+ JSContext *ctx = s->ctx;+ JSValue obj;+ uint32_t len, i;+ JSValue val;+ int ret, prop_flags;+ BOOL is_template;++ obj = JS_NewArray(ctx);+ if (BC_add_object_ref(s, obj))+ goto fail;+ is_template = (tag == BC_TAG_TEMPLATE_OBJECT);+ if (bc_get_leb128(s, &len))+ goto fail;+ for(i = 0; i < len; i++) {+ val = JS_ReadObjectRec(s);+ if (JS_IsException(val))+ goto fail;+ if (is_template)+ prop_flags = JS_PROP_ENUMERABLE;+ else+ prop_flags = JS_PROP_C_W_E;+ ret = JS_DefinePropertyValueUint32(ctx, obj, i, val,+ prop_flags);+ if (ret < 0)+ goto fail;+ }+ if (is_template) {+ val = JS_ReadObjectRec(s);+ if (JS_IsException(val))+ goto fail;+ if (!JS_IsUndefined(val)) {+ ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_raw, val, 0);+ if (ret < 0)+ goto fail;+ }+ JS_PreventExtensions(ctx, obj);+ }+ return obj;+ fail:+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++static JSValue JS_ReadTypedArray(BCReaderState *s)+{+ JSContext *ctx = s->ctx;+ JSValue obj = JS_UNDEFINED, array_buffer = JS_UNDEFINED;+ uint8_t array_tag;+ JSValueConst args[3];+ uint32_t offset, len, idx;+ + if (bc_get_u8(s, &array_tag))+ return JS_EXCEPTION;+ if (array_tag >= JS_TYPED_ARRAY_COUNT)+ return JS_ThrowTypeError(ctx, "invalid typed array");+ if (bc_get_leb128(s, &len))+ return JS_EXCEPTION;+ if (bc_get_leb128(s, &offset))+ return JS_EXCEPTION;+ /* XXX: this hack could be avoided if the typed array could be+ created before the array buffer */+ idx = s->objects_count;+ if (BC_add_object_ref1(s, NULL))+ goto fail;+ array_buffer = JS_ReadObjectRec(s);+ if (JS_IsException(array_buffer))+ return JS_EXCEPTION;+ if (!js_get_array_buffer(ctx, array_buffer)) {+ JS_FreeValue(ctx, array_buffer);+ return JS_EXCEPTION;+ }+ args[0] = array_buffer;+ args[1] = JS_NewInt64(ctx, offset);+ args[2] = JS_NewInt64(ctx, len);+ obj = js_typed_array_constructor(ctx, JS_UNDEFINED,+ 3, args,+ JS_CLASS_UINT8C_ARRAY + array_tag);+ if (JS_IsException(obj))+ goto fail;+ if (s->allow_reference) {+ s->objects[idx] = JS_VALUE_GET_OBJ(obj);+ }+ JS_FreeValue(ctx, array_buffer);+ return obj;+ fail:+ JS_FreeValue(ctx, array_buffer);+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++static JSValue JS_ReadArrayBuffer(BCReaderState *s)+{+ JSContext *ctx = s->ctx;+ uint32_t byte_length;+ JSValue obj;+ + if (bc_get_leb128(s, &byte_length))+ return JS_EXCEPTION;+ if (unlikely(s->buf_end - s->ptr < byte_length)) {+ bc_read_error_end(s);+ return JS_EXCEPTION;+ }+ obj = JS_NewArrayBufferCopy(ctx, s->ptr, byte_length);+ if (JS_IsException(obj))+ goto fail;+ if (BC_add_object_ref(s, obj))+ goto fail;+ s->ptr += byte_length;+ return obj;+ fail:+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++static JSValue JS_ReadSharedArrayBuffer(BCReaderState *s)+{+ JSContext *ctx = s->ctx;+ uint32_t byte_length;+ uint8_t *data_ptr;+ JSValue obj;+ uint64_t u64;+ + if (bc_get_leb128(s, &byte_length))+ return JS_EXCEPTION;+ if (bc_get_u64(s, &u64))+ return JS_EXCEPTION;+ data_ptr = (uint8_t *)(uintptr_t)u64;+ /* the SharedArrayBuffer is cloned */+ obj = js_array_buffer_constructor3(ctx, JS_UNDEFINED, byte_length,+ JS_CLASS_SHARED_ARRAY_BUFFER,+ data_ptr,+ NULL, NULL, FALSE);+ if (JS_IsException(obj))+ goto fail;+ if (BC_add_object_ref(s, obj))+ goto fail;+ return obj;+ fail:+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++static JSValue JS_ReadDate(BCReaderState *s)+{+ JSContext *ctx = s->ctx;+ JSValue val, obj = JS_UNDEFINED;++ val = JS_ReadObjectRec(s);+ if (JS_IsException(val))+ goto fail;+ if (!JS_IsNumber(val)) {+ JS_ThrowTypeError(ctx, "Number tag expected for date");+ goto fail;+ }+ obj = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_DATE],+ JS_CLASS_DATE);+ if (JS_IsException(obj))+ goto fail;+ if (BC_add_object_ref(s, obj))+ goto fail;+ JS_SetObjectData(ctx, obj, val);+ return obj;+ fail:+ JS_FreeValue(ctx, val);+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++static JSValue JS_ReadObjectValue(BCReaderState *s)+{+ JSContext *ctx = s->ctx;+ JSValue val, obj = JS_UNDEFINED;++ val = JS_ReadObjectRec(s);+ if (JS_IsException(val))+ goto fail;+ obj = JS_ToObject(ctx, val);+ if (JS_IsException(obj))+ goto fail;+ if (BC_add_object_ref(s, obj))+ goto fail;+ JS_FreeValue(ctx, val);+ return obj;+ fail:+ JS_FreeValue(ctx, val);+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++static JSValue JS_ReadObjectRec(BCReaderState *s)+{+ JSContext *ctx = s->ctx;+ uint8_t tag;+ JSValue obj = JS_UNDEFINED;++ if (js_check_stack_overflow(ctx->rt, 0))+ return JS_ThrowStackOverflow(ctx);++ if (bc_get_u8(s, &tag))+ return JS_EXCEPTION;++ bc_read_trace(s, "%s {\n", bc_tag_str[tag]);++ switch(tag) {+ case BC_TAG_NULL:+ obj = JS_NULL;+ break;+ case BC_TAG_UNDEFINED:+ obj = JS_UNDEFINED;+ break;+ case BC_TAG_BOOL_FALSE:+ case BC_TAG_BOOL_TRUE:+ obj = JS_NewBool(ctx, tag - BC_TAG_BOOL_FALSE);+ break;+ case BC_TAG_INT32:+ {+ int32_t val;+ if (bc_get_sleb128(s, &val))+ return JS_EXCEPTION;+ bc_read_trace(s, "%d\n", val);+ obj = JS_NewInt32(ctx, val);+ }+ break;+ case BC_TAG_FLOAT64:+ {+ JSFloat64Union u;+ if (bc_get_u64(s, &u.u64))+ return JS_EXCEPTION;+ bc_read_trace(s, "%g\n", u.d);+ obj = __JS_NewFloat64(ctx, u.d);+ }+ break;+ case BC_TAG_STRING:+ {+ JSString *p;+ p = JS_ReadString(s);+ if (!p)+ return JS_EXCEPTION;+ obj = JS_MKPTR(JS_TAG_STRING, p);+ }+ break;+ case BC_TAG_FUNCTION_BYTECODE:+ if (!s->allow_bytecode)+ goto invalid_tag;+ obj = JS_ReadFunctionTag(s);+ break;+ case BC_TAG_MODULE:+ if (!s->allow_bytecode)+ goto invalid_tag;+ obj = JS_ReadModule(s);+ break;+ case BC_TAG_OBJECT:+ obj = JS_ReadObjectTag(s);+ break;+ case BC_TAG_ARRAY:+ case BC_TAG_TEMPLATE_OBJECT:+ obj = JS_ReadArray(s, tag);+ break;+ case BC_TAG_TYPED_ARRAY:+ obj = JS_ReadTypedArray(s);+ break;+ case BC_TAG_ARRAY_BUFFER:+ obj = JS_ReadArrayBuffer(s);+ break;+ case BC_TAG_SHARED_ARRAY_BUFFER:+ if (!s->allow_sab || !ctx->rt->sab_funcs.sab_dup)+ goto invalid_tag;+ obj = JS_ReadSharedArrayBuffer(s);+ break;+ case BC_TAG_DATE:+ obj = JS_ReadDate(s);+ break;+ case BC_TAG_OBJECT_VALUE:+ obj = JS_ReadObjectValue(s);+ break;+#ifdef CONFIG_BIGNUM+ case BC_TAG_BIG_INT:+ case BC_TAG_BIG_FLOAT:+ case BC_TAG_BIG_DECIMAL:+ obj = JS_ReadBigNum(s, tag);+ break;+#endif+ case BC_TAG_OBJECT_REFERENCE:+ {+ uint32_t val;+ if (!s->allow_reference)+ return JS_ThrowSyntaxError(ctx, "object references are not allowed");+ if (bc_get_leb128(s, &val))+ return JS_EXCEPTION;+ bc_read_trace(s, "%u\n", val);+ if (val >= s->objects_count) {+ return JS_ThrowSyntaxError(ctx, "invalid object reference (%u >= %u)",+ val, s->objects_count);+ }+ obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, s->objects[val]));+ }+ break;+ default:+ invalid_tag:+ return JS_ThrowSyntaxError(ctx, "invalid tag (tag=%d pos=%u)",+ tag, (unsigned int)(s->ptr - s->buf_start));+ }+ bc_read_trace(s, "}\n");+ return obj;+}++static int JS_ReadObjectAtoms(BCReaderState *s)+{+ uint8_t v8;+ JSString *p;+ int i;+ JSAtom atom;++ if (bc_get_u8(s, &v8))+ return -1;+ /* XXX: could support byte swapped input */+ if (v8 != BC_VERSION) {+ JS_ThrowSyntaxError(s->ctx, "invalid version (%d expected=%d)",+ v8, BC_VERSION);+ return -1;+ }+ if (bc_get_leb128(s, &s->idx_to_atom_count))+ return -1;++ bc_read_trace(s, "%d atom indexes {\n", s->idx_to_atom_count);++ if (s->idx_to_atom_count != 0) {+ s->idx_to_atom = js_mallocz(s->ctx, s->idx_to_atom_count *+ sizeof(s->idx_to_atom[0]));+ if (!s->idx_to_atom)+ return s->error_state = -1;+ }+ for(i = 0; i < s->idx_to_atom_count; i++) {+ p = JS_ReadString(s);+ if (!p)+ return -1;+ atom = JS_NewAtomStr(s->ctx, p);+ if (atom == JS_ATOM_NULL)+ return s->error_state = -1;+ s->idx_to_atom[i] = atom;+ if (s->is_rom_data && (atom != (i + s->first_atom)))+ s->is_rom_data = FALSE; /* atoms must be relocated */+ }+ bc_read_trace(s, "}\n");+ return 0;+}++static void bc_reader_free(BCReaderState *s)+{+ int i;+ if (s->idx_to_atom) {+ for(i = 0; i < s->idx_to_atom_count; i++) {+ JS_FreeAtom(s->ctx, s->idx_to_atom[i]);+ }+ js_free(s->ctx, s->idx_to_atom);+ }+ js_free(s->ctx, s->objects);+}++JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len,+ int flags)+{+ BCReaderState ss, *s = &ss;+ JSValue obj;++ ctx->binary_object_count += 1;+ ctx->binary_object_size += buf_len;++ memset(s, 0, sizeof(*s));+ s->ctx = ctx;+ s->buf_start = buf;+ s->buf_end = buf + buf_len;+ s->ptr = buf;+ s->allow_bytecode = ((flags & JS_READ_OBJ_BYTECODE) != 0);+ s->is_rom_data = ((flags & JS_READ_OBJ_ROM_DATA) != 0);+ s->allow_sab = ((flags & JS_READ_OBJ_SAB) != 0);+ s->allow_reference = ((flags & JS_READ_OBJ_REFERENCE) != 0);+ if (s->allow_bytecode)+ s->first_atom = JS_ATOM_END;+ else+ s->first_atom = 1;+ if (JS_ReadObjectAtoms(s)) {+ obj = JS_EXCEPTION;+ } else {+ obj = JS_ReadObjectRec(s);+ }+ bc_reader_free(s);+ return obj;+}++/*******************************************************************/+/* runtime functions & objects */++static JSValue js_string_constructor(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv);+static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv);+static JSValue js_number_constructor(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv);++static int check_function(JSContext *ctx, JSValueConst obj)+{+ if (likely(JS_IsFunction(ctx, obj)))+ return 0;+ JS_ThrowTypeError(ctx, "not a function");+ return -1;+}++static int check_exception_free(JSContext *ctx, JSValue obj)+{+ JS_FreeValue(ctx, obj);+ return JS_IsException(obj);+}++static JSAtom find_atom(JSContext *ctx, const char *name)+{+ JSAtom atom;+ int len;++ if (*name == '[') {+ name++;+ len = strlen(name) - 1;+ /* We assume 8 bit non null strings, which is the case for these+ symbols */+ for(atom = JS_ATOM_Symbol_toPrimitive; atom < JS_ATOM_END; atom++) {+ JSAtomStruct *p = ctx->rt->atom_array[atom];+ JSString *str = p;+ if (str->len == len && !memcmp(str->u.str8, name, len))+ return JS_DupAtom(ctx, atom);+ }+ abort();+ } else {+ atom = JS_NewAtom(ctx, name);+ }+ return atom;+}++static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p,+ JSAtom atom, void *opaque)+{+ const JSCFunctionListEntry *e = opaque;+ JSValue val;++ switch(e->def_type) {+ case JS_DEF_CFUNC:+ val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic,+ e->name, e->u.func.length, e->u.func.cproto, e->magic);+ break;+ case JS_DEF_PROP_STRING:+ val = JS_NewAtomString(ctx, e->u.str);+ break;+ case JS_DEF_OBJECT:+ val = JS_NewObject(ctx);+ JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len);+ break;+ default:+ abort();+ }+ return val;+}++static int JS_InstantiateFunctionListItem(JSContext *ctx, JSValueConst obj,+ JSAtom atom,+ const JSCFunctionListEntry *e)+{+ JSValue val;+ int prop_flags = e->prop_flags;++ switch(e->def_type) {+ case JS_DEF_ALIAS: /* using autoinit for aliases is not safe */+ {+ JSAtom atom1 = find_atom(ctx, e->u.alias.name);+ switch (e->u.alias.base) {+ case -1:+ val = JS_GetProperty(ctx, obj, atom1);+ break;+ case 0:+ val = JS_GetProperty(ctx, ctx->global_obj, atom1);+ break;+ case 1:+ val = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], atom1);+ break;+ default:+ abort();+ }+ JS_FreeAtom(ctx, atom1);+ if (atom == JS_ATOM_Symbol_toPrimitive) {+ /* Symbol.toPrimitive functions are not writable */+ prop_flags = JS_PROP_CONFIGURABLE;+ } else if (atom == JS_ATOM_Symbol_hasInstance) {+ /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */+ prop_flags = 0;+ }+ }+ break;+ case JS_DEF_CFUNC:+ if (atom == JS_ATOM_Symbol_toPrimitive) {+ /* Symbol.toPrimitive functions are not writable */+ prop_flags = JS_PROP_CONFIGURABLE;+ } else if (atom == JS_ATOM_Symbol_hasInstance) {+ /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */+ prop_flags = 0;+ }+ JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP,+ (void *)e, prop_flags);+ return 0;+ case JS_DEF_CGETSET: /* XXX: use autoinit again ? */+ case JS_DEF_CGETSET_MAGIC:+ {+ JSValue getter, setter;+ char buf[64];++ getter = JS_UNDEFINED;+ if (e->u.getset.get.generic) {+ snprintf(buf, sizeof(buf), "get %s", e->name);+ getter = JS_NewCFunction2(ctx, e->u.getset.get.generic,+ buf, 0, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_getter_magic : JS_CFUNC_getter,+ e->magic);+ }+ setter = JS_UNDEFINED;+ if (e->u.getset.set.generic) {+ snprintf(buf, sizeof(buf), "set %s", e->name);+ setter = JS_NewCFunction2(ctx, e->u.getset.set.generic,+ buf, 1, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_setter_magic : JS_CFUNC_setter,+ e->magic);+ }+ JS_DefinePropertyGetSet(ctx, obj, atom, getter, setter, prop_flags);+ return 0;+ }+ break;+ case JS_DEF_PROP_INT32:+ val = JS_NewInt32(ctx, e->u.i32);+ break;+ case JS_DEF_PROP_INT64:+ val = JS_NewInt64(ctx, e->u.i64);+ break;+ case JS_DEF_PROP_DOUBLE:+ val = __JS_NewFloat64(ctx, e->u.f64);+ break;+ case JS_DEF_PROP_UNDEFINED:+ val = JS_UNDEFINED;+ break;+ case JS_DEF_PROP_STRING:+ case JS_DEF_OBJECT:+ JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP,+ (void *)e, prop_flags);+ return 0;+ default:+ abort();+ }+ JS_DefinePropertyValue(ctx, obj, atom, val, prop_flags);+ return 0;+}++void JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj,+ const JSCFunctionListEntry *tab, int len)+{+ int i;++ for (i = 0; i < len; i++) {+ const JSCFunctionListEntry *e = &tab[i];+ JSAtom atom = find_atom(ctx, e->name);+ JS_InstantiateFunctionListItem(ctx, obj, atom, e);+ JS_FreeAtom(ctx, atom);+ }+}++int JS_AddModuleExportList(JSContext *ctx, JSModuleDef *m,+ const JSCFunctionListEntry *tab, int len)+{+ int i;+ for(i = 0; i < len; i++) {+ if (JS_AddModuleExport(ctx, m, tab[i].name))+ return -1;+ }+ return 0;+}++int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m,+ const JSCFunctionListEntry *tab, int len)+{+ int i;+ JSValue val;++ for(i = 0; i < len; i++) {+ const JSCFunctionListEntry *e = &tab[i];+ switch(e->def_type) {+ case JS_DEF_CFUNC:+ val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic,+ e->name, e->u.func.length, e->u.func.cproto, e->magic);+ break;+ case JS_DEF_PROP_STRING:+ val = JS_NewString(ctx, e->u.str);+ break;+ case JS_DEF_PROP_INT32:+ val = JS_NewInt32(ctx, e->u.i32);+ break;+ case JS_DEF_PROP_INT64:+ val = JS_NewInt64(ctx, e->u.i64);+ break;+ case JS_DEF_PROP_DOUBLE:+ val = __JS_NewFloat64(ctx, e->u.f64);+ break;+ case JS_DEF_OBJECT:+ val = JS_NewObject(ctx);+ JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len);+ break;+ default:+ abort();+ }+ if (JS_SetModuleExport(ctx, m, e->name, val))+ return -1;+ }+ return 0;+}++/* Note: 'func_obj' is not necessarily a constructor */+static void JS_SetConstructor2(JSContext *ctx,+ JSValueConst func_obj,+ JSValueConst proto,+ int proto_flags, int ctor_flags)+{+ JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype,+ JS_DupValue(ctx, proto), proto_flags);+ JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor,+ JS_DupValue(ctx, func_obj),+ ctor_flags);+ set_cycle_flag(ctx, func_obj);+ set_cycle_flag(ctx, proto);+}++void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj, + JSValueConst proto)+{+ JS_SetConstructor2(ctx, func_obj, proto,+ 0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+}++static void JS_NewGlobalCConstructor2(JSContext *ctx,+ JSValue func_obj,+ const char *name,+ JSValueConst proto)+{+ JS_DefinePropertyValueStr(ctx, ctx->global_obj, name,+ JS_DupValue(ctx, func_obj),+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+ JS_SetConstructor(ctx, func_obj, proto);+ JS_FreeValue(ctx, func_obj);+}++static JSValueConst JS_NewGlobalCConstructor(JSContext *ctx, const char *name,+ JSCFunction *func, int length,+ JSValueConst proto)+{+ JSValue func_obj;+ func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor_or_func, 0);+ JS_NewGlobalCConstructor2(ctx, func_obj, name, proto);+ return func_obj;+}++static JSValueConst JS_NewGlobalCConstructorOnly(JSContext *ctx, const char *name,+ JSCFunction *func, int length,+ JSValueConst proto)+{+ JSValue func_obj;+ func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor, 0);+ JS_NewGlobalCConstructor2(ctx, func_obj, name, proto);+ return func_obj;+}++static JSValue js_global_eval(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return JS_EvalObject(ctx, ctx->global_obj, argv[0], JS_EVAL_TYPE_INDIRECT, -1);+}++static JSValue js_global_isNaN(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ double d;++ /* XXX: does this work for bigfloat? */+ if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))+ return JS_EXCEPTION;+ return JS_NewBool(ctx, isnan(d));+}++static JSValue js_global_isFinite(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ BOOL res;+ double d;+ if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))+ return JS_EXCEPTION;+ res = isfinite(d);+ return JS_NewBool(ctx, res);+}++/* Object class */++static JSValue JS_ToObject(JSContext *ctx, JSValueConst val)+{+ int tag = JS_VALUE_GET_NORM_TAG(val);+ JSValue obj;++ switch(tag) {+ default:+ case JS_TAG_NULL:+ case JS_TAG_UNDEFINED:+ return JS_ThrowTypeError(ctx, "cannot convert to object");+ case JS_TAG_OBJECT:+ case JS_TAG_EXCEPTION:+ return JS_DupValue(ctx, val);+#ifdef CONFIG_BIGNUM+ case JS_TAG_BIG_INT:+ obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT);+ goto set_value;+ case JS_TAG_BIG_FLOAT:+ obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_FLOAT);+ goto set_value;+ case JS_TAG_BIG_DECIMAL:+ obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_DECIMAL);+ goto set_value;+#endif+ case JS_TAG_INT:+ case JS_TAG_FLOAT64:+ obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER);+ goto set_value;+ case JS_TAG_STRING:+ /* XXX: should call the string constructor */+ {+ JSString *p1 = JS_VALUE_GET_STRING(val);+ obj = JS_NewObjectClass(ctx, JS_CLASS_STRING);+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0);+ }+ goto set_value;+ case JS_TAG_BOOL:+ obj = JS_NewObjectClass(ctx, JS_CLASS_BOOLEAN);+ goto set_value;+ case JS_TAG_SYMBOL:+ obj = JS_NewObjectClass(ctx, JS_CLASS_SYMBOL);+ set_value:+ if (!JS_IsException(obj))+ JS_SetObjectData(ctx, obj, JS_DupValue(ctx, val));+ return obj;+ }+}++static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val)+{+ JSValue obj = JS_ToObject(ctx, val);+ JS_FreeValue(ctx, val);+ return obj;+}++static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d,+ JSValueConst desc)+{+ JSValue val, getter, setter;+ int flags;++ if (!JS_IsObject(desc)) {+ JS_ThrowTypeErrorNotAnObject(ctx);+ return -1;+ }+ flags = 0;+ val = JS_UNDEFINED;+ getter = JS_UNDEFINED;+ setter = JS_UNDEFINED;+ if (JS_HasProperty(ctx, desc, JS_ATOM_configurable)) {+ JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_configurable);+ if (JS_IsException(prop))+ goto fail;+ flags |= JS_PROP_HAS_CONFIGURABLE;+ if (JS_ToBoolFree(ctx, prop))+ flags |= JS_PROP_CONFIGURABLE;+ }+ if (JS_HasProperty(ctx, desc, JS_ATOM_writable)) {+ JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_writable);+ if (JS_IsException(prop))+ goto fail;+ flags |= JS_PROP_HAS_WRITABLE;+ if (JS_ToBoolFree(ctx, prop))+ flags |= JS_PROP_WRITABLE;+ }+ if (JS_HasProperty(ctx, desc, JS_ATOM_enumerable)) {+ JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_enumerable);+ if (JS_IsException(prop))+ goto fail;+ flags |= JS_PROP_HAS_ENUMERABLE;+ if (JS_ToBoolFree(ctx, prop))+ flags |= JS_PROP_ENUMERABLE;+ }+ if (JS_HasProperty(ctx, desc, JS_ATOM_value)) {+ flags |= JS_PROP_HAS_VALUE;+ val = JS_GetProperty(ctx, desc, JS_ATOM_value);+ if (JS_IsException(val))+ goto fail;+ }+ if (JS_HasProperty(ctx, desc, JS_ATOM_get)) {+ flags |= JS_PROP_HAS_GET;+ getter = JS_GetProperty(ctx, desc, JS_ATOM_get);+ if (JS_IsException(getter) ||+ !(JS_IsUndefined(getter) || JS_IsFunction(ctx, getter))) {+ JS_ThrowTypeError(ctx, "invalid getter");+ goto fail;+ }+ }+ if (JS_HasProperty(ctx, desc, JS_ATOM_set)) {+ flags |= JS_PROP_HAS_SET;+ setter = JS_GetProperty(ctx, desc, JS_ATOM_set);+ if (JS_IsException(setter) ||+ !(JS_IsUndefined(setter) || JS_IsFunction(ctx, setter))) {+ JS_ThrowTypeError(ctx, "invalid setter");+ goto fail;+ }+ }+ if ((flags & (JS_PROP_HAS_SET | JS_PROP_HAS_GET)) &&+ (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE))) {+ JS_ThrowTypeError(ctx, "cannot have setter/getter and value or writable");+ goto fail;+ }+ d->flags = flags;+ d->value = val;+ d->getter = getter;+ d->setter = setter;+ return 0;+ fail:+ JS_FreeValue(ctx, val);+ JS_FreeValue(ctx, getter);+ JS_FreeValue(ctx, setter);+ return -1;+}++static __exception int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj,+ JSAtom prop, JSValueConst desc,+ int flags)+{+ JSPropertyDescriptor d;+ int ret;++ if (js_obj_to_desc(ctx, &d, desc) < 0)+ return -1;++ ret = JS_DefineProperty(ctx, obj, prop,+ d.value, d.getter, d.setter, d.flags | flags);+ js_free_desc(ctx, &d);+ return ret;+}++static __exception int JS_ObjectDefineProperties(JSContext *ctx,+ JSValueConst obj,+ JSValueConst properties)+{+ JSValue props, desc;+ JSObject *p;+ JSPropertyEnum *atoms;+ uint32_t len, i;+ int ret = -1;++ if (!JS_IsObject(obj)) {+ JS_ThrowTypeErrorNotAnObject(ctx);+ return -1;+ }+ desc = JS_UNDEFINED;+ props = JS_ToObject(ctx, properties);+ if (JS_IsException(props))+ return -1;+ p = JS_VALUE_GET_OBJ(props);+ if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK) < 0)+ goto exception;+ for(i = 0; i < len; i++) {+ JS_FreeValue(ctx, desc);+ desc = JS_GetProperty(ctx, props, atoms[i].atom);+ if (JS_IsException(desc))+ goto exception;+ if (JS_DefinePropertyDesc(ctx, obj, atoms[i].atom, desc, JS_PROP_THROW) < 0)+ goto exception;+ }+ ret = 0;++exception:+ js_free_prop_enum(ctx, atoms, len);+ JS_FreeValue(ctx, props);+ JS_FreeValue(ctx, desc);+ return ret;+}++static JSValue js_object_constructor(JSContext *ctx, JSValueConst new_target,+ int argc, JSValueConst *argv)+{+ JSValue ret;+ if (!JS_IsUndefined(new_target) &&+ JS_VALUE_GET_OBJ(new_target) !=+ JS_VALUE_GET_OBJ(JS_GetActiveFunction(ctx))) {+ ret = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);+ } else {+ int tag = JS_VALUE_GET_NORM_TAG(argv[0]);+ switch(tag) {+ case JS_TAG_NULL:+ case JS_TAG_UNDEFINED:+ ret = JS_NewObject(ctx);+ break;+ default:+ ret = JS_ToObject(ctx, argv[0]);+ break;+ }+ }+ return ret;+}++static JSValue js_object_create(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValueConst proto, props;+ JSValue obj;++ proto = argv[0];+ if (!JS_IsObject(proto) && !JS_IsNull(proto))+ return JS_ThrowTypeError(ctx, "not a prototype");+ obj = JS_NewObjectProto(ctx, proto);+ if (JS_IsException(obj))+ return JS_EXCEPTION;+ props = argv[1];+ if (!JS_IsUndefined(props)) {+ if (JS_ObjectDefineProperties(ctx, obj, props)) {+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+ }+ }+ return obj;+}++static JSValue js_object_getPrototypeOf(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ JSValueConst val;++ val = argv[0];+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) {+ /* ES6 feature non compatible with ES5.1: primitive types are+ accepted */+ if (magic || JS_VALUE_GET_TAG(val) == JS_TAG_NULL ||+ JS_VALUE_GET_TAG(val) == JS_TAG_UNDEFINED)+ return JS_ThrowTypeErrorNotAnObject(ctx);+ }+ return JS_GetPrototype(ctx, val);+}++static JSValue js_object_setPrototypeOf(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValueConst obj;+ obj = argv[0];+ if (JS_SetPrototypeInternal(ctx, obj, argv[1], TRUE) < 0)+ return JS_EXCEPTION;+ return JS_DupValue(ctx, obj);+}++/* magic = 1 if called as Reflect.defineProperty */+static JSValue js_object_defineProperty(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ JSValueConst obj, prop, desc;+ int ret, flags;+ JSAtom atom;++ obj = argv[0];+ prop = argv[1];+ desc = argv[2];++ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)+ return JS_ThrowTypeErrorNotAnObject(ctx);+ atom = JS_ValueToAtom(ctx, prop);+ if (unlikely(atom == JS_ATOM_NULL))+ return JS_EXCEPTION;+ flags = 0;+ if (!magic)+ flags |= JS_PROP_THROW;+ ret = JS_DefinePropertyDesc(ctx, obj, atom, desc, flags);+ JS_FreeAtom(ctx, atom);+ if (ret < 0) {+ return JS_EXCEPTION;+ } else if (magic) {+ return JS_NewBool(ctx, ret);+ } else {+ return JS_DupValue(ctx, obj);+ }+}++static JSValue js_object_defineProperties(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ // defineProperties(obj, properties)+ JSValueConst obj = argv[0];++ if (JS_ObjectDefineProperties(ctx, obj, argv[1]))+ return JS_EXCEPTION;+ else+ return JS_DupValue(ctx, obj);+}++/* magic = 1 if called as __defineSetter__ */+static JSValue js_object___defineGetter__(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ JSValue obj;+ JSValueConst prop, value, get, set;+ int ret, flags;+ JSAtom atom;++ prop = argv[0];+ value = argv[1];++ obj = JS_ToObject(ctx, this_val);+ if (JS_IsException(obj))+ return JS_EXCEPTION;++ if (check_function(ctx, value)) {+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+ }+ atom = JS_ValueToAtom(ctx, prop);+ if (unlikely(atom == JS_ATOM_NULL)) {+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+ }+ flags = JS_PROP_THROW |+ JS_PROP_HAS_ENUMERABLE | JS_PROP_ENUMERABLE |+ JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE;+ if (magic) {+ get = JS_UNDEFINED;+ set = value;+ flags |= JS_PROP_HAS_SET;+ } else {+ get = value;+ set = JS_UNDEFINED;+ flags |= JS_PROP_HAS_GET;+ }+ ret = JS_DefineProperty(ctx, obj, atom, JS_UNDEFINED, get, set, flags);+ JS_FreeValue(ctx, obj);+ JS_FreeAtom(ctx, atom);+ if (ret < 0) {+ return JS_EXCEPTION;+ } else {+ return JS_UNDEFINED;+ }+}++static JSValue js_object_getOwnPropertyDescriptor(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ JSValueConst prop;+ JSAtom atom;+ JSValue ret, obj;+ JSPropertyDescriptor desc;+ int res, flags;++ if (magic) {+ /* Reflect.getOwnPropertyDescriptor case */+ if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT)+ return JS_ThrowTypeErrorNotAnObject(ctx);+ obj = JS_DupValue(ctx, argv[0]);+ } else {+ obj = JS_ToObject(ctx, argv[0]);+ if (JS_IsException(obj))+ return obj;+ }+ prop = argv[1];+ atom = JS_ValueToAtom(ctx, prop);+ if (unlikely(atom == JS_ATOM_NULL))+ goto exception;+ ret = JS_UNDEFINED;+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {+ res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), atom);+ if (res < 0)+ goto exception;+ if (res) {+ ret = JS_NewObject(ctx);+ if (JS_IsException(ret))+ goto exception1;+ flags = JS_PROP_C_W_E | JS_PROP_THROW;+ if (desc.flags & JS_PROP_GETSET) {+ if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, desc.getter), flags) < 0+ || JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, desc.setter), flags) < 0)+ goto exception1;+ } else {+ if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, desc.value), flags) < 0+ || JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable,+ JS_NewBool(ctx, (desc.flags & JS_PROP_WRITABLE) != 0), flags) < 0)+ goto exception1;+ }+ if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable,+ JS_NewBool(ctx, (desc.flags & JS_PROP_ENUMERABLE) != 0), flags) < 0+ || JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable,+ JS_NewBool(ctx, (desc.flags & JS_PROP_CONFIGURABLE) != 0), flags) < 0)+ goto exception1;+ js_free_desc(ctx, &desc);+ }+ }+ JS_FreeAtom(ctx, atom);+ JS_FreeValue(ctx, obj);+ return ret;++exception1:+ js_free_desc(ctx, &desc);+ JS_FreeValue(ctx, ret);+exception:+ JS_FreeAtom(ctx, atom);+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++static JSValue js_object_getOwnPropertyDescriptors(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ //getOwnPropertyDescriptors(obj)+ JSValue obj, r;+ JSObject *p;+ JSPropertyEnum *props;+ uint32_t len, i;++ r = JS_UNDEFINED;+ obj = JS_ToObject(ctx, argv[0]);+ if (JS_IsException(obj))+ return JS_EXCEPTION;++ p = JS_VALUE_GET_OBJ(obj);+ if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p,+ JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK))+ goto exception;+ r = JS_NewObject(ctx);+ if (JS_IsException(r))+ goto exception;+ for(i = 0; i < len; i++) {+ JSValue atomValue, desc;+ JSValueConst args[2];++ atomValue = JS_AtomToValue(ctx, props[i].atom);+ if (JS_IsException(atomValue))+ goto exception;+ args[0] = obj;+ args[1] = atomValue;+ desc = js_object_getOwnPropertyDescriptor(ctx, JS_UNDEFINED, 2, args, 0);+ JS_FreeValue(ctx, atomValue);+ if (JS_IsException(desc))+ goto exception;+ if (!JS_IsUndefined(desc)) {+ if (JS_DefinePropertyValue(ctx, r, props[i].atom, desc,+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)+ goto exception;+ }+ }+ js_free_prop_enum(ctx, props, len);+ JS_FreeValue(ctx, obj);+ return r;++exception:+ js_free_prop_enum(ctx, props, len);+ JS_FreeValue(ctx, obj);+ JS_FreeValue(ctx, r);+ return JS_EXCEPTION;+}++static JSValue JS_GetOwnPropertyNames2(JSContext *ctx, JSValueConst obj1,+ int flags, int kind)+{+ JSValue obj, r, val, key, value;+ JSObject *p;+ JSPropertyEnum *atoms;+ uint32_t len, i, j;++ r = JS_UNDEFINED;+ val = JS_UNDEFINED;+ obj = JS_ToObject(ctx, obj1);+ if (JS_IsException(obj))+ return JS_EXCEPTION;+ p = JS_VALUE_GET_OBJ(obj);+ if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, flags & ~JS_GPN_ENUM_ONLY))+ goto exception;+ r = JS_NewArray(ctx);+ if (JS_IsException(r))+ goto exception;+ for(j = i = 0; i < len; i++) {+ JSAtom atom = atoms[i].atom;+ if (flags & JS_GPN_ENUM_ONLY) {+ JSPropertyDescriptor desc;+ int res;++ /* Check if property is still enumerable */+ res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);+ if (res < 0)+ goto exception;+ if (!res)+ continue;+ js_free_desc(ctx, &desc);+ if (!(desc.flags & JS_PROP_ENUMERABLE))+ continue;+ }+ switch(kind) {+ default:+ case JS_ITERATOR_KIND_KEY:+ val = JS_AtomToValue(ctx, atom);+ if (JS_IsException(val))+ goto exception;+ break;+ case JS_ITERATOR_KIND_VALUE:+ val = JS_GetProperty(ctx, obj, atom);+ if (JS_IsException(val))+ goto exception;+ break;+ case JS_ITERATOR_KIND_KEY_AND_VALUE:+ val = JS_NewArray(ctx);+ if (JS_IsException(val))+ goto exception;+ key = JS_AtomToValue(ctx, atom);+ if (JS_IsException(key))+ goto exception1;+ if (JS_CreateDataPropertyUint32(ctx, val, 0, key, JS_PROP_THROW) < 0)+ goto exception1;+ value = JS_GetProperty(ctx, obj, atom);+ if (JS_IsException(value))+ goto exception1;+ if (JS_CreateDataPropertyUint32(ctx, val, 1, value, JS_PROP_THROW) < 0)+ goto exception1;+ break;+ }+ if (JS_CreateDataPropertyUint32(ctx, r, j++, val, 0) < 0)+ goto exception;+ }+ goto done;++exception1:+ JS_FreeValue(ctx, val);+exception:+ JS_FreeValue(ctx, r);+ r = JS_EXCEPTION;+done:+ js_free_prop_enum(ctx, atoms, len);+ JS_FreeValue(ctx, obj);+ return r;+}++static JSValue js_object_getOwnPropertyNames(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return JS_GetOwnPropertyNames2(ctx, argv[0],+ JS_GPN_STRING_MASK, JS_ITERATOR_KIND_KEY);+}++static JSValue js_object_getOwnPropertySymbols(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return JS_GetOwnPropertyNames2(ctx, argv[0],+ JS_GPN_SYMBOL_MASK, JS_ITERATOR_KIND_KEY);+}++static JSValue js_object_keys(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int kind)+{+ return JS_GetOwnPropertyNames2(ctx, argv[0],+ JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK, kind);+}++static JSValue js_object_isExtensible(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int reflect)+{+ JSValueConst obj;+ int ret;++ obj = argv[0];+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {+ if (reflect)+ return JS_ThrowTypeErrorNotAnObject(ctx);+ else+ return JS_FALSE;+ }+ ret = JS_IsExtensible(ctx, obj);+ if (ret < 0)+ return JS_EXCEPTION;+ else+ return JS_NewBool(ctx, ret);+}++static JSValue js_object_preventExtensions(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int reflect)+{+ JSValueConst obj;+ int ret;++ obj = argv[0];+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {+ if (reflect)+ return JS_ThrowTypeErrorNotAnObject(ctx);+ else+ return JS_DupValue(ctx, obj);+ }+ ret = JS_PreventExtensions(ctx, obj);+ if (ret < 0)+ return JS_EXCEPTION;+ if (reflect) {+ return JS_NewBool(ctx, ret);+ } else {+ if (!ret)+ return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false");+ return JS_DupValue(ctx, obj);+ }+}++static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue obj;+ JSAtom atom;+ JSObject *p;+ BOOL ret;++ atom = JS_ValueToAtom(ctx, argv[0]); /* must be done first */+ if (unlikely(atom == JS_ATOM_NULL))+ return JS_EXCEPTION;+ obj = JS_ToObject(ctx, this_val);+ if (JS_IsException(obj)) {+ JS_FreeAtom(ctx, atom);+ return obj;+ }+ p = JS_VALUE_GET_OBJ(obj);+ ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom);+ JS_FreeAtom(ctx, atom);+ JS_FreeValue(ctx, obj);+ if (ret < 0)+ return JS_EXCEPTION;+ else+ return JS_NewBool(ctx, ret);+}++static JSValue js_object_valueOf(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return JS_ToObject(ctx, this_val);+}++static JSValue js_object_toString(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue obj, tag;+ int is_array;+ JSAtom atom;+ JSObject *p;++ if (JS_IsNull(this_val)) {+ tag = JS_NewString(ctx, "Null");+ } else if (JS_IsUndefined(this_val)) {+ tag = JS_NewString(ctx, "Undefined");+ } else {+ obj = JS_ToObject(ctx, this_val);+ if (JS_IsException(obj))+ return obj;+ is_array = JS_IsArray(ctx, obj);+ if (is_array < 0) {+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+ }+ if (is_array) {+ atom = JS_ATOM_Array;+ } else if (JS_IsFunction(ctx, obj)) {+ atom = JS_ATOM_Function;+ } else {+ p = JS_VALUE_GET_OBJ(obj);+ switch(p->class_id) {+ case JS_CLASS_STRING:+ case JS_CLASS_ARGUMENTS:+ case JS_CLASS_MAPPED_ARGUMENTS:+ case JS_CLASS_ERROR:+ case JS_CLASS_BOOLEAN:+ case JS_CLASS_NUMBER:+ case JS_CLASS_DATE:+ case JS_CLASS_REGEXP:+ atom = ctx->rt->class_array[p->class_id].class_name;+ break;+ default:+ atom = JS_ATOM_Object;+ break;+ }+ }+ tag = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_toStringTag);+ JS_FreeValue(ctx, obj);+ if (JS_IsException(tag))+ return JS_EXCEPTION;+ if (!JS_IsString(tag)) {+ JS_FreeValue(ctx, tag);+ tag = JS_AtomToString(ctx, atom);+ }+ }+ return JS_ConcatString3(ctx, "[object ", tag, "]");+}++static JSValue js_object_toLocaleString(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return JS_Invoke(ctx, this_val, JS_ATOM_toString, 0, NULL);+}++static JSValue js_object_assign(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ // Object.assign(obj, source1)+ JSValue obj, s;+ int i;++ s = JS_UNDEFINED;+ obj = JS_ToObject(ctx, argv[0]);+ if (JS_IsException(obj))+ goto exception;+ for (i = 1; i < argc; i++) {+ if (!JS_IsNull(argv[i]) && !JS_IsUndefined(argv[i])) {+ s = JS_ToObject(ctx, argv[i]);+ if (JS_IsException(s))+ goto exception;+ if (JS_CopyDataProperties(ctx, obj, s, JS_UNDEFINED, TRUE))+ goto exception;+ JS_FreeValue(ctx, s);+ }+ }+ return obj;+exception:+ JS_FreeValue(ctx, obj);+ JS_FreeValue(ctx, s);+ return JS_EXCEPTION;+}++static JSValue js_object_seal(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int freeze_flag)+{+ JSValueConst obj = argv[0];+ JSObject *p;+ JSPropertyEnum *props;+ uint32_t len, i;+ int flags, desc_flags, res;++ if (!JS_IsObject(obj))+ return JS_DupValue(ctx, obj);++ res = JS_PreventExtensions(ctx, obj);+ if (res < 0)+ return JS_EXCEPTION;+ if (!res) {+ return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false");+ }+ + p = JS_VALUE_GET_OBJ(obj);+ flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK;+ if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags))+ return JS_EXCEPTION;++ for(i = 0; i < len; i++) {+ JSPropertyDescriptor desc;+ JSAtom prop = props[i].atom;++ desc_flags = JS_PROP_THROW | JS_PROP_HAS_CONFIGURABLE;+ if (freeze_flag) {+ res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);+ if (res < 0)+ goto exception;+ if (res) {+ if (desc.flags & JS_PROP_WRITABLE)+ desc_flags |= JS_PROP_HAS_WRITABLE;+ js_free_desc(ctx, &desc);+ }+ }+ if (JS_DefineProperty(ctx, obj, prop, JS_UNDEFINED,+ JS_UNDEFINED, JS_UNDEFINED, desc_flags) < 0)+ goto exception;+ }+ js_free_prop_enum(ctx, props, len);+ return JS_DupValue(ctx, obj);++ exception:+ js_free_prop_enum(ctx, props, len);+ return JS_EXCEPTION;+}++static JSValue js_object_isSealed(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int is_frozen)+{+ JSValueConst obj = argv[0];+ JSObject *p;+ JSPropertyEnum *props;+ uint32_t len, i;+ int flags, res;+ + if (!JS_IsObject(obj))+ return JS_TRUE;++ p = JS_VALUE_GET_OBJ(obj);+ flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK;+ if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags))+ return JS_EXCEPTION;++ for(i = 0; i < len; i++) {+ JSPropertyDescriptor desc;+ JSAtom prop = props[i].atom;++ res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);+ if (res < 0)+ goto exception;+ if (res) {+ js_free_desc(ctx, &desc);+ if ((desc.flags & JS_PROP_CONFIGURABLE)+ || (is_frozen && (desc.flags & JS_PROP_WRITABLE))) {+ res = FALSE;+ goto done;+ }+ }+ }+ res = JS_IsExtensible(ctx, obj);+ if (res < 0)+ return JS_EXCEPTION;+ res ^= 1;+done: + js_free_prop_enum(ctx, props, len);+ return JS_NewBool(ctx, res);++exception:+ js_free_prop_enum(ctx, props, len);+ return JS_EXCEPTION;+}++static JSValue js_object_fromEntries(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue obj, iter, next_method = JS_UNDEFINED;+ JSValueConst iterable;+ BOOL done;++ /* RequireObjectCoercible() not necessary because it is tested in+ JS_GetIterator() by JS_GetProperty() */+ iterable = argv[0];++ obj = JS_NewObject(ctx);+ if (JS_IsException(obj))+ return obj;+ + iter = JS_GetIterator(ctx, iterable, FALSE);+ if (JS_IsException(iter))+ goto fail;+ next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);+ if (JS_IsException(next_method))+ goto fail;+ + for(;;) {+ JSValue key, value, item;+ item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);+ if (JS_IsException(item))+ goto fail;+ if (done) {+ JS_FreeValue(ctx, item);+ break;+ }+ + key = JS_UNDEFINED;+ value = JS_UNDEFINED;+ if (!JS_IsObject(item)) {+ JS_ThrowTypeErrorNotAnObject(ctx);+ goto fail1;+ }+ key = JS_GetPropertyUint32(ctx, item, 0);+ if (JS_IsException(key))+ goto fail1;+ value = JS_GetPropertyUint32(ctx, item, 1);+ if (JS_IsException(value)) {+ JS_FreeValue(ctx, key);+ goto fail1;+ }+ if (JS_DefinePropertyValueValue(ctx, obj, key, value,+ JS_PROP_C_W_E | JS_PROP_THROW) < 0) {+ fail1:+ JS_FreeValue(ctx, item);+ goto fail;+ }+ JS_FreeValue(ctx, item);+ }+ JS_FreeValue(ctx, next_method);+ JS_FreeValue(ctx, iter);+ return obj;+ fail:+ if (JS_IsObject(iter)) {+ /* close the iterator object, preserving pending exception */+ JS_IteratorClose(ctx, iter, TRUE);+ }+ JS_FreeValue(ctx, next_method);+ JS_FreeValue(ctx, iter);+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++#if 0+/* Note: corresponds to ECMA spec: CreateDataPropertyOrThrow() */+static JSValue js_object___setOwnProperty(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ int ret;+ ret = JS_DefinePropertyValueValue(ctx, argv[0], JS_DupValue(ctx, argv[1]),+ JS_DupValue(ctx, argv[2]),+ JS_PROP_C_W_E | JS_PROP_THROW);+ if (ret < 0)+ return JS_EXCEPTION;+ else+ return JS_NewBool(ctx, ret);+}++static JSValue js_object___toObject(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return JS_ToObject(ctx, argv[0]);+}++static JSValue js_object___toPrimitive(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ int hint = HINT_NONE;++ if (JS_VALUE_GET_TAG(argv[1]) == JS_TAG_INT)+ hint = JS_VALUE_GET_INT(argv[1]);++ return JS_ToPrimitive(ctx, argv[0], hint);+}+#endif++/* return an empty string if not an object */+static JSValue js_object___getClass(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSAtom atom;+ JSObject *p;+ uint32_t tag;+ int class_id;++ tag = JS_VALUE_GET_NORM_TAG(argv[0]);+ if (tag == JS_TAG_OBJECT) {+ p = JS_VALUE_GET_OBJ(argv[0]);+ class_id = p->class_id;+ if (class_id == JS_CLASS_PROXY && JS_IsFunction(ctx, argv[0]))+ class_id = JS_CLASS_BYTECODE_FUNCTION;+ atom = ctx->rt->class_array[class_id].class_name;+ } else {+ atom = JS_ATOM_empty_string;+ }+ return JS_AtomToString(ctx, atom);+}++static JSValue js_object_is(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return JS_NewBool(ctx, js_same_value(ctx, argv[0], argv[1]));+}++#if 0+static JSValue js_object___getObjectData(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return JS_GetObjectData(ctx, argv[0]);+}++static JSValue js_object___setObjectData(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ if (JS_SetObjectData(ctx, argv[0], JS_DupValue(ctx, argv[1])))+ return JS_EXCEPTION;+ return JS_DupValue(ctx, argv[1]);+}++static JSValue js_object___toPropertyKey(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return JS_ToPropertyKey(ctx, argv[0]);+}++static JSValue js_object___isObject(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return JS_NewBool(ctx, JS_IsObject(argv[0]));+}++static JSValue js_object___isSameValueZero(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return JS_NewBool(ctx, js_same_value_zero(ctx, argv[0], argv[1]));+}++static JSValue js_object___isConstructor(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return JS_NewBool(ctx, JS_IsConstructor(ctx, argv[0]));+}+#endif++static JSValue JS_SpeciesConstructor(JSContext *ctx, JSValueConst obj,+ JSValueConst defaultConstructor)+{+ JSValue ctor, species;++ if (!JS_IsObject(obj))+ return JS_ThrowTypeErrorNotAnObject(ctx);+ ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor);+ if (JS_IsException(ctor))+ return ctor;+ if (JS_IsUndefined(ctor))+ return JS_DupValue(ctx, defaultConstructor);+ if (!JS_IsObject(ctor)) {+ JS_FreeValue(ctx, ctor);+ return JS_ThrowTypeErrorNotAnObject(ctx);+ }+ species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species);+ JS_FreeValue(ctx, ctor);+ if (JS_IsException(species))+ return species;+ if (JS_IsUndefined(species) || JS_IsNull(species))+ return JS_DupValue(ctx, defaultConstructor);+ if (!JS_IsConstructor(ctx, species)) {+ JS_FreeValue(ctx, species);+ return JS_ThrowTypeError(ctx, "not a constructor");+ }+ return species;+}++#if 0+static JSValue js_object___speciesConstructor(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return JS_SpeciesConstructor(ctx, argv[0], argv[1]);+}+#endif++static JSValue js_object_get___proto__(JSContext *ctx, JSValueConst this_val)+{+ JSValue val, ret;++ val = JS_ToObject(ctx, this_val);+ if (JS_IsException(val))+ return val;+ ret = JS_GetPrototype(ctx, val);+ JS_FreeValue(ctx, val);+ return ret;+}++static JSValue js_object_set___proto__(JSContext *ctx, JSValueConst this_val,+ JSValueConst proto)+{+ if (JS_IsUndefined(this_val) || JS_IsNull(this_val))+ return JS_ThrowTypeErrorNotAnObject(ctx);+ if (!JS_IsObject(proto) && !JS_IsNull(proto))+ return JS_UNDEFINED;+ if (JS_SetPrototypeInternal(ctx, this_val, proto, TRUE) < 0)+ return JS_EXCEPTION;+ else+ return JS_UNDEFINED;+}++static JSValue js_object_isPrototypeOf(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue obj, v1;+ JSValueConst v;+ int res;++ v = argv[0];+ if (!JS_IsObject(v))+ return JS_FALSE;+ obj = JS_ToObject(ctx, this_val);+ if (JS_IsException(obj))+ return JS_EXCEPTION;+ v1 = JS_DupValue(ctx, v);+ for(;;) {+ v1 = JS_GetPrototypeFree(ctx, v1);+ if (JS_IsException(v1))+ goto exception;+ if (JS_IsNull(v1)) {+ res = FALSE;+ break;+ }+ if (JS_VALUE_GET_OBJ(obj) == JS_VALUE_GET_OBJ(v1)) {+ res = TRUE;+ break;+ }+ /* avoid infinite loop (possible with proxies) */+ if (js_poll_interrupts(ctx))+ goto exception;+ }+ JS_FreeValue(ctx, v1);+ JS_FreeValue(ctx, obj);+ return JS_NewBool(ctx, res);++exception:+ JS_FreeValue(ctx, v1);+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++static JSValue js_object_propertyIsEnumerable(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue obj, res = JS_EXCEPTION;+ JSAtom prop = JS_ATOM_NULL;+ JSPropertyDescriptor desc;+ int has_prop;++ obj = JS_ToObject(ctx, this_val);+ if (JS_IsException(obj))+ goto exception;+ prop = JS_ValueToAtom(ctx, argv[0]);+ if (unlikely(prop == JS_ATOM_NULL))+ goto exception;++ has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop);+ if (has_prop < 0)+ goto exception;+ if (has_prop) {+ res = JS_NewBool(ctx, (desc.flags & JS_PROP_ENUMERABLE) != 0);+ js_free_desc(ctx, &desc);+ } else {+ res = JS_FALSE;+ }++exception:+ JS_FreeAtom(ctx, prop);+ JS_FreeValue(ctx, obj);+ return res;+}++static JSValue js_object___lookupGetter__(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int setter)+{+ JSValue obj, res = JS_EXCEPTION;+ JSAtom prop = JS_ATOM_NULL;+ JSPropertyDescriptor desc;+ int has_prop;++ obj = JS_ToObject(ctx, this_val);+ if (JS_IsException(obj))+ goto exception;+ prop = JS_ValueToAtom(ctx, argv[0]);+ if (unlikely(prop == JS_ATOM_NULL))+ goto exception;++ for (;;) {+ has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop);+ if (has_prop < 0)+ goto exception;+ if (has_prop) {+ if (desc.flags & JS_PROP_GETSET)+ res = JS_DupValue(ctx, setter ? desc.setter : desc.getter);+ else+ res = JS_UNDEFINED;+ js_free_desc(ctx, &desc);+ break;+ }+ obj = JS_GetPrototypeFree(ctx, obj);+ if (JS_IsException(obj))+ goto exception;+ if (JS_IsNull(obj)) {+ res = JS_UNDEFINED;+ break;+ }+ /* avoid infinite loop (possible with proxies) */+ if (js_poll_interrupts(ctx))+ goto exception;+ }++exception:+ JS_FreeAtom(ctx, prop);+ JS_FreeValue(ctx, obj);+ return res;+}++static const JSCFunctionListEntry js_object_funcs[] = {+ JS_CFUNC_DEF("create", 2, js_object_create ),+ JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 0 ),+ JS_CFUNC_DEF("setPrototypeOf", 2, js_object_setPrototypeOf ),+ JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 0 ),+ JS_CFUNC_DEF("defineProperties", 2, js_object_defineProperties ),+ JS_CFUNC_DEF("getOwnPropertyNames", 1, js_object_getOwnPropertyNames ),+ JS_CFUNC_DEF("getOwnPropertySymbols", 1, js_object_getOwnPropertySymbols ),+ JS_CFUNC_MAGIC_DEF("keys", 1, js_object_keys, JS_ITERATOR_KIND_KEY ),+ JS_CFUNC_MAGIC_DEF("values", 1, js_object_keys, JS_ITERATOR_KIND_VALUE ),+ JS_CFUNC_MAGIC_DEF("entries", 1, js_object_keys, JS_ITERATOR_KIND_KEY_AND_VALUE ),+ JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 0 ),+ JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 0 ),+ JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 0 ),+ JS_CFUNC_DEF("getOwnPropertyDescriptors", 1, js_object_getOwnPropertyDescriptors ),+ JS_CFUNC_DEF("is", 2, js_object_is ),+ JS_CFUNC_DEF("assign", 2, js_object_assign ),+ JS_CFUNC_MAGIC_DEF("seal", 1, js_object_seal, 0 ),+ JS_CFUNC_MAGIC_DEF("freeze", 1, js_object_seal, 1 ),+ JS_CFUNC_MAGIC_DEF("isSealed", 1, js_object_isSealed, 0 ),+ JS_CFUNC_MAGIC_DEF("isFrozen", 1, js_object_isSealed, 1 ),+ JS_CFUNC_DEF("__getClass", 1, js_object___getClass ),+ //JS_CFUNC_DEF("__isObject", 1, js_object___isObject ),+ //JS_CFUNC_DEF("__isConstructor", 1, js_object___isConstructor ),+ //JS_CFUNC_DEF("__toObject", 1, js_object___toObject ),+ //JS_CFUNC_DEF("__setOwnProperty", 3, js_object___setOwnProperty ),+ //JS_CFUNC_DEF("__toPrimitive", 2, js_object___toPrimitive ),+ //JS_CFUNC_DEF("__toPropertyKey", 1, js_object___toPropertyKey ),+ //JS_CFUNC_DEF("__speciesConstructor", 2, js_object___speciesConstructor ),+ //JS_CFUNC_DEF("__isSameValueZero", 2, js_object___isSameValueZero ),+ //JS_CFUNC_DEF("__getObjectData", 1, js_object___getObjectData ),+ //JS_CFUNC_DEF("__setObjectData", 2, js_object___setObjectData ),+ JS_CFUNC_DEF("fromEntries", 1, js_object_fromEntries ),+};++static const JSCFunctionListEntry js_object_proto_funcs[] = {+ JS_CFUNC_DEF("toString", 0, js_object_toString ),+ JS_CFUNC_DEF("toLocaleString", 0, js_object_toLocaleString ),+ JS_CFUNC_DEF("valueOf", 0, js_object_valueOf ),+ JS_CFUNC_DEF("hasOwnProperty", 1, js_object_hasOwnProperty ),+ JS_CFUNC_DEF("isPrototypeOf", 1, js_object_isPrototypeOf ),+ JS_CFUNC_DEF("propertyIsEnumerable", 1, js_object_propertyIsEnumerable ),+ JS_CGETSET_DEF("__proto__", js_object_get___proto__, js_object_set___proto__ ),+ JS_CFUNC_MAGIC_DEF("__defineGetter__", 2, js_object___defineGetter__, 0 ),+ JS_CFUNC_MAGIC_DEF("__defineSetter__", 2, js_object___defineGetter__, 1 ),+ JS_CFUNC_MAGIC_DEF("__lookupGetter__", 1, js_object___lookupGetter__, 0 ),+ JS_CFUNC_MAGIC_DEF("__lookupSetter__", 1, js_object___lookupGetter__, 1 ),+};++/* Function class */++static JSValue js_function_proto(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return JS_UNDEFINED;+}++/* XXX: add a specific eval mode so that Function("}), ({") is rejected */+static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target,+ int argc, JSValueConst *argv, int magic)+{+ JSFunctionKindEnum func_kind = magic;+ int i, n, ret;+ JSValue s, proto, obj = JS_UNDEFINED;+ StringBuffer b_s, *b = &b_s;++ string_buffer_init(ctx, b, 0);+ string_buffer_putc8(b, '(');+ + if (func_kind == JS_FUNC_ASYNC || func_kind == JS_FUNC_ASYNC_GENERATOR) {+ string_buffer_puts8(b, "async ");+ }+ string_buffer_puts8(b, "function");++ if (func_kind == JS_FUNC_GENERATOR || func_kind == JS_FUNC_ASYNC_GENERATOR) {+ string_buffer_putc8(b, '*');+ }+ string_buffer_puts8(b, " anonymous(");++ n = argc - 1;+ for(i = 0; i < n; i++) {+ if (i != 0) {+ string_buffer_putc8(b, ',');+ }+ if (string_buffer_concat_value(b, argv[i]))+ goto fail;+ }+ string_buffer_puts8(b, "\n) {\n");+ if (n >= 0) {+ if (string_buffer_concat_value(b, argv[n]))+ goto fail;+ }+ string_buffer_puts8(b, "\n})");+ s = string_buffer_end(b);+ if (JS_IsException(s))+ goto fail1;++ obj = JS_EvalObject(ctx, ctx->global_obj, s, JS_EVAL_TYPE_INDIRECT, -1);+ JS_FreeValue(ctx, s);+ if (JS_IsException(obj))+ goto fail1;+ if (!JS_IsUndefined(new_target)) {+ /* set the prototype */+ proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype);+ if (JS_IsException(proto))+ goto fail1;+ if (!JS_IsObject(proto)) {+ JSContext *realm;+ JS_FreeValue(ctx, proto);+ realm = JS_GetFunctionRealm(ctx, new_target);+ if (!realm)+ goto fail1;+ proto = JS_DupValue(ctx, realm->class_proto[func_kind_to_class_id[func_kind]]);+ }+ ret = JS_SetPrototypeInternal(ctx, obj, proto, TRUE);+ JS_FreeValue(ctx, proto);+ if (ret < 0)+ goto fail1;+ }+ return obj;++ fail:+ string_buffer_free(b);+ fail1:+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++static __exception int js_get_length32(JSContext *ctx, uint32_t *pres,+ JSValueConst obj)+{+ JSValue len_val;+ len_val = JS_GetProperty(ctx, obj, JS_ATOM_length);+ if (JS_IsException(len_val)) {+ *pres = 0;+ return -1;+ }+ return JS_ToUint32Free(ctx, pres, len_val);+}++static __exception int js_get_length64(JSContext *ctx, int64_t *pres,+ JSValueConst obj)+{+ JSValue len_val;+ len_val = JS_GetProperty(ctx, obj, JS_ATOM_length);+ if (JS_IsException(len_val)) {+ *pres = 0;+ return -1;+ }+ return JS_ToLengthFree(ctx, pres, len_val);+}++static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len)+{+ uint32_t i;+ for(i = 0; i < len; i++) {+ JS_FreeValue(ctx, tab[i]);+ }+ js_free(ctx, tab);+}++/* XXX: should use ValueArray */+static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen,+ JSValueConst array_arg)+{+ uint32_t len, i;+ JSValue *tab, ret;+ JSObject *p;++ if (JS_VALUE_GET_TAG(array_arg) != JS_TAG_OBJECT) {+ JS_ThrowTypeError(ctx, "not a object");+ return NULL;+ }+ if (js_get_length32(ctx, &len, array_arg))+ return NULL;+ /* avoid allocating 0 bytes */+ tab = js_mallocz(ctx, sizeof(tab[0]) * max_uint32(1, len));+ if (!tab)+ return NULL;+ p = JS_VALUE_GET_OBJ(array_arg);+ if ((p->class_id == JS_CLASS_ARRAY || p->class_id == JS_CLASS_ARGUMENTS) &&+ p->fast_array &&+ len == p->u.array.count) {+ for(i = 0; i < len; i++) {+ tab[i] = JS_DupValue(ctx, p->u.array.u.values[i]);+ }+ } else {+ for(i = 0; i < len; i++) {+ ret = JS_GetPropertyUint32(ctx, array_arg, i);+ if (JS_IsException(ret)) {+ free_arg_list(ctx, tab, i);+ return NULL;+ }+ tab[i] = ret;+ }+ }+ *plen = len;+ return tab;+}++/* magic value: 0 = normal apply, 1 = apply for constructor, 2 =+ Reflect.apply */+static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ JSValueConst this_arg, array_arg;+ uint32_t len;+ JSValue *tab, ret;++ if (check_function(ctx, this_val))+ return JS_EXCEPTION;+ this_arg = argv[0];+ array_arg = argv[1];+ if ((JS_VALUE_GET_TAG(array_arg) == JS_TAG_UNDEFINED ||+ JS_VALUE_GET_TAG(array_arg) == JS_TAG_NULL) && magic != 2) {+ return JS_Call(ctx, this_val, this_arg, 0, NULL);+ }+ tab = build_arg_list(ctx, &len, array_arg);+ if (!tab)+ return JS_EXCEPTION;+ if (magic & 1) {+ ret = JS_CallConstructor2(ctx, this_val, this_arg, len, (JSValueConst *)tab);+ } else {+ ret = JS_Call(ctx, this_val, this_arg, len, (JSValueConst *)tab);+ }+ free_arg_list(ctx, tab, len);+ return ret;+}++static JSValue js_function_call(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ if (argc <= 0) {+ return JS_Call(ctx, this_val, JS_UNDEFINED, 0, NULL);+ } else {+ return JS_Call(ctx, this_val, argv[0], argc - 1, argv + 1);+ }+}++static JSValue js_function_bind(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSBoundFunction *bf;+ JSValue func_obj, name1, len_val;+ JSObject *p;+ int arg_count, i, ret;++ if (check_function(ctx, this_val))+ return JS_EXCEPTION;++ func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,+ JS_CLASS_BOUND_FUNCTION);+ if (JS_IsException(func_obj))+ return JS_EXCEPTION;+ p = JS_VALUE_GET_OBJ(func_obj);+ p->is_constructor = JS_IsConstructor(ctx, this_val);+ arg_count = max_int(0, argc - 1);+ bf = js_malloc(ctx, sizeof(*bf) + arg_count * sizeof(JSValue));+ if (!bf)+ goto exception;+ bf->func_obj = JS_DupValue(ctx, this_val);+ bf->this_val = JS_DupValue(ctx, argv[0]);+ bf->argc = arg_count;+ for(i = 0; i < arg_count; i++) {+ bf->argv[i] = JS_DupValue(ctx, argv[i + 1]);+ }+ p->u.bound_function = bf;++ /* XXX: the spec could be simpler by only using GetOwnProperty */+ ret = JS_GetOwnProperty(ctx, NULL, this_val, JS_ATOM_length);+ if (ret < 0)+ goto exception;+ if (!ret) {+ len_val = JS_NewInt32(ctx, 0);+ } else {+ len_val = JS_GetProperty(ctx, this_val, JS_ATOM_length);+ if (JS_IsException(len_val))+ goto exception;+ if (JS_VALUE_GET_TAG(len_val) == JS_TAG_INT) {+ /* most common case */+ int len1 = JS_VALUE_GET_INT(len_val);+ if (len1 <= arg_count)+ len1 = 0;+ else+ len1 -= arg_count;+ len_val = JS_NewInt32(ctx, len1);+ } else if (JS_VALUE_GET_NORM_TAG(len_val) == JS_TAG_FLOAT64) {+ double d = JS_VALUE_GET_FLOAT64(len_val);+ if (isnan(d)) {+ d = 0.0;+ } else {+ d = trunc(d);+ if (d <= (double)arg_count)+ d = 0.0;+ else+ d -= (double)arg_count; /* also converts -0 to +0 */+ }+ len_val = JS_NewFloat64(ctx, d);+ } else {+ JS_FreeValue(ctx, len_val);+ len_val = JS_NewInt32(ctx, 0);+ }+ }+ JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length,+ len_val, JS_PROP_CONFIGURABLE);++ name1 = JS_GetProperty(ctx, this_val, JS_ATOM_name);+ if (JS_IsException(name1))+ goto exception;+ if (!JS_IsString(name1)) {+ JS_FreeValue(ctx, name1);+ name1 = JS_AtomToString(ctx, JS_ATOM_empty_string);+ }+ name1 = JS_ConcatString3(ctx, "bound ", name1, "");+ if (JS_IsException(name1))+ goto exception;+ JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name1,+ JS_PROP_CONFIGURABLE);+ return func_obj;+ exception:+ JS_FreeValue(ctx, func_obj);+ return JS_EXCEPTION;+}++static JSValue js_function_toString(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSObject *p;+ JSFunctionKindEnum func_kind = JS_FUNC_NORMAL;++ if (check_function(ctx, this_val))+ return JS_EXCEPTION;++ p = JS_VALUE_GET_OBJ(this_val);+ if (js_class_has_bytecode(p->class_id)) {+ JSFunctionBytecode *b = p->u.func.function_bytecode;+ if (b->has_debug && b->debug.source) {+ return JS_NewStringLen(ctx, b->debug.source, b->debug.source_len);+ }+ func_kind = b->func_kind;+ }+ {+ JSValue name;+ const char *pref, *suff;++ switch(func_kind) {+ default:+ case JS_FUNC_NORMAL:+ pref = "function ";+ break;+ case JS_FUNC_GENERATOR:+ pref = "function *";+ break;+ case JS_FUNC_ASYNC:+ pref = "async function ";+ break;+ case JS_FUNC_ASYNC_GENERATOR:+ pref = "async function *";+ break;+ }+ suff = "() {\n [native code]\n}";+ name = JS_GetProperty(ctx, this_val, JS_ATOM_name);+ if (JS_IsUndefined(name))+ name = JS_AtomToString(ctx, JS_ATOM_empty_string);+ return JS_ConcatString3(ctx, pref, name, suff);+ }+}++static JSValue js_function_hasInstance(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ int ret;+ ret = JS_OrdinaryIsInstanceOf(ctx, argv[0], this_val);+ if (ret < 0)+ return JS_EXCEPTION;+ else+ return JS_NewBool(ctx, ret);+}++static const JSCFunctionListEntry js_function_proto_funcs[] = {+ JS_CFUNC_DEF("call", 1, js_function_call ),+ JS_CFUNC_MAGIC_DEF("apply", 2, js_function_apply, 0 ),+ JS_CFUNC_DEF("bind", 1, js_function_bind ),+ JS_CFUNC_DEF("toString", 0, js_function_toString ),+ JS_CFUNC_DEF("[Symbol.hasInstance]", 1, js_function_hasInstance ),+ JS_CGETSET_DEF("fileName", js_function_proto_fileName, NULL ),+ JS_CGETSET_DEF("lineNumber", js_function_proto_lineNumber, NULL ),+};++/* Error class */++static JSValue iterator_to_array(JSContext *ctx, JSValueConst items)+{+ JSValue iter, next_method = JS_UNDEFINED;+ JSValue v, r = JS_UNDEFINED;+ int64_t k;+ BOOL done;+ + iter = JS_GetIterator(ctx, items, FALSE);+ if (JS_IsException(iter))+ goto exception;+ next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);+ if (JS_IsException(next_method))+ goto exception;+ r = JS_NewArray(ctx);+ if (JS_IsException(r))+ goto exception;+ for (k = 0;; k++) {+ v = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);+ if (JS_IsException(v))+ goto exception_close;+ if (done)+ break;+ if (JS_DefinePropertyValueInt64(ctx, r, k, v,+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)+ goto exception_close;+ }+ done:+ JS_FreeValue(ctx, next_method);+ JS_FreeValue(ctx, iter);+ return r;+ exception_close:+ JS_IteratorClose(ctx, iter, TRUE);+ exception:+ JS_FreeValue(ctx, r);+ r = JS_EXCEPTION;+ goto done;+}++static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target,+ int argc, JSValueConst *argv, int magic)+{+ JSValue obj, msg, proto;+ JSValueConst message;++ if (JS_IsUndefined(new_target))+ new_target = JS_GetActiveFunction(ctx);+ proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype);+ if (JS_IsException(proto))+ return proto;+ if (!JS_IsObject(proto)) {+ JSContext *realm;+ JSValueConst proto1;+ + JS_FreeValue(ctx, proto);+ realm = JS_GetFunctionRealm(ctx, new_target);+ if (!realm)+ return JS_EXCEPTION;+ if (magic < 0) {+ proto1 = realm->class_proto[JS_CLASS_ERROR];+ } else {+ proto1 = realm->native_error_proto[magic];+ }+ proto = JS_DupValue(ctx, proto1);+ }+ obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_ERROR);+ JS_FreeValue(ctx, proto);+ if (JS_IsException(obj))+ return obj;+ if (magic == JS_AGGREGATE_ERROR) {+ message = argv[1];+ } else {+ message = argv[0];+ }++ if (!JS_IsUndefined(message)) {+ msg = JS_ToString(ctx, message);+ if (unlikely(JS_IsException(msg)))+ goto exception;+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, msg,+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+ }++ if (magic == JS_AGGREGATE_ERROR) {+ JSValue error_list = iterator_to_array(ctx, argv[0]);+ if (JS_IsException(error_list))+ goto exception;+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, error_list,+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+ }++ /* skip the Error() function in the backtrace */+ build_backtrace(ctx, obj, NULL, 0, JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL);+ return obj;+ exception:+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue name, msg;++ if (!JS_IsObject(this_val))+ return JS_ThrowTypeErrorNotAnObject(ctx);+ name = JS_GetProperty(ctx, this_val, JS_ATOM_name);+ if (JS_IsUndefined(name))+ name = JS_AtomToString(ctx, JS_ATOM_Error);+ else+ name = JS_ToStringFree(ctx, name);+ if (JS_IsException(name))+ return JS_EXCEPTION;++ msg = JS_GetProperty(ctx, this_val, JS_ATOM_message);+ if (JS_IsUndefined(msg))+ msg = JS_AtomToString(ctx, JS_ATOM_empty_string);+ else+ msg = JS_ToStringFree(ctx, msg);+ if (JS_IsException(msg)) {+ JS_FreeValue(ctx, name);+ return JS_EXCEPTION;+ }+ if (!JS_IsEmptyString(name) && !JS_IsEmptyString(msg))+ name = JS_ConcatString3(ctx, "", name, ": ");+ return JS_ConcatString(ctx, name, msg);+}++static const JSCFunctionListEntry js_error_proto_funcs[] = {+ JS_CFUNC_DEF("toString", 0, js_error_toString ),+ JS_PROP_STRING_DEF("name", "Error", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),+ JS_PROP_STRING_DEF("message", "", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),+};++/* AggregateError */++/* used by C code. */+static JSValue js_aggregate_error_constructor(JSContext *ctx,+ JSValueConst errors)+{+ JSValue obj;+ + obj = JS_NewObjectProtoClass(ctx,+ ctx->native_error_proto[JS_AGGREGATE_ERROR],+ JS_CLASS_ERROR);+ if (JS_IsException(obj))+ return obj;+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, JS_DupValue(ctx, errors),+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+ return obj;+}++/* Array */++static int JS_CopySubArray(JSContext *ctx,+ JSValueConst obj, int64_t to_pos,+ int64_t from_pos, int64_t count, int dir)+{+ int64_t i, from, to;+ JSValue val;+ int fromPresent;++ /* XXX: should special case fast arrays */+ for (i = 0; i < count; i++) {+ if (dir < 0) {+ from = from_pos + count - i - 1;+ to = to_pos + count - i - 1;+ } else {+ from = from_pos + i;+ to = to_pos + i;+ }+ fromPresent = JS_TryGetPropertyInt64(ctx, obj, from, &val);+ if (fromPresent < 0)+ goto exception;++ if (fromPresent) {+ if (JS_SetPropertyInt64(ctx, obj, to, val) < 0)+ goto exception;+ } else {+ if (JS_DeletePropertyInt64(ctx, obj, to, JS_PROP_THROW) < 0)+ goto exception;+ }+ }+ return 0;++ exception:+ return -1;+}++static JSValue js_array_constructor(JSContext *ctx, JSValueConst new_target,+ int argc, JSValueConst *argv)+{+ JSValue obj;+ int i;++ obj = js_create_from_ctor(ctx, new_target, JS_CLASS_ARRAY);+ if (JS_IsException(obj))+ return obj;+ if (argc == 1 && JS_IsNumber(argv[0])) {+ uint32_t len;+ if (JS_ToArrayLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]), TRUE))+ goto fail;+ if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, len)) < 0)+ goto fail;+ } else {+ for(i = 0; i < argc; i++) {+ if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0)+ goto fail;+ }+ }+ return obj;+fail:+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++static JSValue js_array_from(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ // from(items, mapfn = void 0, this_arg = void 0)+ JSValueConst items = argv[0], mapfn, this_arg;+ JSValueConst args[2];+ JSValue stack[2];+ JSValue iter, r, v, v2, arrayLike;+ int64_t k, len;+ int done, mapping;++ mapping = FALSE;+ mapfn = JS_UNDEFINED;+ this_arg = JS_UNDEFINED;+ r = JS_UNDEFINED;+ arrayLike = JS_UNDEFINED;+ stack[0] = JS_UNDEFINED;+ stack[1] = JS_UNDEFINED;++ if (argc > 1) {+ mapfn = argv[1];+ if (!JS_IsUndefined(mapfn)) {+ if (check_function(ctx, mapfn))+ goto exception;+ mapping = 1;+ if (argc > 2)+ this_arg = argv[2];+ }+ }+ iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator);+ if (JS_IsException(iter))+ goto exception;+ if (!JS_IsUndefined(iter)) {+ JS_FreeValue(ctx, iter);+ if (JS_IsConstructor(ctx, this_val))+ r = JS_CallConstructor(ctx, this_val, 0, NULL);+ else+ r = JS_NewArray(ctx);+ if (JS_IsException(r))+ goto exception;+ stack[0] = JS_DupValue(ctx, items);+ if (js_for_of_start(ctx, &stack[1], FALSE))+ goto exception;+ for (k = 0;; k++) {+ v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done);+ if (JS_IsException(v))+ goto exception_close;+ if (done)+ break;+ if (mapping) {+ args[0] = v;+ args[1] = JS_NewInt32(ctx, k);+ v2 = JS_Call(ctx, mapfn, this_arg, 2, args);+ JS_FreeValue(ctx, v);+ v = v2;+ if (JS_IsException(v))+ goto exception_close;+ }+ if (JS_DefinePropertyValueInt64(ctx, r, k, v,+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)+ goto exception_close;+ }+ } else {+ arrayLike = JS_ToObject(ctx, items);+ if (JS_IsException(arrayLike))+ goto exception;+ if (js_get_length64(ctx, &len, arrayLike) < 0)+ goto exception;+ v = JS_NewInt64(ctx, len);+ args[0] = v;+ if (JS_IsConstructor(ctx, this_val)) {+ r = JS_CallConstructor(ctx, this_val, 1, args);+ } else {+ r = js_array_constructor(ctx, JS_UNDEFINED, 1, args);+ }+ JS_FreeValue(ctx, v);+ if (JS_IsException(r))+ goto exception;+ for(k = 0; k < len; k++) {+ v = JS_GetPropertyInt64(ctx, arrayLike, k);+ if (JS_IsException(v))+ goto exception;+ if (mapping) {+ args[0] = v;+ args[1] = JS_NewInt32(ctx, k);+ v2 = JS_Call(ctx, mapfn, this_arg, 2, args);+ JS_FreeValue(ctx, v);+ v = v2;+ if (JS_IsException(v))+ goto exception;+ }+ if (JS_DefinePropertyValueInt64(ctx, r, k, v,+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)+ goto exception;+ }+ }+ if (JS_SetProperty(ctx, r, JS_ATOM_length, JS_NewUint32(ctx, k)) < 0)+ goto exception;+ goto done;++ exception_close:+ if (!JS_IsUndefined(stack[0]))+ JS_IteratorClose(ctx, stack[0], TRUE);+ exception:+ JS_FreeValue(ctx, r);+ r = JS_EXCEPTION;+ done:+ JS_FreeValue(ctx, arrayLike);+ JS_FreeValue(ctx, stack[0]);+ JS_FreeValue(ctx, stack[1]);+ return r;+}++static JSValue js_array_of(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue obj, args[1];+ int i;++ if (JS_IsConstructor(ctx, this_val)) {+ args[0] = JS_NewInt32(ctx, argc);+ obj = JS_CallConstructor(ctx, this_val, 1, (JSValueConst *)args);+ } else {+ obj = JS_NewArray(ctx);+ }+ if (JS_IsException(obj))+ return JS_EXCEPTION;+ for(i = 0; i < argc; i++) {+ if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i]),+ JS_PROP_THROW) < 0) {+ goto fail;+ }+ }+ if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, argc)) < 0) {+ fail:+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+ }+ return obj;+}++static JSValue js_array_isArray(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ int ret;+ ret = JS_IsArray(ctx, argv[0]);+ if (ret < 0)+ return JS_EXCEPTION;+ else+ return JS_NewBool(ctx, ret);+}++static JSValue js_get_this(JSContext *ctx,+ JSValueConst this_val)+{+ return JS_DupValue(ctx, this_val);+}++static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValueConst obj,+ JSValueConst len_val)+{+ JSValue ctor, ret, species;+ int res;+ JSContext *realm;+ + res = JS_IsArray(ctx, obj);+ if (res < 0)+ return JS_EXCEPTION;+ if (!res)+ return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val);+ ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor);+ if (JS_IsException(ctor))+ return ctor;+ if (JS_IsConstructor(ctx, ctor)) {+ /* legacy web compatibility */+ realm = JS_GetFunctionRealm(ctx, ctor);+ if (!realm) {+ JS_FreeValue(ctx, ctor);+ return JS_EXCEPTION;+ }+ if (realm != ctx &&+ js_same_value(ctx, ctor, realm->array_ctor)) {+ JS_FreeValue(ctx, ctor);+ ctor = JS_UNDEFINED;+ }+ }+ if (JS_IsObject(ctor)) {+ species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species);+ JS_FreeValue(ctx, ctor);+ if (JS_IsException(species))+ return species;+ ctor = species;+ if (JS_IsNull(ctor))+ ctor = JS_UNDEFINED;+ }+ if (JS_IsUndefined(ctor)) {+ return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val);+ } else {+ ret = JS_CallConstructor(ctx, ctor, 1, &len_val);+ JS_FreeValue(ctx, ctor);+ return ret;+ }+}++static const JSCFunctionListEntry js_array_funcs[] = {+ JS_CFUNC_DEF("isArray", 1, js_array_isArray ),+ JS_CFUNC_DEF("from", 1, js_array_from ),+ JS_CFUNC_DEF("of", 0, js_array_of ),+ JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),+};++static int JS_isConcatSpreadable(JSContext *ctx, JSValueConst obj)+{+ JSValue val;++ if (!JS_IsObject(obj))+ return FALSE;+ val = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_isConcatSpreadable);+ if (JS_IsException(val))+ return -1;+ if (!JS_IsUndefined(val))+ return JS_ToBoolFree(ctx, val);+ return JS_IsArray(ctx, obj);+}++static JSValue js_array_concat(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue obj, arr, val;+ JSValueConst e;+ int64_t len, k, n;+ int i, res;++ arr = JS_UNDEFINED;+ obj = JS_ToObject(ctx, this_val);+ if (JS_IsException(obj))+ goto exception;++ arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));+ if (JS_IsException(arr))+ goto exception;+ n = 0;+ for (i = -1; i < argc; i++) {+ if (i < 0)+ e = obj;+ else+ e = argv[i];++ res = JS_isConcatSpreadable(ctx, e);+ if (res < 0)+ goto exception;+ if (res) {+ if (js_get_length64(ctx, &len, e))+ goto exception;+ if (n + len > MAX_SAFE_INTEGER) {+ JS_ThrowTypeError(ctx, "Array loo long");+ goto exception;+ }+ for (k = 0; k < len; k++, n++) {+ res = JS_TryGetPropertyInt64(ctx, e, k, &val);+ if (res < 0)+ goto exception;+ if (res) {+ if (JS_DefinePropertyValueInt64(ctx, arr, n, val,+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)+ goto exception;+ }+ }+ } else {+ if (n >= MAX_SAFE_INTEGER) {+ JS_ThrowTypeError(ctx, "Array loo long");+ goto exception;+ }+ if (JS_DefinePropertyValueInt64(ctx, arr, n, JS_DupValue(ctx, e),+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)+ goto exception;+ n++;+ }+ }+ if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0)+ goto exception;++ JS_FreeValue(ctx, obj);+ return arr;++exception:+ JS_FreeValue(ctx, arr);+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++#define special_every 0+#define special_some 1+#define special_forEach 2+#define special_map 3+#define special_filter 4+#define special_TA 8++static int js_typed_array_get_length_internal(JSContext *ctx, JSValueConst obj);++static JSValue js_typed_array___speciesCreate(JSContext *ctx,+ JSValueConst this_val,+ int argc, JSValueConst *argv);++static JSValue js_array_every(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int special)+{+ JSValue obj, val, index_val, res, ret;+ JSValueConst args[3];+ JSValueConst func, this_arg;+ int64_t len, k, n;+ int present;++ ret = JS_UNDEFINED;+ val = JS_UNDEFINED;+ if (special & special_TA) {+ obj = JS_DupValue(ctx, this_val);+ len = js_typed_array_get_length_internal(ctx, obj);+ if (len < 0)+ goto exception;+ } else {+ obj = JS_ToObject(ctx, this_val);+ if (js_get_length64(ctx, &len, obj))+ goto exception;+ }+ func = argv[0];+ this_arg = JS_UNDEFINED;+ if (argc > 1)+ this_arg = argv[1];+ + if (check_function(ctx, func))+ goto exception;++ switch (special) {+ case special_every:+ case special_every | special_TA:+ ret = JS_TRUE;+ break;+ case special_some:+ case special_some | special_TA:+ ret = JS_FALSE;+ break;+ case special_map:+ /* XXX: JS_ArraySpeciesCreate should take int64_t */+ ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt64(ctx, len));+ if (JS_IsException(ret))+ goto exception;+ break;+ case special_filter:+ ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));+ if (JS_IsException(ret))+ goto exception;+ break;+ case special_map | special_TA:+ args[0] = obj;+ args[1] = JS_NewInt32(ctx, len);+ ret = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);+ if (JS_IsException(ret))+ goto exception;+ break;+ case special_filter | special_TA:+ ret = JS_NewArray(ctx);+ if (JS_IsException(ret))+ goto exception;+ break;+ }+ n = 0;++ for(k = 0; k < len; k++) {+ present = JS_TryGetPropertyInt64(ctx, obj, k, &val);+ if (present < 0)+ goto exception;+ if (present) {+ index_val = JS_NewInt64(ctx, k);+ if (JS_IsException(index_val))+ goto exception;+ args[0] = val;+ args[1] = index_val;+ args[2] = obj;+ res = JS_Call(ctx, func, this_arg, 3, args);+ JS_FreeValue(ctx, index_val);+ if (JS_IsException(res))+ goto exception;+ switch (special) {+ case special_every:+ case special_every | special_TA:+ if (!JS_ToBoolFree(ctx, res)) {+ ret = JS_FALSE;+ goto done;+ }+ break;+ case special_some:+ case special_some | special_TA:+ if (JS_ToBoolFree(ctx, res)) {+ ret = JS_TRUE;+ goto done;+ }+ break;+ case special_map:+ if (JS_DefinePropertyValueInt64(ctx, ret, k, res,+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)+ goto exception;+ break;+ case special_map | special_TA:+ if (JS_SetPropertyValue(ctx, ret, JS_NewInt32(ctx, k), res, JS_PROP_THROW) < 0)+ goto exception;+ break;+ case special_filter:+ case special_filter | special_TA:+ if (JS_ToBoolFree(ctx, res)) {+ if (JS_DefinePropertyValueInt64(ctx, ret, n++, JS_DupValue(ctx, val),+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)+ goto exception;+ }+ break;+ default:+ JS_FreeValue(ctx, res);+ break;+ }+ JS_FreeValue(ctx, val);+ val = JS_UNDEFINED;+ }+ }+done:+ if (special == (special_filter | special_TA)) {+ JSValue arr;+ args[0] = obj;+ args[1] = JS_NewInt32(ctx, n);+ arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);+ if (JS_IsException(arr))+ goto exception;+ args[0] = ret;+ res = JS_Invoke(ctx, arr, JS_ATOM_set, 1, args);+ if (check_exception_free(ctx, res))+ goto exception;+ JS_FreeValue(ctx, ret);+ ret = arr;+ }+ JS_FreeValue(ctx, val);+ JS_FreeValue(ctx, obj);+ return ret;++exception:+ JS_FreeValue(ctx, ret);+ JS_FreeValue(ctx, val);+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++#define special_reduce 0+#define special_reduceRight 1++static JSValue js_array_reduce(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int special)+{+ JSValue obj, val, index_val, acc, acc1;+ JSValueConst args[4];+ JSValueConst func;+ int64_t len, k, k1;+ int present;++ acc = JS_UNDEFINED;+ val = JS_UNDEFINED;+ if (special & special_TA) {+ obj = JS_DupValue(ctx, this_val);+ len = js_typed_array_get_length_internal(ctx, obj);+ if (len < 0)+ goto exception;+ } else {+ obj = JS_ToObject(ctx, this_val);+ if (js_get_length64(ctx, &len, obj))+ goto exception;+ }+ func = argv[0];++ if (check_function(ctx, func))+ goto exception;++ k = 0;+ if (argc > 1) {+ acc = JS_DupValue(ctx, argv[1]);+ } else {+ for(;;) {+ if (k >= len) {+ JS_ThrowTypeError(ctx, "empty array");+ goto exception;+ }+ k1 = (special & special_reduceRight) ? len - k - 1 : k;+ k++;+ present = JS_TryGetPropertyInt64(ctx, obj, k1, &acc);+ if (present < 0)+ goto exception;+ if (present)+ break;+ }+ }+ for (; k < len; k++) {+ k1 = (special & special_reduceRight) ? len - k - 1 : k;+ present = JS_TryGetPropertyInt64(ctx, obj, k1, &val);+ if (present < 0)+ goto exception;+ if (present) {+ index_val = JS_NewInt64(ctx, k1);+ if (JS_IsException(index_val))+ goto exception;+ args[0] = acc;+ args[1] = val;+ args[2] = index_val;+ args[3] = obj;+ acc1 = JS_Call(ctx, func, JS_UNDEFINED, 4, args);+ JS_FreeValue(ctx, index_val);+ JS_FreeValue(ctx, val);+ val = JS_UNDEFINED;+ if (JS_IsException(acc1))+ goto exception;+ JS_FreeValue(ctx, acc);+ acc = acc1;+ }+ }+ JS_FreeValue(ctx, obj);+ return acc;++exception:+ JS_FreeValue(ctx, acc);+ JS_FreeValue(ctx, val);+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++static JSValue js_array_fill(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue obj;+ int64_t len, start, end;++ obj = JS_ToObject(ctx, this_val);+ if (js_get_length64(ctx, &len, obj))+ goto exception;++ start = 0;+ if (argc > 1 && !JS_IsUndefined(argv[1])) {+ if (JS_ToInt64Clamp(ctx, &start, argv[1], 0, len, len))+ goto exception;+ }++ end = len;+ if (argc > 2 && !JS_IsUndefined(argv[2])) {+ if (JS_ToInt64Clamp(ctx, &end, argv[2], 0, len, len))+ goto exception;+ }++ /* XXX: should special case fast arrays */+ while (start < end) {+ if (JS_SetPropertyInt64(ctx, obj, start,+ JS_DupValue(ctx, argv[0])) < 0)+ goto exception;+ start++;+ }+ return obj;++ exception:+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++static JSValue js_array_includes(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue obj, val;+ int64_t len, n, res;+ JSValue *arrp;+ uint32_t count;++ obj = JS_ToObject(ctx, this_val);+ if (js_get_length64(ctx, &len, obj))+ goto exception;++ res = FALSE;+ if (len > 0) {+ n = 0;+ if (argc > 1) {+ if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len))+ goto exception;+ }+ if (js_get_fast_array(ctx, obj, &arrp, &count)) {+ for (; n < count; n++) {+ if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]),+ JS_DupValue(ctx, arrp[n]),+ JS_EQ_SAME_VALUE_ZERO)) {+ res = TRUE;+ goto done;+ }+ }+ }+ for (; n < len; n++) {+ val = JS_GetPropertyInt64(ctx, obj, n);+ if (JS_IsException(val))+ goto exception;+ if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val,+ JS_EQ_SAME_VALUE_ZERO)) {+ res = TRUE;+ break;+ }+ }+ }+ done:+ JS_FreeValue(ctx, obj);+ return JS_NewBool(ctx, res);++ exception:+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++static JSValue js_array_indexOf(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue obj, val;+ int64_t len, n, res;+ JSValue *arrp;+ uint32_t count;++ obj = JS_ToObject(ctx, this_val);+ if (js_get_length64(ctx, &len, obj))+ goto exception;++ res = -1;+ if (len > 0) {+ n = 0;+ if (argc > 1) {+ if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len))+ goto exception;+ }+ if (js_get_fast_array(ctx, obj, &arrp, &count)) {+ for (; n < count; n++) {+ if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]),+ JS_DupValue(ctx, arrp[n]), JS_EQ_STRICT)) {+ res = n;+ goto done;+ }+ }+ }+ for (; n < len; n++) {+ int present = JS_TryGetPropertyInt64(ctx, obj, n, &val);+ if (present < 0)+ goto exception;+ if (present) {+ if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) {+ res = n;+ break;+ }+ }+ }+ }+ done:+ JS_FreeValue(ctx, obj);+ return JS_NewInt64(ctx, res);++ exception:+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++static JSValue js_array_lastIndexOf(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue obj, val;+ int64_t len, n, res;+ int present;++ obj = JS_ToObject(ctx, this_val);+ if (js_get_length64(ctx, &len, obj))+ goto exception;++ res = -1;+ if (len > 0) {+ n = len - 1;+ if (argc > 1) {+ if (JS_ToInt64Clamp(ctx, &n, argv[1], -1, len - 1, len))+ goto exception;+ }+ /* XXX: should special case fast arrays */+ for (; n >= 0; n--) {+ present = JS_TryGetPropertyInt64(ctx, obj, n, &val);+ if (present < 0)+ goto exception;+ if (present) {+ if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) {+ res = n;+ break;+ }+ }+ }+ }+ JS_FreeValue(ctx, obj);+ return JS_NewInt64(ctx, res);++ exception:+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++static JSValue js_array_find(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int findIndex)+{+ JSValueConst func, this_arg;+ JSValueConst args[3];+ JSValue obj, val, index_val, res;+ int64_t len, k;++ index_val = JS_UNDEFINED;+ val = JS_UNDEFINED;+ obj = JS_ToObject(ctx, this_val);+ if (js_get_length64(ctx, &len, obj))+ goto exception;++ func = argv[0];+ if (check_function(ctx, func))+ goto exception;++ this_arg = JS_UNDEFINED;+ if (argc > 1)+ this_arg = argv[1];++ for(k = 0; k < len; k++) {+ index_val = JS_NewInt64(ctx, k);+ if (JS_IsException(index_val))+ goto exception;+ val = JS_GetPropertyValue(ctx, obj, index_val);+ if (JS_IsException(val))+ goto exception;+ args[0] = val;+ args[1] = index_val;+ args[2] = this_val;+ res = JS_Call(ctx, func, this_arg, 3, args);+ if (JS_IsException(res))+ goto exception;+ if (JS_ToBoolFree(ctx, res)) {+ if (findIndex) {+ JS_FreeValue(ctx, val);+ JS_FreeValue(ctx, obj);+ return index_val;+ } else {+ JS_FreeValue(ctx, index_val);+ JS_FreeValue(ctx, obj);+ return val;+ }+ }+ JS_FreeValue(ctx, val);+ JS_FreeValue(ctx, index_val);+ }+ JS_FreeValue(ctx, obj);+ if (findIndex)+ return JS_NewInt32(ctx, -1);+ else+ return JS_UNDEFINED;++exception:+ JS_FreeValue(ctx, index_val);+ JS_FreeValue(ctx, val);+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++static JSValue js_array_toString(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue obj, method, ret;++ obj = JS_ToObject(ctx, this_val);+ if (JS_IsException(obj))+ return JS_EXCEPTION;+ method = JS_GetProperty(ctx, obj, JS_ATOM_join);+ if (JS_IsException(method)) {+ ret = JS_EXCEPTION;+ } else+ if (!JS_IsFunction(ctx, method)) {+ /* Use intrinsic Object.prototype.toString */+ JS_FreeValue(ctx, method);+ ret = js_object_toString(ctx, obj, 0, NULL);+ } else {+ ret = JS_CallFree(ctx, method, obj, 0, NULL);+ }+ JS_FreeValue(ctx, obj);+ return ret;+}++static JSValue js_array_join(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int toLocaleString)+{+ JSValue obj, sep = JS_UNDEFINED, el;+ StringBuffer b_s, *b = &b_s;+ JSString *p = NULL;+ int64_t i, n;+ int c;++ obj = JS_ToObject(ctx, this_val);+ if (js_get_length64(ctx, &n, obj))+ goto exception;++ c = ','; /* default separator */+ if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) {+ sep = JS_ToString(ctx, argv[0]);+ if (JS_IsException(sep))+ goto exception;+ p = JS_VALUE_GET_STRING(sep);+ if (p->len == 1 && !p->is_wide_char)+ c = p->u.str8[0];+ else+ c = -1;+ }+ string_buffer_init(ctx, b, 0);++ for(i = 0; i < n; i++) {+ if (i > 0) {+ if (c >= 0) {+ string_buffer_putc8(b, c);+ } else {+ string_buffer_concat(b, p, 0, p->len);+ }+ }+ el = JS_GetPropertyUint32(ctx, obj, i);+ if (JS_IsException(el))+ goto fail;+ if (!JS_IsNull(el) && !JS_IsUndefined(el)) {+ if (toLocaleString) {+ el = JS_ToLocaleStringFree(ctx, el);+ }+ if (string_buffer_concat_value_free(b, el))+ goto fail;+ }+ }+ JS_FreeValue(ctx, sep);+ JS_FreeValue(ctx, obj);+ return string_buffer_end(b);++fail:+ string_buffer_free(b);+ JS_FreeValue(ctx, sep);+exception:+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++static JSValue js_array_pop(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int shift)+{+ JSValue obj, res = JS_UNDEFINED;+ int64_t len, newLen;+ JSValue *arrp;+ uint32_t count32;++ obj = JS_ToObject(ctx, this_val);+ if (js_get_length64(ctx, &len, obj))+ goto exception;+ newLen = 0;+ if (len > 0) {+ newLen = len - 1;+ /* Special case fast arrays */+ if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {+ JSObject *p = JS_VALUE_GET_OBJ(obj);+ if (shift) {+ res = arrp[0];+ memmove(arrp, arrp + 1, (count32 - 1) * sizeof(*arrp));+ p->u.array.count--;+ } else {+ res = arrp[count32 - 1];+ p->u.array.count--;+ }+ } else {+ if (shift) {+ res = JS_GetPropertyInt64(ctx, obj, 0);+ if (JS_IsException(res))+ goto exception;+ if (JS_CopySubArray(ctx, obj, 0, 1, len - 1, +1))+ goto exception;+ } else {+ res = JS_GetPropertyInt64(ctx, obj, newLen);+ if (JS_IsException(res))+ goto exception;+ }+ if (JS_DeletePropertyInt64(ctx, obj, newLen, JS_PROP_THROW) < 0)+ goto exception;+ }+ }+ if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0)+ goto exception;++ JS_FreeValue(ctx, obj);+ return res;++ exception:+ JS_FreeValue(ctx, res);+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++static JSValue js_array_push(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int unshift)+{+ JSValue obj;+ int i;+ int64_t len, from, newLen;++ obj = JS_ToObject(ctx, this_val);+ if (js_get_length64(ctx, &len, obj))+ goto exception;+ newLen = len + argc;+ if (newLen > MAX_SAFE_INTEGER) {+ JS_ThrowTypeError(ctx, "Array loo long");+ goto exception;+ }+ from = len;+ if (unshift && argc > 0) {+ if (JS_CopySubArray(ctx, obj, argc, 0, len, -1))+ goto exception;+ from = 0;+ }+ for(i = 0; i < argc; i++) {+ if (JS_SetPropertyInt64(ctx, obj, from + i,+ JS_DupValue(ctx, argv[i])) < 0)+ goto exception;+ }+ if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0)+ goto exception;++ JS_FreeValue(ctx, obj);+ return JS_NewInt64(ctx, newLen);++ exception:+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++static JSValue js_array_reverse(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue obj, lval, hval;+ JSValue *arrp;+ int64_t len, l, h;+ int l_present, h_present;+ uint32_t count32;++ lval = JS_UNDEFINED;+ obj = JS_ToObject(ctx, this_val);+ if (js_get_length64(ctx, &len, obj))+ goto exception;++ /* Special case fast arrays */+ if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {+ uint32_t ll, hh;++ if (count32 > 1) {+ for (ll = 0, hh = count32 - 1; ll < hh; ll++, hh--) {+ lval = arrp[ll];+ arrp[ll] = arrp[hh];+ arrp[hh] = lval;+ }+ }+ return obj;+ }++ for (l = 0, h = len - 1; l < h; l++, h--) {+ l_present = JS_TryGetPropertyInt64(ctx, obj, l, &lval);+ if (l_present < 0)+ goto exception;+ h_present = JS_TryGetPropertyInt64(ctx, obj, h, &hval);+ if (h_present < 0)+ goto exception;+ if (h_present) {+ if (JS_SetPropertyInt64(ctx, obj, l, hval) < 0)+ goto exception;++ if (l_present) {+ if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) {+ lval = JS_UNDEFINED;+ goto exception;+ }+ lval = JS_UNDEFINED;+ } else {+ if (JS_DeletePropertyInt64(ctx, obj, h, JS_PROP_THROW) < 0)+ goto exception;+ }+ } else {+ if (l_present) {+ if (JS_DeletePropertyInt64(ctx, obj, l, JS_PROP_THROW) < 0)+ goto exception;+ if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) {+ lval = JS_UNDEFINED;+ goto exception;+ }+ lval = JS_UNDEFINED;+ }+ }+ }+ return obj;++ exception:+ JS_FreeValue(ctx, lval);+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int splice)+{+ JSValue obj, arr, val, len_val;+ int64_t len, start, k, final, n, count, del_count, new_len;+ int kPresent;+ JSValue *arrp;+ uint32_t count32, i, item_count;++ arr = JS_UNDEFINED;+ obj = JS_ToObject(ctx, this_val);+ if (js_get_length64(ctx, &len, obj))+ goto exception;++ if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len))+ goto exception;++ if (splice) {+ if (argc == 0) {+ item_count = 0;+ del_count = 0;+ } else+ if (argc == 1) {+ item_count = 0;+ del_count = len - start;+ } else {+ item_count = argc - 2;+ if (JS_ToInt64Clamp(ctx, &del_count, argv[1], 0, len - start, 0))+ goto exception;+ }+ if (len + item_count - del_count > MAX_SAFE_INTEGER) {+ JS_ThrowTypeError(ctx, "Array loo long");+ goto exception;+ }+ count = del_count;+ } else {+ item_count = 0; /* avoid warning */+ final = len;+ if (!JS_IsUndefined(argv[1])) {+ if (JS_ToInt64Clamp(ctx, &final, argv[1], 0, len, len))+ goto exception;+ }+ count = max_int64(final - start, 0);+ }+ len_val = JS_NewInt64(ctx, count);+ arr = JS_ArraySpeciesCreate(ctx, obj, len_val);+ JS_FreeValue(ctx, len_val);+ if (JS_IsException(arr))+ goto exception;++ k = start;+ final = start + count;+ n = 0;+ /* The fast array test on arr ensures that+ JS_CreateDataPropertyUint32() won't modify obj in case arr is+ an exotic object */+ /* Special case fast arrays */+ if (js_get_fast_array(ctx, obj, &arrp, &count32) &&+ js_is_fast_array(ctx, arr)) {+ /* XXX: should share code with fast array constructor */+ for (; k < final && k < count32; k++, n++) {+ if (JS_CreateDataPropertyUint32(ctx, arr, n, JS_DupValue(ctx, arrp[k]), JS_PROP_THROW) < 0)+ goto exception;+ }+ }+ /* Copy the remaining elements if any (handle case of inherited properties) */+ for (; k < final; k++, n++) {+ kPresent = JS_TryGetPropertyInt64(ctx, obj, k, &val);+ if (kPresent < 0)+ goto exception;+ if (kPresent) {+ if (JS_CreateDataPropertyUint32(ctx, arr, n, val, JS_PROP_THROW) < 0)+ goto exception;+ }+ }+ if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0)+ goto exception;++ if (splice) {+ new_len = len + item_count - del_count;+ if (item_count != del_count) {+ if (JS_CopySubArray(ctx, obj, start + item_count,+ start + del_count, len - (start + del_count),+ item_count <= del_count ? +1 : -1) < 0)+ goto exception;++ for (k = len; k-- > new_len; ) {+ if (JS_DeletePropertyInt64(ctx, obj, k, JS_PROP_THROW) < 0)+ goto exception;+ }+ }+ for (i = 0; i < item_count; i++) {+ if (JS_SetPropertyInt64(ctx, obj, start + i, JS_DupValue(ctx, argv[i + 2])) < 0)+ goto exception;+ }+ if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, new_len)) < 0)+ goto exception;+ }+ JS_FreeValue(ctx, obj);+ return arr;++ exception:+ JS_FreeValue(ctx, obj);+ JS_FreeValue(ctx, arr);+ return JS_EXCEPTION;+}++static JSValue js_array_copyWithin(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue obj;+ int64_t len, from, to, final, count;++ obj = JS_ToObject(ctx, this_val);+ if (js_get_length64(ctx, &len, obj))+ goto exception;++ if (JS_ToInt64Clamp(ctx, &to, argv[0], 0, len, len))+ goto exception;++ if (JS_ToInt64Clamp(ctx, &from, argv[1], 0, len, len))+ goto exception;++ final = len;+ if (argc > 2 && !JS_IsUndefined(argv[2])) {+ if (JS_ToInt64Clamp(ctx, &final, argv[2], 0, len, len))+ goto exception;+ }++ count = min_int64(final - from, len - to);++ if (JS_CopySubArray(ctx, obj, to, from, count,+ (from < to && to < from + count) ? -1 : +1))+ goto exception;++ return obj;++ exception:+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValueConst target,+ JSValueConst source, int64_t sourceLen,+ int64_t targetIndex, int depth,+ JSValueConst mapperFunction,+ JSValueConst thisArg)+{+ JSValue element;+ int64_t sourceIndex, elementLen;+ int present, is_array;++ for (sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) {+ present = JS_TryGetPropertyInt64(ctx, source, sourceIndex, &element);+ if (present < 0)+ return -1;+ if (!present)+ continue;+ if (!JS_IsUndefined(mapperFunction)) {+ JSValueConst args[3] = { element, JS_NewInt64(ctx, sourceIndex), source };+ element = JS_Call(ctx, mapperFunction, thisArg, 3, args);+ JS_FreeValue(ctx, (JSValue)args[0]);+ JS_FreeValue(ctx, (JSValue)args[1]);+ if (JS_IsException(element))+ return -1;+ }+ if (depth > 0) {+ is_array = JS_IsArray(ctx, element);+ if (is_array < 0)+ goto fail;+ if (is_array) {+ if (js_get_length64(ctx, &elementLen, element) < 0)+ goto fail;+ targetIndex = JS_FlattenIntoArray(ctx, target, element,+ elementLen, targetIndex,+ depth - 1,+ JS_UNDEFINED, JS_UNDEFINED);+ if (targetIndex < 0)+ goto fail;+ JS_FreeValue(ctx, element);+ continue;+ }+ }+ if (targetIndex >= MAX_SAFE_INTEGER) {+ JS_ThrowTypeError(ctx, "Array too long");+ goto fail;+ }+ if (JS_DefinePropertyValueInt64(ctx, target, targetIndex, element,+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)+ return -1;+ targetIndex++;+ }+ return targetIndex;++fail:+ JS_FreeValue(ctx, element);+ return -1;+}++static JSValue js_array_flatten(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int map)+{+ JSValue obj, arr;+ JSValueConst mapperFunction, thisArg;+ int64_t sourceLen;+ int depthNum;++ arr = JS_UNDEFINED;+ obj = JS_ToObject(ctx, this_val);+ if (js_get_length64(ctx, &sourceLen, obj))+ goto exception;++ depthNum = 1;+ mapperFunction = JS_UNDEFINED;+ thisArg = JS_UNDEFINED;+ if (map) {+ mapperFunction = argv[0];+ if (argc > 1) {+ thisArg = argv[1];+ }+ if (check_function(ctx, mapperFunction))+ goto exception;+ } else {+ if (argc > 0 && !JS_IsUndefined(argv[0])) {+ if (JS_ToInt32Sat(ctx, &depthNum, argv[0]) < 0)+ goto exception;+ }+ }+ arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));+ if (JS_IsException(arr))+ goto exception;+ if (JS_FlattenIntoArray(ctx, arr, obj, sourceLen, 0, depthNum,+ mapperFunction, thisArg) < 0)+ goto exception;+ JS_FreeValue(ctx, obj);+ return arr;++exception:+ JS_FreeValue(ctx, obj);+ JS_FreeValue(ctx, arr);+ return JS_EXCEPTION;+}++/* Array sort */++typedef struct ValueSlot {+ JSValue val;+ JSString *str;+ int64_t pos;+} ValueSlot;++struct array_sort_context {+ JSContext *ctx;+ int exception;+ int has_method;+ JSValueConst method;+};++static int js_array_cmp_generic(const void *a, const void *b, void *opaque) {+ struct array_sort_context *psc = opaque;+ JSContext *ctx = psc->ctx;+ JSValueConst argv[2];+ JSValue res;+ ValueSlot *ap = (ValueSlot *)(void *)a;+ ValueSlot *bp = (ValueSlot *)(void *)b;+ int cmp;++ if (psc->exception)+ return 0;++ if (psc->has_method) {+ /* custom sort function is specified as returning 0 for identical+ * objects: avoid method call overhead.+ */+ if (!memcmp(&ap->val, &bp->val, sizeof(ap->val)))+ goto cmp_same;+ argv[0] = ap->val;+ argv[1] = bp->val;+ res = JS_Call(ctx, psc->method, JS_UNDEFINED, 2, argv);+ if (JS_IsException(res))+ goto exception;+ if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) {+ int val = JS_VALUE_GET_INT(res);+ cmp = (val > 0) - (val < 0);+ } else {+ double val;+ if (JS_ToFloat64Free(ctx, &val, res) < 0)+ goto exception;+ cmp = (val > 0) - (val < 0);+ }+ } else {+ /* Not supposed to bypass ToString even for identical objects as+ * tested in test262/test/built-ins/Array/prototype/sort/bug_596_1.js+ */+ if (!ap->str) {+ JSValue str = JS_ToString(ctx, ap->val);+ if (JS_IsException(str))+ goto exception;+ ap->str = JS_VALUE_GET_STRING(str);+ }+ if (!bp->str) {+ JSValue str = JS_ToString(ctx, bp->val);+ if (JS_IsException(str))+ goto exception;+ bp->str = JS_VALUE_GET_STRING(str);+ }+ cmp = js_string_compare(ctx, ap->str, bp->str);+ }+ if (cmp != 0)+ return cmp;+cmp_same:+ /* make sort stable: compare array offsets */+ return (ap->pos > bp->pos) - (ap->pos < bp->pos);++exception:+ psc->exception = 1;+ return 0;+}++static JSValue js_array_sort(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ struct array_sort_context asc = { ctx, 0, 0, argv[0] };+ JSValue obj = JS_UNDEFINED;+ ValueSlot *array = NULL;+ size_t array_size = 0, pos = 0, n = 0;+ int64_t i, len, undefined_count = 0;+ int present;++ if (!JS_IsUndefined(asc.method)) {+ if (check_function(ctx, asc.method))+ goto exception;+ asc.has_method = 1;+ }+ obj = JS_ToObject(ctx, this_val);+ if (js_get_length64(ctx, &len, obj))+ goto exception;++ /* XXX: should special case fast arrays */+ for (i = 0; i < len; i++) {+ if (pos >= array_size) {+ size_t new_size, slack;+ ValueSlot *new_array;+ new_size = (array_size + (array_size >> 1) + 31) & ~15;+ new_array = js_realloc2(ctx, array, new_size * sizeof(*array), &slack);+ if (new_array == NULL)+ goto exception;+ new_size += slack / sizeof(*new_array);+ array = new_array;+ array_size = new_size;+ }+ present = JS_TryGetPropertyInt64(ctx, obj, i, &array[pos].val);+ if (present < 0)+ goto exception;+ if (present == 0)+ continue;+ if (JS_IsUndefined(array[pos].val)) {+ undefined_count++;+ continue;+ }+ array[pos].str = NULL;+ array[pos].pos = i;+ pos++;+ }+ rqsort(array, pos, sizeof(*array), js_array_cmp_generic, &asc);+ if (asc.exception)+ goto exception;++ /* XXX: should special case fast arrays */+ while (n < pos) {+ if (array[n].str)+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str));+ if (array[n].pos == n) {+ JS_FreeValue(ctx, array[n].val);+ } else {+ if (JS_SetPropertyInt64(ctx, obj, n, array[n].val) < 0) {+ n++;+ goto exception;+ }+ }+ n++;+ }+ js_free(ctx, array);+ for (i = n; undefined_count-- > 0; i++) {+ if (JS_SetPropertyInt64(ctx, obj, i, JS_UNDEFINED) < 0)+ goto fail;+ }+ for (; i < len; i++) {+ if (JS_DeletePropertyInt64(ctx, obj, i, JS_PROP_THROW) < 0)+ goto fail;+ }+ return obj;++exception:+ for (; n < pos; n++) {+ JS_FreeValue(ctx, array[n].val);+ if (array[n].str)+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str));+ }+ js_free(ctx, array);+fail:+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++typedef struct JSArrayIteratorData {+ JSValue obj;+ JSIteratorKindEnum kind;+ uint32_t idx;+} JSArrayIteratorData;++static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val)+{+ JSObject *p = JS_VALUE_GET_OBJ(val);+ JSArrayIteratorData *it = p->u.array_iterator_data;+ if (it) {+ JS_FreeValueRT(rt, it->obj);+ js_free_rt(rt, it);+ }+}++static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func)+{+ JSObject *p = JS_VALUE_GET_OBJ(val);+ JSArrayIteratorData *it = p->u.array_iterator_data;+ if (it) {+ JS_MarkValue(rt, it->obj, mark_func);+ }+}++static JSValue js_create_array(JSContext *ctx, int len, JSValueConst *tab)+{+ JSValue obj;+ int i;++ obj = JS_NewArray(ctx);+ if (JS_IsException(obj))+ return JS_EXCEPTION;+ for(i = 0; i < len; i++) {+ if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, tab[i]), 0) < 0) {+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+ }+ }+ return obj;+}++static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ JSValue enum_obj, arr;+ JSArrayIteratorData *it;+ JSIteratorKindEnum kind;+ int class_id;++ kind = magic & 3;+ if (magic & 4) {+ /* string iterator case */+ arr = JS_ToStringCheckObject(ctx, this_val);+ class_id = JS_CLASS_STRING_ITERATOR;+ } else {+ arr = JS_ToObject(ctx, this_val);+ class_id = JS_CLASS_ARRAY_ITERATOR;+ }+ if (JS_IsException(arr))+ goto fail;+ enum_obj = JS_NewObjectClass(ctx, class_id);+ if (JS_IsException(enum_obj))+ goto fail;+ it = js_malloc(ctx, sizeof(*it));+ if (!it)+ goto fail1;+ it->obj = arr;+ it->kind = kind;+ it->idx = 0;+ JS_SetOpaque(enum_obj, it);+ return enum_obj;+ fail1:+ JS_FreeValue(ctx, enum_obj);+ fail:+ JS_FreeValue(ctx, arr);+ return JS_EXCEPTION;+}++static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv,+ BOOL *pdone, int magic)+{+ JSArrayIteratorData *it;+ uint32_t len, idx;+ JSValue val, obj;+ JSObject *p;++ it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_ITERATOR);+ if (!it)+ goto fail1;+ if (JS_IsUndefined(it->obj))+ goto done;+ p = JS_VALUE_GET_OBJ(it->obj);+ if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {+ if (typed_array_is_detached(ctx, p)) {+ JS_ThrowTypeErrorDetachedArrayBuffer(ctx);+ goto fail1;+ }+ len = p->u.array.count;+ } else {+ if (js_get_length32(ctx, &len, it->obj)) {+ fail1:+ *pdone = FALSE;+ return JS_EXCEPTION;+ }+ }+ idx = it->idx;+ if (idx >= len) {+ JS_FreeValue(ctx, it->obj);+ it->obj = JS_UNDEFINED;+ done:+ *pdone = TRUE;+ return JS_UNDEFINED;+ }+ it->idx = idx + 1;+ *pdone = FALSE;+ if (it->kind == JS_ITERATOR_KIND_KEY) {+ return JS_NewUint32(ctx, idx);+ } else {+ val = JS_GetPropertyUint32(ctx, it->obj, idx);+ if (JS_IsException(val))+ return JS_EXCEPTION;+ if (it->kind == JS_ITERATOR_KIND_VALUE) {+ return val;+ } else {+ JSValueConst args[2];+ JSValue num;+ num = JS_NewUint32(ctx, idx);+ args[0] = num;+ args[1] = val;+ obj = js_create_array(ctx, 2, args);+ JS_FreeValue(ctx, val);+ JS_FreeValue(ctx, num);+ return obj;+ }+ }+}++static JSValue js_iterator_proto_iterator(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return JS_DupValue(ctx, this_val);+}++static const JSCFunctionListEntry js_iterator_proto_funcs[] = {+ JS_CFUNC_DEF("[Symbol.iterator]", 0, js_iterator_proto_iterator ),+};++static const JSCFunctionListEntry js_array_proto_funcs[] = {+ JS_CFUNC_DEF("concat", 1, js_array_concat ),+ JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every ),+ JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some ),+ JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach ),+ JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map ),+ JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter ),+ JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce ),+ JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight ),+ JS_CFUNC_DEF("fill", 1, js_array_fill ),+ JS_CFUNC_MAGIC_DEF("find", 1, js_array_find, 0 ),+ JS_CFUNC_MAGIC_DEF("findIndex", 1, js_array_find, 1 ),+ JS_CFUNC_DEF("indexOf", 1, js_array_indexOf ),+ JS_CFUNC_DEF("lastIndexOf", 1, js_array_lastIndexOf ),+ JS_CFUNC_DEF("includes", 1, js_array_includes ),+ JS_CFUNC_MAGIC_DEF("join", 1, js_array_join, 0 ),+ JS_CFUNC_DEF("toString", 0, js_array_toString ),+ JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_array_join, 1 ),+ JS_CFUNC_MAGIC_DEF("pop", 0, js_array_pop, 0 ),+ JS_CFUNC_MAGIC_DEF("push", 1, js_array_push, 0 ),+ JS_CFUNC_MAGIC_DEF("shift", 0, js_array_pop, 1 ),+ JS_CFUNC_MAGIC_DEF("unshift", 1, js_array_push, 1 ),+ JS_CFUNC_DEF("reverse", 0, js_array_reverse ),+ JS_CFUNC_DEF("sort", 1, js_array_sort ),+ JS_CFUNC_MAGIC_DEF("slice", 2, js_array_slice, 0 ),+ JS_CFUNC_MAGIC_DEF("splice", 2, js_array_slice, 1 ),+ JS_CFUNC_DEF("copyWithin", 2, js_array_copyWithin ),+ JS_CFUNC_MAGIC_DEF("flatMap", 1, js_array_flatten, 1 ),+ JS_CFUNC_MAGIC_DEF("flat", 0, js_array_flatten, 0 ),+ JS_CFUNC_MAGIC_DEF("values", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE ),+ JS_ALIAS_DEF("[Symbol.iterator]", "values" ),+ JS_CFUNC_MAGIC_DEF("keys", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY ),+ JS_CFUNC_MAGIC_DEF("entries", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ),+};++static const JSCFunctionListEntry js_array_iterator_proto_funcs[] = {+ JS_ITERATOR_NEXT_DEF("next", 0, js_array_iterator_next, 0 ),+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Array Iterator", JS_PROP_CONFIGURABLE ),+};++/* Number */++static JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target,+ int argc, JSValueConst *argv)+{+ JSValue val, obj;+ if (argc == 0) {+ val = JS_NewInt32(ctx, 0);+ } else {+ val = JS_ToNumeric(ctx, argv[0]);+ if (JS_IsException(val))+ return val;+ switch(JS_VALUE_GET_TAG(val)) {+#ifdef CONFIG_BIGNUM+ case JS_TAG_BIG_INT:+ case JS_TAG_BIG_FLOAT:+ {+ JSBigFloat *p = JS_VALUE_GET_PTR(val);+ double d;+ bf_get_float64(&p->num, &d, BF_RNDN);+ JS_FreeValue(ctx, val);+ val = __JS_NewFloat64(ctx, d);+ }+ break;+ case JS_TAG_BIG_DECIMAL:+ val = JS_ToStringFree(ctx, val);+ if (JS_IsException(val))+ return val;+ val = JS_ToNumberFree(ctx, val);+ if (JS_IsException(val))+ return val;+ break;+#endif+ default:+ break;+ }+ }+ if (!JS_IsUndefined(new_target)) {+ obj = js_create_from_ctor(ctx, new_target, JS_CLASS_NUMBER);+ if (!JS_IsException(obj))+ JS_SetObjectData(ctx, obj, val);+ return obj;+ } else {+ return val;+ }+}++#if 0+static JSValue js_number___toInteger(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[0]));+}++static JSValue js_number___toLength(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ int64_t v;+ if (JS_ToLengthFree(ctx, &v, JS_DupValue(ctx, argv[0])))+ return JS_EXCEPTION;+ return JS_NewInt64(ctx, v);+}+#endif++static JSValue js_number_isNaN(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ if (!JS_IsNumber(argv[0]))+ return JS_FALSE;+ return js_global_isNaN(ctx, this_val, argc, argv);+}++static JSValue js_number_isFinite(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ if (!JS_IsNumber(argv[0]))+ return JS_FALSE;+ return js_global_isFinite(ctx, this_val, argc, argv);+}++static JSValue js_number_isInteger(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ int ret;+ ret = JS_NumberIsInteger(ctx, argv[0]);+ if (ret < 0)+ return JS_EXCEPTION;+ else+ return JS_NewBool(ctx, ret);+}++static JSValue js_number_isSafeInteger(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ double d;+ if (!JS_IsNumber(argv[0]))+ return JS_FALSE;+ if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))+ return JS_EXCEPTION;+ return JS_NewBool(ctx, is_safe_integer(d));+}++static const JSCFunctionListEntry js_number_funcs[] = {+ /* global ParseInt and parseFloat should be defined already or delayed */+ JS_ALIAS_BASE_DEF("parseInt", "parseInt", 0 ),+ JS_ALIAS_BASE_DEF("parseFloat", "parseFloat", 0 ),+ JS_CFUNC_DEF("isNaN", 1, js_number_isNaN ),+ JS_CFUNC_DEF("isFinite", 1, js_number_isFinite ),+ JS_CFUNC_DEF("isInteger", 1, js_number_isInteger ),+ JS_CFUNC_DEF("isSafeInteger", 1, js_number_isSafeInteger ),+ JS_PROP_DOUBLE_DEF("MAX_VALUE", 1.7976931348623157e+308, 0 ),+ JS_PROP_DOUBLE_DEF("MIN_VALUE", 5e-324, 0 ),+ JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),+ JS_PROP_DOUBLE_DEF("NEGATIVE_INFINITY", -INFINITY, 0 ),+ JS_PROP_DOUBLE_DEF("POSITIVE_INFINITY", INFINITY, 0 ),+ JS_PROP_DOUBLE_DEF("EPSILON", 2.220446049250313e-16, 0 ), /* ES6 */+ JS_PROP_DOUBLE_DEF("MAX_SAFE_INTEGER", 9007199254740991.0, 0 ), /* ES6 */+ JS_PROP_DOUBLE_DEF("MIN_SAFE_INTEGER", -9007199254740991.0, 0 ), /* ES6 */+ //JS_CFUNC_DEF("__toInteger", 1, js_number___toInteger ),+ //JS_CFUNC_DEF("__toLength", 1, js_number___toLength ),+};++static JSValue js_thisNumberValue(JSContext *ctx, JSValueConst this_val)+{+ if (JS_IsNumber(this_val))+ return JS_DupValue(ctx, this_val);++ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {+ JSObject *p = JS_VALUE_GET_OBJ(this_val);+ if (p->class_id == JS_CLASS_NUMBER) {+ if (JS_IsNumber(p->u.object_data))+ return JS_DupValue(ctx, p->u.object_data);+ }+ }+ return JS_ThrowTypeError(ctx, "not a number");+}++static JSValue js_number_valueOf(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return js_thisNumberValue(ctx, this_val);+}++static int js_get_radix(JSContext *ctx, JSValueConst val)+{+ int radix;+ if (JS_ToInt32Sat(ctx, &radix, val))+ return -1;+ if (radix < 2 || radix > 36) {+ JS_ThrowRangeError(ctx, "radix must be between 2 and 36");+ return -1;+ }+ return radix;+}++static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ JSValue val;+ int base;+ double d;++ val = js_thisNumberValue(ctx, this_val);+ if (JS_IsException(val))+ return val;+ if (magic || JS_IsUndefined(argv[0])) {+ base = 10;+ } else {+ base = js_get_radix(ctx, argv[0]);+ if (base < 0)+ goto fail;+ }+ if (JS_ToFloat64Free(ctx, &d, val))+ return JS_EXCEPTION;+ return js_dtoa(ctx, d, base, 0, JS_DTOA_VAR_FORMAT);+ fail:+ JS_FreeValue(ctx, val);+ return JS_EXCEPTION;+}++static JSValue js_number_toFixed(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue val;+ int f;+ double d;++ val = js_thisNumberValue(ctx, this_val);+ if (JS_IsException(val))+ return val;+ if (JS_ToFloat64Free(ctx, &d, val))+ return JS_EXCEPTION;+ if (JS_ToInt32Sat(ctx, &f, argv[0]))+ return JS_EXCEPTION;+ if (f < 0 || f > 100)+ return JS_ThrowRangeError(ctx, "invalid number of digits");+ if (fabs(d) >= 1e21) {+ return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d));+ } else {+ return js_dtoa(ctx, d, 10, f, JS_DTOA_FRAC_FORMAT);+ }+}++static JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue val;+ int f, flags;+ double d;++ val = js_thisNumberValue(ctx, this_val);+ if (JS_IsException(val))+ return val;+ if (JS_ToFloat64Free(ctx, &d, val))+ return JS_EXCEPTION;+ if (JS_ToInt32Sat(ctx, &f, argv[0]))+ return JS_EXCEPTION;+ if (!isfinite(d)) {+ return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d));+ }+ if (JS_IsUndefined(argv[0])) {+ flags = 0;+ f = 0;+ } else {+ if (f < 0 || f > 100)+ return JS_ThrowRangeError(ctx, "invalid number of digits");+ f++;+ flags = JS_DTOA_FIXED_FORMAT;+ }+ return js_dtoa(ctx, d, 10, f, flags | JS_DTOA_FORCE_EXP);+}++static JSValue js_number_toPrecision(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue val;+ int p;+ double d;++ val = js_thisNumberValue(ctx, this_val);+ if (JS_IsException(val))+ return val;+ if (JS_ToFloat64Free(ctx, &d, val))+ return JS_EXCEPTION;+ if (JS_IsUndefined(argv[0]))+ goto to_string;+ if (JS_ToInt32Sat(ctx, &p, argv[0]))+ return JS_EXCEPTION;+ if (!isfinite(d)) {+ to_string:+ return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d));+ }+ if (p < 1 || p > 100)+ return JS_ThrowRangeError(ctx, "invalid number of digits");+ return js_dtoa(ctx, d, 10, p, JS_DTOA_FIXED_FORMAT);+}++static const JSCFunctionListEntry js_number_proto_funcs[] = {+ JS_CFUNC_DEF("toExponential", 1, js_number_toExponential ),+ JS_CFUNC_DEF("toFixed", 1, js_number_toFixed ),+ JS_CFUNC_DEF("toPrecision", 1, js_number_toPrecision ),+ JS_CFUNC_MAGIC_DEF("toString", 1, js_number_toString, 0 ),+ JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_number_toString, 1 ),+ JS_CFUNC_DEF("valueOf", 0, js_number_valueOf ),+};++static JSValue js_parseInt(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ const char *str, *p;+ int radix, flags;+ JSValue ret;++ str = JS_ToCString(ctx, argv[0]);+ if (!str)+ return JS_EXCEPTION;+ if (JS_ToInt32(ctx, &radix, argv[1])) {+ JS_FreeCString(ctx, str);+ return JS_EXCEPTION;+ }+ if (radix != 0 && (radix < 2 || radix > 36)) {+ ret = JS_NAN;+ } else {+ p = str;+ p += skip_spaces(p);+ flags = ATOD_INT_ONLY | ATOD_ACCEPT_PREFIX_AFTER_SIGN;+ ret = js_atof(ctx, p, NULL, radix, flags);+ }+ JS_FreeCString(ctx, str);+ return ret;+}++static JSValue js_parseFloat(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ const char *str, *p;+ JSValue ret;++ str = JS_ToCString(ctx, argv[0]);+ if (!str)+ return JS_EXCEPTION;+ p = str;+ p += skip_spaces(p);+ ret = js_atof(ctx, p, NULL, 10, 0);+ JS_FreeCString(ctx, str);+ return ret;+}++/* Boolean */+static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst new_target,+ int argc, JSValueConst *argv)+{+ JSValue val, obj;+ val = JS_NewBool(ctx, JS_ToBool(ctx, argv[0]));+ if (!JS_IsUndefined(new_target)) {+ obj = js_create_from_ctor(ctx, new_target, JS_CLASS_BOOLEAN);+ if (!JS_IsException(obj))+ JS_SetObjectData(ctx, obj, val);+ return obj;+ } else {+ return val;+ }+}++static JSValue js_thisBooleanValue(JSContext *ctx, JSValueConst this_val)+{+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_BOOL)+ return JS_DupValue(ctx, this_val);++ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {+ JSObject *p = JS_VALUE_GET_OBJ(this_val);+ if (p->class_id == JS_CLASS_BOOLEAN) {+ if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_BOOL)+ return p->u.object_data;+ }+ }+ return JS_ThrowTypeError(ctx, "not a boolean");+}++static JSValue js_boolean_toString(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue val = js_thisBooleanValue(ctx, this_val);+ if (JS_IsException(val))+ return val;+ return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ?+ JS_ATOM_true : JS_ATOM_false);+}++static JSValue js_boolean_valueOf(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return js_thisBooleanValue(ctx, this_val);+}++static const JSCFunctionListEntry js_boolean_proto_funcs[] = {+ JS_CFUNC_DEF("toString", 0, js_boolean_toString ),+ JS_CFUNC_DEF("valueOf", 0, js_boolean_valueOf ),+};++/* String */++static int js_string_get_own_property(JSContext *ctx,+ JSPropertyDescriptor *desc,+ JSValueConst obj, JSAtom prop)+{+ JSObject *p;+ JSString *p1;+ uint32_t idx, ch;++ /* This is a class exotic method: obj class_id is JS_CLASS_STRING */+ if (__JS_AtomIsTaggedInt(prop)) {+ p = JS_VALUE_GET_OBJ(obj);+ if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) {+ p1 = JS_VALUE_GET_STRING(p->u.object_data);+ idx = __JS_AtomToUInt32(prop);+ if (idx < p1->len) {+ if (desc) {+ if (p1->is_wide_char)+ ch = p1->u.str16[idx];+ else+ ch = p1->u.str8[idx];+ desc->flags = JS_PROP_ENUMERABLE;+ desc->value = js_new_string_char(ctx, ch);+ desc->getter = JS_UNDEFINED;+ desc->setter = JS_UNDEFINED;+ }+ return TRUE;+ }+ }+ }+ return FALSE;+}++static int js_string_define_own_property(JSContext *ctx,+ JSValueConst this_obj,+ JSAtom prop, JSValueConst val,+ JSValueConst getter,+ JSValueConst setter, int flags)+{+ uint32_t idx;+ JSObject *p;+ JSString *p1, *p2;+ + if (__JS_AtomIsTaggedInt(prop)) {+ idx = __JS_AtomToUInt32(prop);+ p = JS_VALUE_GET_OBJ(this_obj);+ if (JS_VALUE_GET_TAG(p->u.object_data) != JS_TAG_STRING)+ goto def;+ p1 = JS_VALUE_GET_STRING(p->u.object_data);+ if (idx >= p1->len)+ goto def;+ if (!check_define_prop_flags(JS_PROP_ENUMERABLE, flags))+ goto fail;+ /* check that the same value is configured */+ if (flags & JS_PROP_HAS_VALUE) {+ if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)+ goto fail;+ p2 = JS_VALUE_GET_STRING(val);+ if (p2->len != 1)+ goto fail;+ if (string_get(p1, idx) != string_get(p2, 0)) {+ fail:+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable");+ }+ }+ return TRUE;+ } else {+ def:+ return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter,+ flags | JS_PROP_NO_EXOTIC);+ }+}++static int js_string_delete_property(JSContext *ctx,+ JSValueConst obj, JSAtom prop)+{+ uint32_t idx;++ if (__JS_AtomIsTaggedInt(prop)) {+ idx = __JS_AtomToUInt32(prop);+ if (idx < js_string_obj_get_length(ctx, obj)) {+ return FALSE;+ }+ }+ return TRUE;+}++static const JSClassExoticMethods js_string_exotic_methods = {+ .get_own_property = js_string_get_own_property,+ .define_own_property = js_string_define_own_property,+ .delete_property = js_string_delete_property,+};++static JSValue js_string_constructor(JSContext *ctx, JSValueConst new_target,+ int argc, JSValueConst *argv)+{+ JSValue val, obj;+ if (argc == 0) {+ val = JS_AtomToString(ctx, JS_ATOM_empty_string);+ } else {+ if (JS_IsUndefined(new_target) && JS_IsSymbol(argv[0])) {+ JSAtomStruct *p = JS_VALUE_GET_PTR(argv[0]);+ val = JS_ConcatString3(ctx, "Symbol(", JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p)), ")");+ } else {+ val = JS_ToString(ctx, argv[0]);+ }+ if (JS_IsException(val))+ return val;+ }+ if (!JS_IsUndefined(new_target)) {+ JSString *p1 = JS_VALUE_GET_STRING(val);++ obj = js_create_from_ctor(ctx, new_target, JS_CLASS_STRING);+ if (!JS_IsException(obj)) {+ JS_SetObjectData(ctx, obj, val);+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0);+ }+ return obj;+ } else {+ return val;+ }+}++static JSValue js_thisStringValue(JSContext *ctx, JSValueConst this_val)+{+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_STRING)+ return JS_DupValue(ctx, this_val);++ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {+ JSObject *p = JS_VALUE_GET_OBJ(this_val);+ if (p->class_id == JS_CLASS_STRING) {+ if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING)+ return JS_DupValue(ctx, p->u.object_data);+ }+ }+ return JS_ThrowTypeError(ctx, "not a string");+}++static JSValue js_string_fromCharCode(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ int i;+ StringBuffer b_s, *b = &b_s;++ string_buffer_init(ctx, b, argc);++ for(i = 0; i < argc; i++) {+ int32_t c;+ if (JS_ToInt32(ctx, &c, argv[i]) || string_buffer_putc16(b, c & 0xffff)) {+ string_buffer_free(b);+ return JS_EXCEPTION;+ }+ }+ return string_buffer_end(b);+}++static JSValue js_string_fromCodePoint(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ double d;+ int i, c;+ StringBuffer b_s, *b = &b_s;++ /* XXX: could pre-compute string length if all arguments are JS_TAG_INT */++ if (string_buffer_init(ctx, b, argc))+ goto fail;+ for(i = 0; i < argc; i++) {+ if (JS_VALUE_GET_TAG(argv[i]) == JS_TAG_INT) {+ c = JS_VALUE_GET_INT(argv[i]);+ if (c < 0 || c > 0x10ffff)+ goto range_error;+ } else {+ if (JS_ToFloat64(ctx, &d, argv[i]))+ goto fail;+ if (d < 0 || d > 0x10ffff || (c = (int)d) != d)+ goto range_error;+ }+ if (string_buffer_putc(b, c))+ goto fail;+ }+ return string_buffer_end(b);++ range_error:+ JS_ThrowRangeError(ctx, "invalid code point");+ fail:+ string_buffer_free(b);+ return JS_EXCEPTION;+}++static JSValue js_string_raw(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ // raw(temp,...a)+ JSValue cooked, val, raw;+ StringBuffer b_s, *b = &b_s;+ int64_t i, n;++ string_buffer_init(ctx, b, 0);+ raw = JS_UNDEFINED;+ cooked = JS_ToObject(ctx, argv[0]);+ if (JS_IsException(cooked))+ goto exception;+ raw = JS_ToObjectFree(ctx, JS_GetProperty(ctx, cooked, JS_ATOM_raw));+ if (JS_IsException(raw))+ goto exception;+ if (js_get_length64(ctx, &n, raw) < 0)+ goto exception;+ + for (i = 0; i < n; i++) {+ val = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, raw, i));+ if (JS_IsException(val))+ goto exception;+ string_buffer_concat_value_free(b, val);+ if (i < n - 1 && i + 1 < argc) {+ if (string_buffer_concat_value(b, argv[i + 1]))+ goto exception;+ }+ }+ JS_FreeValue(ctx, cooked);+ JS_FreeValue(ctx, raw);+ return string_buffer_end(b);++exception:+ JS_FreeValue(ctx, cooked);+ JS_FreeValue(ctx, raw);+ string_buffer_free(b);+ return JS_EXCEPTION;+}++/* only used in test262 */+JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ uint32_t start, end, i, n;+ StringBuffer b_s, *b = &b_s;++ if (JS_ToUint32(ctx, &start, argv[0]) ||+ JS_ToUint32(ctx, &end, argv[1]))+ return JS_EXCEPTION;+ end = min_uint32(end, 0x10ffff + 1);++ if (start > end) {+ start = end;+ }+ n = end - start;+ if (end > 0x10000) {+ n += end - max_uint32(start, 0x10000);+ }+ if (string_buffer_init2(ctx, b, n, end >= 0x100))+ return JS_EXCEPTION;+ for(i = start; i < end; i++) {+ string_buffer_putc(b, i);+ }+ return string_buffer_end(b);+}++#if 0+static JSValue js_string___isSpace(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ int c;+ if (JS_ToInt32(ctx, &c, argv[0]))+ return JS_EXCEPTION;+ return JS_NewBool(ctx, lre_is_space(c));+}+#endif++static JSValue js_string_charCodeAt(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue val, ret;+ JSString *p;+ int idx, c;++ val = JS_ToStringCheckObject(ctx, this_val);+ if (JS_IsException(val))+ return val;+ p = JS_VALUE_GET_STRING(val);+ if (JS_ToInt32Sat(ctx, &idx, argv[0])) {+ JS_FreeValue(ctx, val);+ return JS_EXCEPTION;+ }+ if (idx < 0 || idx >= p->len) {+ ret = JS_NAN;+ } else {+ if (p->is_wide_char)+ c = p->u.str16[idx];+ else+ c = p->u.str8[idx];+ ret = JS_NewInt32(ctx, c);+ }+ JS_FreeValue(ctx, val);+ return ret;+}++static JSValue js_string_charAt(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue val, ret;+ JSString *p;+ int idx, c;++ val = JS_ToStringCheckObject(ctx, this_val);+ if (JS_IsException(val))+ return val;+ p = JS_VALUE_GET_STRING(val);+ if (JS_ToInt32Sat(ctx, &idx, argv[0])) {+ JS_FreeValue(ctx, val);+ return JS_EXCEPTION;+ }+ if (idx < 0 || idx >= p->len) {+ ret = js_new_string8(ctx, NULL, 0);+ } else {+ if (p->is_wide_char)+ c = p->u.str16[idx];+ else+ c = p->u.str8[idx];+ ret = js_new_string_char(ctx, c);+ }+ JS_FreeValue(ctx, val);+ return ret;+}++static JSValue js_string_codePointAt(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue val, ret;+ JSString *p;+ int idx, c;++ val = JS_ToStringCheckObject(ctx, this_val);+ if (JS_IsException(val))+ return val;+ p = JS_VALUE_GET_STRING(val);+ if (JS_ToInt32Sat(ctx, &idx, argv[0])) {+ JS_FreeValue(ctx, val);+ return JS_EXCEPTION;+ }+ if (idx < 0 || idx >= p->len) {+ ret = JS_UNDEFINED;+ } else {+ c = string_getc(p, &idx);+ ret = JS_NewInt32(ctx, c);+ }+ JS_FreeValue(ctx, val);+ return ret;+}++static JSValue js_string_concat(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue r;+ int i;++ /* XXX: Use more efficient method */+ /* XXX: This method is OK if r has a single refcount */+ /* XXX: should use string_buffer? */+ r = JS_ToStringCheckObject(ctx, this_val);+ for (i = 0; i < argc; i++) {+ if (JS_IsException(r))+ break;+ r = JS_ConcatString(ctx, r, JS_DupValue(ctx, argv[i]));+ }+ return r;+}++static int string_cmp(JSString *p1, JSString *p2, int x1, int x2, int len)+{+ int i, c1, c2;+ for (i = 0; i < len; i++) {+ if ((c1 = string_get(p1, x1 + i)) != (c2 = string_get(p2, x2 + i)))+ return c1 - c2;+ }+ return 0;+}++static int string_indexof_char(JSString *p, int c, int from)+{+ /* assuming 0 <= from <= p->len */+ int i, len = p->len;+ if (p->is_wide_char) {+ for (i = from; i < len; i++) {+ if (p->u.str16[i] == c)+ return i;+ }+ } else {+ if ((c & ~0xff) == 0) {+ for (i = from; i < len; i++) {+ if (p->u.str8[i] == (uint8_t)c)+ return i;+ }+ }+ }+ return -1;+}++static int string_indexof(JSString *p1, JSString *p2, int from)+{+ /* assuming 0 <= from <= p1->len */+ int c, i, j, len1 = p1->len, len2 = p2->len;+ if (len2 == 0)+ return from;+ for (i = from, c = string_get(p2, 0); i + len2 <= len1; i = j + 1) {+ j = string_indexof_char(p1, c, i);+ if (j < 0 || j + len2 > len1)+ break;+ if (!string_cmp(p1, p2, j + 1, 1, len2 - 1))+ return j;+ }+ return -1;+}++static int64_t string_advance_index(JSString *p, int64_t index, BOOL unicode)+{+ if (!unicode || index >= p->len || !p->is_wide_char) {+ index++;+ } else {+ int index32 = (int)index;+ string_getc(p, &index32);+ index = index32;+ }+ return index;+}++static JSValue js_string_indexOf(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int lastIndexOf)+{+ JSValue str, v;+ int i, len, v_len, pos, start, stop, ret, inc;+ JSString *p;+ JSString *p1;++ str = JS_ToStringCheckObject(ctx, this_val);+ if (JS_IsException(str))+ return str;+ v = JS_ToString(ctx, argv[0]);+ if (JS_IsException(v))+ goto fail;+ p = JS_VALUE_GET_STRING(str);+ p1 = JS_VALUE_GET_STRING(v);+ len = p->len;+ v_len = p1->len;+ if (lastIndexOf) {+ pos = len - v_len;+ if (argc > 1) {+ double d;+ if (JS_ToFloat64(ctx, &d, argv[1]))+ goto fail;+ if (!isnan(d)) {+ if (d <= 0)+ pos = 0;+ else if (d < pos)+ pos = d;+ }+ }+ start = pos;+ stop = 0;+ inc = -1;+ } else {+ pos = 0;+ if (argc > 1) {+ if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0))+ goto fail;+ }+ start = pos;+ stop = len - v_len;+ inc = 1;+ }+ ret = -1;+ if (len >= v_len && inc * (stop - start) >= 0) {+ for (i = start;; i += inc) {+ if (!string_cmp(p, p1, i, 0, v_len)) {+ ret = i;+ break;+ }+ if (i == stop)+ break;+ }+ }+ JS_FreeValue(ctx, str);+ JS_FreeValue(ctx, v);+ return JS_NewInt32(ctx, ret);++fail:+ JS_FreeValue(ctx, str);+ JS_FreeValue(ctx, v);+ return JS_EXCEPTION;+}++/* return < 0 if exception or TRUE/FALSE */+static int js_is_regexp(JSContext *ctx, JSValueConst obj);++static JSValue js_string_includes(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ JSValue str, v = JS_UNDEFINED;+ int i, len, v_len, pos, start, stop, ret;+ JSString *p;+ JSString *p1;++ str = JS_ToStringCheckObject(ctx, this_val);+ if (JS_IsException(str))+ return str;+ ret = js_is_regexp(ctx, argv[0]);+ if (ret) {+ if (ret > 0)+ JS_ThrowTypeError(ctx, "regex not supported");+ goto fail;+ }+ v = JS_ToString(ctx, argv[0]);+ if (JS_IsException(v))+ goto fail;+ p = JS_VALUE_GET_STRING(str);+ p1 = JS_VALUE_GET_STRING(v);+ len = p->len;+ v_len = p1->len;+ pos = (magic == 2) ? len : 0;+ if (argc > 1 && !JS_IsUndefined(argv[1])) {+ if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0))+ goto fail;+ }+ len -= v_len;+ ret = 0;+ if (magic == 0) {+ start = pos;+ stop = len;+ } else {+ if (magic == 1) {+ if (pos > len)+ goto done;+ } else {+ pos -= v_len;+ }+ start = stop = pos;+ }+ if (start >= 0 && start <= stop) {+ for (i = start;; i++) {+ if (!string_cmp(p, p1, i, 0, v_len)) {+ ret = 1;+ break;+ }+ if (i == stop)+ break;+ }+ }+ done:+ JS_FreeValue(ctx, str);+ JS_FreeValue(ctx, v);+ return JS_NewBool(ctx, ret);++fail:+ JS_FreeValue(ctx, str);+ JS_FreeValue(ctx, v);+ return JS_EXCEPTION;+}++static int check_regexp_g_flag(JSContext *ctx, JSValueConst regexp)+{+ int ret;+ JSValue flags;+ + ret = js_is_regexp(ctx, regexp);+ if (ret < 0)+ return -1;+ if (ret) {+ flags = JS_GetProperty(ctx, regexp, JS_ATOM_flags);+ if (JS_IsException(flags))+ return -1;+ if (JS_IsUndefined(flags) || JS_IsNull(flags)) {+ JS_ThrowTypeError(ctx, "cannot convert to object");+ return -1;+ }+ flags = JS_ToStringFree(ctx, flags);+ if (JS_IsException(flags))+ return -1;+ ret = string_indexof_char(JS_VALUE_GET_STRING(flags), 'g', 0);+ JS_FreeValue(ctx, flags);+ if (ret < 0) {+ JS_ThrowTypeError(ctx, "regexp must have the 'g' flag");+ return -1;+ }+ }+ return 0;+}++static JSValue js_string_match(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int atom)+{+ // match(rx), search(rx), matchAll(rx)+ // atom is JS_ATOM_Symbol_match, JS_ATOM_Symbol_search, or JS_ATOM_Symbol_matchAll+ JSValueConst O = this_val, regexp = argv[0], args[2];+ JSValue matcher, S, rx, result, str;+ int args_len;++ if (JS_IsUndefined(O) || JS_IsNull(O))+ return JS_ThrowTypeError(ctx, "cannot convert to object");++ if (!JS_IsUndefined(regexp) && !JS_IsNull(regexp)) {+ matcher = JS_GetProperty(ctx, regexp, atom);+ if (JS_IsException(matcher))+ return JS_EXCEPTION;+ if (atom == JS_ATOM_Symbol_matchAll) {+ if (check_regexp_g_flag(ctx, regexp) < 0) {+ JS_FreeValue(ctx, matcher);+ return JS_EXCEPTION;+ }+ }+ if (!JS_IsUndefined(matcher) && !JS_IsNull(matcher)) {+ return JS_CallFree(ctx, matcher, regexp, 1, &O);+ }+ }+ S = JS_ToString(ctx, O);+ if (JS_IsException(S))+ return JS_EXCEPTION;+ args_len = 1;+ args[0] = regexp;+ str = JS_UNDEFINED;+ if (atom == JS_ATOM_Symbol_matchAll) {+ str = JS_NewString(ctx, "g");+ if (JS_IsException(str))+ goto fail;+ args[args_len++] = (JSValueConst)str;+ }+ rx = JS_CallConstructor(ctx, ctx->regexp_ctor, args_len, args);+ JS_FreeValue(ctx, str);+ if (JS_IsException(rx)) {+ fail:+ JS_FreeValue(ctx, S);+ return JS_EXCEPTION;+ }+ result = JS_InvokeFree(ctx, rx, atom, 1, (JSValueConst *)&S);+ JS_FreeValue(ctx, S);+ return result;+}++static JSValue js_string___GetSubstitution(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ // GetSubstitution(matched, str, position, captures, namedCaptures, rep)+ JSValueConst matched, str, captures, namedCaptures, rep;+ JSValue capture, name, s;+ uint32_t position, len, matched_len, captures_len;+ int i, j, j0, k, k1;+ int c, c1;+ StringBuffer b_s, *b = &b_s;+ JSString *sp, *rp;++ matched = argv[0];+ str = argv[1];+ captures = argv[3];+ namedCaptures = argv[4];+ rep = argv[5];++ if (!JS_IsString(rep) || !JS_IsString(str))+ return JS_ThrowTypeError(ctx, "not a string");++ sp = JS_VALUE_GET_STRING(str);+ rp = JS_VALUE_GET_STRING(rep);++ string_buffer_init(ctx, b, 0);++ captures_len = 0;+ if (!JS_IsUndefined(captures)) {+ if (js_get_length32(ctx, &captures_len, captures))+ goto exception;+ }+ if (js_get_length32(ctx, &matched_len, matched))+ goto exception;+ if (JS_ToUint32(ctx, &position, argv[2]) < 0)+ goto exception;++ len = rp->len;+ i = 0;+ for(;;) {+ j = string_indexof_char(rp, '$', i);+ if (j < 0 || j + 1 >= len)+ break;+ string_buffer_concat(b, rp, i, j);+ j0 = j++;+ c = string_get(rp, j++);+ if (c == '$') {+ string_buffer_putc8(b, '$');+ } else if (c == '&') {+ if (string_buffer_concat_value(b, matched))+ goto exception;+ } else if (c == '`') {+ string_buffer_concat(b, sp, 0, position);+ } else if (c == '\'') {+ string_buffer_concat(b, sp, position + matched_len, sp->len);+ } else if (c >= '0' && c <= '9') {+ k = c - '0';+ if (j < len) {+ c1 = string_get(rp, j);+ if (c1 >= '0' && c1 <= '9') {+ /* This behavior is specified in ES6 and refined in ECMA 2019 */+ /* ECMA 2019 does not have the extra test, but+ Test262 S15.5.4.11_A3_T1..3 require this behavior */+ k1 = k * 10 + c1 - '0';+ if (k1 >= 1 && k1 < captures_len) {+ k = k1;+ j++;+ }+ }+ }+ if (k >= 1 && k < captures_len) {+ s = JS_GetPropertyInt64(ctx, captures, k);+ if (JS_IsException(s))+ goto exception;+ if (!JS_IsUndefined(s)) {+ if (string_buffer_concat_value_free(b, s))+ goto exception;+ }+ } else {+ goto norep;+ }+ } else if (c == '<' && !JS_IsUndefined(namedCaptures)) {+ k = string_indexof_char(rp, '>', j);+ if (k < 0)+ goto norep;+ name = js_sub_string(ctx, rp, j, k);+ if (JS_IsException(name))+ goto exception;+ capture = JS_GetPropertyValue(ctx, namedCaptures, name);+ if (JS_IsException(capture))+ goto exception;+ if (!JS_IsUndefined(capture)) {+ if (string_buffer_concat_value_free(b, capture))+ goto exception;+ }+ j = k + 1;+ } else {+ norep:+ string_buffer_concat(b, rp, j0, j);+ }+ i = j;+ }+ string_buffer_concat(b, rp, i, rp->len);+ return string_buffer_end(b);+exception:+ string_buffer_free(b);+ return JS_EXCEPTION;+}++static JSValue js_string_replace(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv,+ int is_replaceAll)+{+ // replace(rx, rep)+ JSValueConst O = this_val, searchValue = argv[0], replaceValue = argv[1];+ JSValueConst args[6];+ JSValue str, search_str, replaceValue_str, repl_str;+ JSString *sp, *searchp;+ StringBuffer b_s, *b = &b_s;+ int pos, functionalReplace, endOfLastMatch;+ BOOL is_first;++ if (JS_IsUndefined(O) || JS_IsNull(O))+ return JS_ThrowTypeError(ctx, "cannot convert to object");++ search_str = JS_UNDEFINED;+ replaceValue_str = JS_UNDEFINED;+ repl_str = JS_UNDEFINED;++ if (!JS_IsUndefined(searchValue) && !JS_IsNull(searchValue)) {+ JSValue replacer;+ if (is_replaceAll) {+ if (check_regexp_g_flag(ctx, searchValue) < 0)+ return JS_EXCEPTION;+ }+ replacer = JS_GetProperty(ctx, searchValue, JS_ATOM_Symbol_replace);+ if (JS_IsException(replacer))+ return JS_EXCEPTION;+ if (!JS_IsUndefined(replacer) && !JS_IsNull(replacer)) {+ args[0] = O;+ args[1] = replaceValue;+ return JS_CallFree(ctx, replacer, searchValue, 2, args);+ }+ }+ string_buffer_init(ctx, b, 0);++ str = JS_ToString(ctx, O);+ if (JS_IsException(str))+ goto exception;+ search_str = JS_ToString(ctx, searchValue);+ if (JS_IsException(search_str))+ goto exception;+ functionalReplace = JS_IsFunction(ctx, replaceValue);+ if (!functionalReplace) {+ replaceValue_str = JS_ToString(ctx, replaceValue);+ if (JS_IsException(replaceValue_str))+ goto exception;+ }++ sp = JS_VALUE_GET_STRING(str);+ searchp = JS_VALUE_GET_STRING(search_str);+ endOfLastMatch = 0;+ is_first = TRUE;+ for(;;) {+ if (unlikely(searchp->len == 0)) {+ if (is_first)+ pos = 0;+ else if (endOfLastMatch >= sp->len)+ pos = -1;+ else+ pos = endOfLastMatch + 1;+ } else {+ pos = string_indexof(sp, searchp, endOfLastMatch);+ }+ if (pos < 0) {+ if (is_first) {+ string_buffer_free(b);+ JS_FreeValue(ctx, search_str);+ JS_FreeValue(ctx, replaceValue_str);+ return str;+ } else {+ break;+ }+ }+ if (functionalReplace) {+ args[0] = search_str;+ args[1] = JS_NewInt32(ctx, pos);+ args[2] = str;+ repl_str = JS_ToStringFree(ctx, JS_Call(ctx, replaceValue, JS_UNDEFINED, 3, args));+ } else {+ args[0] = search_str;+ args[1] = str;+ args[2] = JS_NewInt32(ctx, pos);+ args[3] = JS_UNDEFINED;+ args[4] = JS_UNDEFINED;+ args[5] = replaceValue_str;+ repl_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args);+ }+ if (JS_IsException(repl_str))+ goto exception;+ + string_buffer_concat(b, sp, endOfLastMatch, pos);+ string_buffer_concat_value_free(b, repl_str);+ endOfLastMatch = pos + searchp->len;+ is_first = FALSE;+ if (!is_replaceAll)+ break;+ }+ string_buffer_concat(b, sp, endOfLastMatch, sp->len);+ JS_FreeValue(ctx, search_str);+ JS_FreeValue(ctx, replaceValue_str);+ JS_FreeValue(ctx, str);+ return string_buffer_end(b);++exception:+ string_buffer_free(b);+ JS_FreeValue(ctx, search_str);+ JS_FreeValue(ctx, replaceValue_str);+ JS_FreeValue(ctx, str);+ return JS_EXCEPTION;+}++static JSValue js_string_split(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ // split(sep, limit)+ JSValueConst O = this_val, separator = argv[0], limit = argv[1];+ JSValueConst args[2];+ JSValue S, A, R, T;+ uint32_t lim, lengthA;+ int64_t p, q, s, r, e;+ JSString *sp, *rp;++ if (JS_IsUndefined(O) || JS_IsNull(O))+ return JS_ThrowTypeError(ctx, "cannot convert to object");++ S = JS_UNDEFINED;+ A = JS_UNDEFINED;+ R = JS_UNDEFINED;++ if (!JS_IsUndefined(separator) && !JS_IsNull(separator)) {+ JSValue splitter;+ splitter = JS_GetProperty(ctx, separator, JS_ATOM_Symbol_split);+ if (JS_IsException(splitter))+ return JS_EXCEPTION;+ if (!JS_IsUndefined(splitter) && !JS_IsNull(splitter)) {+ args[0] = O;+ args[1] = limit;+ return JS_CallFree(ctx, splitter, separator, 2, args);+ }+ }+ S = JS_ToString(ctx, O);+ if (JS_IsException(S))+ goto exception;+ A = JS_NewArray(ctx);+ if (JS_IsException(A))+ goto exception;+ lengthA = 0;+ if (JS_IsUndefined(limit)) {+ lim = 0xffffffff;+ } else {+ if (JS_ToUint32(ctx, &lim, limit) < 0)+ goto exception;+ }+ sp = JS_VALUE_GET_STRING(S);+ s = sp->len;+ R = JS_ToString(ctx, separator);+ if (JS_IsException(R))+ goto exception;+ rp = JS_VALUE_GET_STRING(R);+ r = rp->len;+ p = 0;+ if (lim == 0)+ goto done;+ if (JS_IsUndefined(separator))+ goto add_tail;+ if (s == 0) {+ if (r != 0)+ goto add_tail;+ goto done;+ }+ q = p;+ for (q = p; (q += !r) <= s - r - !r; q = p = e + r) {+ e = string_indexof(sp, rp, q);+ if (e < 0)+ break;+ T = js_sub_string(ctx, sp, p, e);+ if (JS_IsException(T))+ goto exception;+ if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T, 0) < 0)+ goto exception;+ if (lengthA == lim)+ goto done;+ }+add_tail:+ T = js_sub_string(ctx, sp, p, s);+ if (JS_IsException(T))+ goto exception;+ if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T,0 ) < 0)+ goto exception;+done:+ JS_FreeValue(ctx, S);+ JS_FreeValue(ctx, R);+ return A;++exception:+ JS_FreeValue(ctx, A);+ JS_FreeValue(ctx, S);+ JS_FreeValue(ctx, R);+ return JS_EXCEPTION;+}++static JSValue js_string_substring(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue str, ret;+ int a, b, start, end;+ JSString *p;++ str = JS_ToStringCheckObject(ctx, this_val);+ if (JS_IsException(str))+ return str;+ p = JS_VALUE_GET_STRING(str);+ if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, p->len, 0)) {+ JS_FreeValue(ctx, str);+ return JS_EXCEPTION;+ }+ b = p->len;+ if (!JS_IsUndefined(argv[1])) {+ if (JS_ToInt32Clamp(ctx, &b, argv[1], 0, p->len, 0)) {+ JS_FreeValue(ctx, str);+ return JS_EXCEPTION;+ }+ }+ if (a < b) {+ start = a;+ end = b;+ } else {+ start = b;+ end = a;+ }+ ret = js_sub_string(ctx, p, start, end);+ JS_FreeValue(ctx, str);+ return ret;+}++static JSValue js_string_substr(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue str, ret;+ int a, len, n;+ JSString *p;++ str = JS_ToStringCheckObject(ctx, this_val);+ if (JS_IsException(str))+ return str;+ p = JS_VALUE_GET_STRING(str);+ len = p->len;+ if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, len, len)) {+ JS_FreeValue(ctx, str);+ return JS_EXCEPTION;+ }+ n = len - a;+ if (!JS_IsUndefined(argv[1])) {+ if (JS_ToInt32Clamp(ctx, &n, argv[1], 0, len - a, 0)) {+ JS_FreeValue(ctx, str);+ return JS_EXCEPTION;+ }+ }+ ret = js_sub_string(ctx, p, a, a + n);+ JS_FreeValue(ctx, str);+ return ret;+}++static JSValue js_string_slice(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue str, ret;+ int len, start, end;+ JSString *p;++ str = JS_ToStringCheckObject(ctx, this_val);+ if (JS_IsException(str))+ return str;+ p = JS_VALUE_GET_STRING(str);+ len = p->len;+ if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) {+ JS_FreeValue(ctx, str);+ return JS_EXCEPTION;+ }+ end = len;+ if (!JS_IsUndefined(argv[1])) {+ if (JS_ToInt32Clamp(ctx, &end, argv[1], 0, len, len)) {+ JS_FreeValue(ctx, str);+ return JS_EXCEPTION;+ }+ }+ ret = js_sub_string(ctx, p, start, max_int(end, start));+ JS_FreeValue(ctx, str);+ return ret;+}++static JSValue js_string_pad(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int padEnd)+{+ JSValue str, v = JS_UNDEFINED;+ StringBuffer b_s, *b = &b_s;+ JSString *p, *p1 = NULL;+ int n, len, c = ' ';++ str = JS_ToStringCheckObject(ctx, this_val);+ if (JS_IsException(str))+ goto fail1;+ if (JS_ToInt32Sat(ctx, &n, argv[0]))+ goto fail2;+ p = JS_VALUE_GET_STRING(str);+ len = p->len;+ if (len >= n)+ return str;+ if (argc > 1 && !JS_IsUndefined(argv[1])) {+ v = JS_ToString(ctx, argv[1]);+ if (JS_IsException(v))+ goto fail2;+ p1 = JS_VALUE_GET_STRING(v);+ if (p1->len == 0) {+ JS_FreeValue(ctx, v);+ return str;+ }+ if (p1->len == 1) {+ c = string_get(p1, 0);+ p1 = NULL;+ }+ }+ if (n > JS_STRING_LEN_MAX) {+ JS_ThrowInternalError(ctx, "string too long");+ goto fail2;+ }+ if (string_buffer_init(ctx, b, n))+ goto fail3;+ n -= len;+ if (padEnd) {+ if (string_buffer_concat(b, p, 0, len))+ goto fail;+ }+ if (p1) {+ while (n > 0) {+ int chunk = min_int(n, p1->len);+ if (string_buffer_concat(b, p1, 0, chunk))+ goto fail;+ n -= chunk;+ }+ } else {+ if (string_buffer_fill(b, c, n))+ goto fail;+ }+ if (!padEnd) {+ if (string_buffer_concat(b, p, 0, len))+ goto fail;+ }+ JS_FreeValue(ctx, v);+ JS_FreeValue(ctx, str);+ return string_buffer_end(b);++fail:+ string_buffer_free(b);+fail3:+ JS_FreeValue(ctx, v);+fail2:+ JS_FreeValue(ctx, str);+fail1:+ return JS_EXCEPTION;+}++static JSValue js_string_repeat(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue str;+ StringBuffer b_s, *b = &b_s;+ JSString *p;+ int64_t val;+ int n, len;++ str = JS_ToStringCheckObject(ctx, this_val);+ if (JS_IsException(str))+ goto fail;+ if (JS_ToInt64Sat(ctx, &val, argv[0]))+ goto fail;+ if (val < 0 || val > 2147483647) {+ JS_ThrowRangeError(ctx, "invalid repeat count");+ goto fail;+ }+ n = val;+ p = JS_VALUE_GET_STRING(str);+ len = p->len;+ if (len == 0 || n == 1)+ return str;+ if (val * len > JS_STRING_LEN_MAX) {+ JS_ThrowInternalError(ctx, "string too long");+ goto fail;+ }+ if (string_buffer_init2(ctx, b, n * len, p->is_wide_char))+ goto fail;+ if (len == 1) {+ string_buffer_fill(b, string_get(p, 0), n);+ } else {+ while (n-- > 0) {+ string_buffer_concat(b, p, 0, len);+ }+ }+ JS_FreeValue(ctx, str);+ return string_buffer_end(b);++fail:+ JS_FreeValue(ctx, str);+ return JS_EXCEPTION;+}++static JSValue js_string_trim(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ JSValue str, ret;+ int a, b, len;+ JSString *p;++ str = JS_ToStringCheckObject(ctx, this_val);+ if (JS_IsException(str))+ return str;+ p = JS_VALUE_GET_STRING(str);+ a = 0;+ b = len = p->len;+ if (magic & 1) {+ while (a < len && lre_is_space(string_get(p, a)))+ a++;+ }+ if (magic & 2) {+ while (b > a && lre_is_space(string_get(p, b - 1)))+ b--;+ }+ ret = js_sub_string(ctx, p, a, b);+ JS_FreeValue(ctx, str);+ return ret;+}++static JSValue js_string___quote(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return JS_ToQuotedString(ctx, this_val);+}++/* return 0 if before the first char */+static int string_prevc(JSString *p, int *pidx)+{+ int idx, c, c1;++ idx = *pidx;+ if (idx <= 0)+ return 0;+ idx--;+ if (p->is_wide_char) {+ c = p->u.str16[idx];+ if (c >= 0xdc00 && c < 0xe000 && idx > 0) {+ c1 = p->u.str16[idx - 1];+ if (c1 >= 0xd800 && c1 <= 0xdc00) {+ c = (((c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000;+ idx--;+ }+ }+ } else {+ c = p->u.str8[idx];+ }+ *pidx = idx;+ return c;+}++static BOOL test_final_sigma(JSString *p, int sigma_pos)+{+ int k, c1;++ /* before C: skip case ignorable chars and check there is+ a cased letter */+ k = sigma_pos;+ for(;;) {+ c1 = string_prevc(p, &k);+ if (!lre_is_case_ignorable(c1))+ break;+ }+ if (!lre_is_cased(c1))+ return FALSE;++ /* after C: skip case ignorable chars and check there is+ no cased letter */+ k = sigma_pos + 1;+ for(;;) {+ if (k >= p->len)+ return TRUE;+ c1 = string_getc(p, &k);+ if (!lre_is_case_ignorable(c1))+ break;+ }+ return !lre_is_cased(c1);+}++static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue a, b;+ int cmp;++ a = JS_ToStringCheckObject(ctx, this_val);+ if (JS_IsException(a))+ return JS_EXCEPTION;+ b = JS_ToString(ctx, argv[0]);+ if (JS_IsException(b)) {+ JS_FreeValue(ctx, a);+ return JS_EXCEPTION;+ }+ cmp = js_string_compare(ctx, JS_VALUE_GET_STRING(a), JS_VALUE_GET_STRING(b));+ JS_FreeValue(ctx, a);+ JS_FreeValue(ctx, b);+ return JS_NewInt32(ctx, cmp);+}++static JSValue js_string_toLowerCase(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int to_lower)+{+ JSValue val;+ StringBuffer b_s, *b = &b_s;+ JSString *p;+ int i, c, j, l;+ uint32_t res[LRE_CC_RES_LEN_MAX];++ val = JS_ToStringCheckObject(ctx, this_val);+ if (JS_IsException(val))+ return val;+ p = JS_VALUE_GET_STRING(val);+ if (p->len == 0)+ return val;+ if (string_buffer_init(ctx, b, p->len))+ goto fail;+ for(i = 0; i < p->len;) {+ c = string_getc(p, &i);+ if (c == 0x3a3 && to_lower && test_final_sigma(p, i - 1)) {+ res[0] = 0x3c2; /* final sigma */+ l = 1;+ } else {+ l = lre_case_conv(res, c, to_lower);+ }+ for(j = 0; j < l; j++) {+ if (string_buffer_putc(b, res[j]))+ goto fail;+ }+ }+ JS_FreeValue(ctx, val);+ return string_buffer_end(b);+ fail:+ JS_FreeValue(ctx, val);+ string_buffer_free(b);+ return JS_EXCEPTION;+}++#ifdef CONFIG_ALL_UNICODE++/* return (-1, NULL) if exception, otherwise (len, buf) */+static int JS_ToUTF32String(JSContext *ctx, uint32_t **pbuf, JSValueConst val1)+{+ JSValue val;+ JSString *p;+ uint32_t *buf;+ int i, j, len;++ val = JS_ToString(ctx, val1);+ if (JS_IsException(val))+ return -1;+ p = JS_VALUE_GET_STRING(val);+ len = p->len;+ /* UTF32 buffer length is len minus the number of correct surrogates pairs */+ buf = js_malloc(ctx, sizeof(buf[0]) * max_int(len, 1));+ if (!buf) {+ JS_FreeValue(ctx, val);+ goto fail;+ }+ for(i = j = 0; i < len;)+ buf[j++] = string_getc(p, &i);+ JS_FreeValue(ctx, val);+ *pbuf = buf;+ return j;+ fail:+ *pbuf = NULL;+ return -1;+}++static JSValue JS_NewUTF32String(JSContext *ctx, const uint32_t *buf, int len)+{+ int i;+ StringBuffer b_s, *b = &b_s;+ if (string_buffer_init(ctx, b, len))+ return JS_EXCEPTION;+ for(i = 0; i < len; i++) {+ if (string_buffer_putc(b, buf[i]))+ goto fail;+ }+ return string_buffer_end(b);+ fail:+ string_buffer_free(b);+ return JS_EXCEPTION;+}++static JSValue js_string_normalize(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ const char *form, *p;+ size_t form_len;+ int is_compat, buf_len, out_len;+ UnicodeNormalizationEnum n_type;+ JSValue val;+ uint32_t *buf, *out_buf;++ val = JS_ToStringCheckObject(ctx, this_val);+ if (JS_IsException(val))+ return val;+ buf_len = JS_ToUTF32String(ctx, &buf, val);+ JS_FreeValue(ctx, val);+ if (buf_len < 0)+ return JS_EXCEPTION;++ if (argc == 0 || JS_IsUndefined(argv[0])) {+ n_type = UNICODE_NFC;+ } else {+ form = JS_ToCStringLen(ctx, &form_len, argv[0]);+ if (!form)+ goto fail1;+ p = form;+ if (p[0] != 'N' || p[1] != 'F')+ goto bad_form;+ p += 2;+ is_compat = FALSE;+ if (*p == 'K') {+ is_compat = TRUE;+ p++;+ }+ if (*p == 'C' || *p == 'D') {+ n_type = UNICODE_NFC + is_compat * 2 + (*p - 'C');+ if ((p + 1 - form) != form_len)+ goto bad_form;+ } else {+ bad_form:+ JS_FreeCString(ctx, form);+ JS_ThrowRangeError(ctx, "bad normalization form");+ fail1:+ js_free(ctx, buf);+ return JS_EXCEPTION;+ }+ JS_FreeCString(ctx, form);+ }++ out_len = unicode_normalize(&out_buf, buf, buf_len, n_type,+ ctx->rt, (DynBufReallocFunc *)js_realloc_rt);+ js_free(ctx, buf);+ if (out_len < 0)+ return JS_EXCEPTION;+ val = JS_NewUTF32String(ctx, out_buf, out_len);+ js_free(ctx, out_buf);+ return val;+}+#endif /* CONFIG_ALL_UNICODE */++/* also used for String.prototype.valueOf */+static JSValue js_string_toString(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return js_thisStringValue(ctx, this_val);+}++#if 0+static JSValue js_string___toStringCheckObject(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return JS_ToStringCheckObject(ctx, argv[0]);+}++static JSValue js_string___toString(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return JS_ToString(ctx, argv[0]);+}++static JSValue js_string___advanceStringIndex(JSContext *ctx, JSValueConst+ this_val,+ int argc, JSValueConst *argv)+{+ JSValue str;+ int idx;+ BOOL is_unicode;+ JSString *p;++ str = JS_ToString(ctx, argv[0]);+ if (JS_IsException(str))+ return str;+ if (JS_ToInt32Sat(ctx, &idx, argv[1])) {+ JS_FreeValue(ctx, str);+ return JS_EXCEPTION;+ }+ is_unicode = JS_ToBool(ctx, argv[2]);+ p = JS_VALUE_GET_STRING(str);+ if (!is_unicode || (unsigned)idx >= p->len || !p->is_wide_char) {+ idx++;+ } else {+ string_getc(p, &idx);+ }+ JS_FreeValue(ctx, str);+ return JS_NewInt32(ctx, idx);+}+#endif++/* String Iterator */++static JSValue js_string_iterator_next(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv,+ BOOL *pdone, int magic)+{+ JSArrayIteratorData *it;+ uint32_t idx, c, start;+ JSString *p;++ it = JS_GetOpaque2(ctx, this_val, JS_CLASS_STRING_ITERATOR);+ if (!it) {+ *pdone = FALSE;+ return JS_EXCEPTION;+ }+ if (JS_IsUndefined(it->obj))+ goto done;+ p = JS_VALUE_GET_STRING(it->obj);+ idx = it->idx;+ if (idx >= p->len) {+ JS_FreeValue(ctx, it->obj);+ it->obj = JS_UNDEFINED;+ done:+ *pdone = TRUE;+ return JS_UNDEFINED;+ }++ start = idx;+ c = string_getc(p, (int *)&idx);+ it->idx = idx;+ *pdone = FALSE;+ if (c <= 0xffff) {+ return js_new_string_char(ctx, c);+ } else {+ return js_new_string16(ctx, p->u.str16 + start, 2);+ }+}++/* ES6 Annex B 2.3.2 etc. */+enum {+ magic_string_anchor,+ magic_string_big,+ magic_string_blink,+ magic_string_bold,+ magic_string_fixed,+ magic_string_fontcolor,+ magic_string_fontsize,+ magic_string_italics,+ magic_string_link,+ magic_string_small,+ magic_string_strike,+ magic_string_sub,+ magic_string_sup,+};++static JSValue js_string_CreateHTML(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ JSValue str;+ const JSString *p;+ StringBuffer b_s, *b = &b_s;+ static struct { const char *tag, *attr; } const defs[] = {+ { "a", "name" }, { "big", NULL }, { "blink", NULL }, { "b", NULL },+ { "tt", NULL }, { "font", "color" }, { "font", "size" }, { "i", NULL },+ { "a", "href" }, { "small", NULL }, { "strike", NULL }, + { "sub", NULL }, { "sup", NULL },+ };++ str = JS_ToStringCheckObject(ctx, this_val);+ if (JS_IsException(str))+ return JS_EXCEPTION;+ string_buffer_init(ctx, b, 7);+ string_buffer_putc8(b, '<');+ string_buffer_puts8(b, defs[magic].tag);+ if (defs[magic].attr) {+ // r += " " + attr + "=\"" + value + "\"";+ JSValue value;+ int i;++ string_buffer_putc8(b, ' ');+ string_buffer_puts8(b, defs[magic].attr);+ string_buffer_puts8(b, "=\"");+ value = JS_ToStringCheckObject(ctx, argv[0]);+ if (JS_IsException(value)) {+ JS_FreeValue(ctx, str);+ string_buffer_free(b);+ return JS_EXCEPTION;+ }+ p = JS_VALUE_GET_STRING(value);+ for (i = 0; i < p->len; i++) {+ int c = string_get(p, i);+ if (c == '"') {+ string_buffer_puts8(b, """);+ } else {+ string_buffer_putc16(b, c);+ }+ }+ JS_FreeValue(ctx, value);+ string_buffer_putc8(b, '\"');+ }+ // return r + ">" + str + "</" + tag + ">";+ string_buffer_putc8(b, '>');+ string_buffer_concat_value_free(b, str);+ string_buffer_puts8(b, "</");+ string_buffer_puts8(b, defs[magic].tag);+ string_buffer_putc8(b, '>');+ return string_buffer_end(b);+}++static const JSCFunctionListEntry js_string_funcs[] = {+ JS_CFUNC_DEF("fromCharCode", 1, js_string_fromCharCode ),+ JS_CFUNC_DEF("fromCodePoint", 1, js_string_fromCodePoint ),+ JS_CFUNC_DEF("raw", 1, js_string_raw ),+ //JS_CFUNC_DEF("__toString", 1, js_string___toString ),+ //JS_CFUNC_DEF("__isSpace", 1, js_string___isSpace ),+ //JS_CFUNC_DEF("__toStringCheckObject", 1, js_string___toStringCheckObject ),+ //JS_CFUNC_DEF("__advanceStringIndex", 3, js_string___advanceStringIndex ),+ //JS_CFUNC_DEF("__GetSubstitution", 6, js_string___GetSubstitution ),+};++static const JSCFunctionListEntry js_string_proto_funcs[] = {+ JS_PROP_INT32_DEF("length", 0, JS_PROP_CONFIGURABLE ),+ JS_CFUNC_DEF("charCodeAt", 1, js_string_charCodeAt ),+ JS_CFUNC_DEF("charAt", 1, js_string_charAt ),+ JS_CFUNC_DEF("concat", 1, js_string_concat ),+ JS_CFUNC_DEF("codePointAt", 1, js_string_codePointAt ),+ JS_CFUNC_MAGIC_DEF("indexOf", 1, js_string_indexOf, 0 ),+ JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_string_indexOf, 1 ),+ JS_CFUNC_MAGIC_DEF("includes", 1, js_string_includes, 0 ),+ JS_CFUNC_MAGIC_DEF("endsWith", 1, js_string_includes, 2 ),+ JS_CFUNC_MAGIC_DEF("startsWith", 1, js_string_includes, 1 ),+ JS_CFUNC_MAGIC_DEF("match", 1, js_string_match, JS_ATOM_Symbol_match ),+ JS_CFUNC_MAGIC_DEF("matchAll", 1, js_string_match, JS_ATOM_Symbol_matchAll ),+ JS_CFUNC_MAGIC_DEF("search", 1, js_string_match, JS_ATOM_Symbol_search ),+ JS_CFUNC_DEF("split", 2, js_string_split ),+ JS_CFUNC_DEF("substring", 2, js_string_substring ),+ JS_CFUNC_DEF("substr", 2, js_string_substr ),+ JS_CFUNC_DEF("slice", 2, js_string_slice ),+ JS_CFUNC_DEF("repeat", 1, js_string_repeat ),+ JS_CFUNC_MAGIC_DEF("replace", 2, js_string_replace, 0 ),+ JS_CFUNC_MAGIC_DEF("replaceAll", 2, js_string_replace, 1 ),+ JS_CFUNC_MAGIC_DEF("padEnd", 1, js_string_pad, 1 ),+ JS_CFUNC_MAGIC_DEF("padStart", 1, js_string_pad, 0 ),+ JS_CFUNC_MAGIC_DEF("trim", 0, js_string_trim, 3 ),+ JS_CFUNC_MAGIC_DEF("trimEnd", 0, js_string_trim, 2 ),+ JS_ALIAS_DEF("trimRight", "trimEnd" ),+ JS_CFUNC_MAGIC_DEF("trimStart", 0, js_string_trim, 1 ),+ JS_ALIAS_DEF("trimLeft", "trimStart" ),+ JS_CFUNC_DEF("toString", 0, js_string_toString ),+ JS_CFUNC_DEF("valueOf", 0, js_string_toString ),+ JS_CFUNC_DEF("__quote", 1, js_string___quote ),+ JS_CFUNC_DEF("localeCompare", 1, js_string_localeCompare ),+ JS_CFUNC_MAGIC_DEF("toLowerCase", 0, js_string_toLowerCase, 1 ),+ JS_CFUNC_MAGIC_DEF("toUpperCase", 0, js_string_toLowerCase, 0 ),+ JS_CFUNC_MAGIC_DEF("toLocaleLowerCase", 0, js_string_toLowerCase, 1 ),+ JS_CFUNC_MAGIC_DEF("toLocaleUpperCase", 0, js_string_toLowerCase, 0 ),+ JS_CFUNC_MAGIC_DEF("[Symbol.iterator]", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE | 4 ),+ /* ES6 Annex B 2.3.2 etc. */+ JS_CFUNC_MAGIC_DEF("anchor", 1, js_string_CreateHTML, magic_string_anchor ),+ JS_CFUNC_MAGIC_DEF("big", 0, js_string_CreateHTML, magic_string_big ),+ JS_CFUNC_MAGIC_DEF("blink", 0, js_string_CreateHTML, magic_string_blink ),+ JS_CFUNC_MAGIC_DEF("bold", 0, js_string_CreateHTML, magic_string_bold ),+ JS_CFUNC_MAGIC_DEF("fixed", 0, js_string_CreateHTML, magic_string_fixed ),+ JS_CFUNC_MAGIC_DEF("fontcolor", 1, js_string_CreateHTML, magic_string_fontcolor ),+ JS_CFUNC_MAGIC_DEF("fontsize", 1, js_string_CreateHTML, magic_string_fontsize ),+ JS_CFUNC_MAGIC_DEF("italics", 0, js_string_CreateHTML, magic_string_italics ),+ JS_CFUNC_MAGIC_DEF("link", 1, js_string_CreateHTML, magic_string_link ),+ JS_CFUNC_MAGIC_DEF("small", 0, js_string_CreateHTML, magic_string_small ),+ JS_CFUNC_MAGIC_DEF("strike", 0, js_string_CreateHTML, magic_string_strike ),+ JS_CFUNC_MAGIC_DEF("sub", 0, js_string_CreateHTML, magic_string_sub ),+ JS_CFUNC_MAGIC_DEF("sup", 0, js_string_CreateHTML, magic_string_sup ),+};++static const JSCFunctionListEntry js_string_iterator_proto_funcs[] = {+ JS_ITERATOR_NEXT_DEF("next", 0, js_string_iterator_next, 0 ),+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "String Iterator", JS_PROP_CONFIGURABLE ),+};++#ifdef CONFIG_ALL_UNICODE+static const JSCFunctionListEntry js_string_proto_normalize[] = {+ JS_CFUNC_DEF("normalize", 0, js_string_normalize ),+};+#endif++void JS_AddIntrinsicStringNormalize(JSContext *ctx)+{+#ifdef CONFIG_ALL_UNICODE+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_normalize,+ countof(js_string_proto_normalize));+#endif+}++/* Math */++/* precondition: a and b are not NaN */+static double js_fmin(double a, double b)+{+ if (a == 0 && b == 0) {+ JSFloat64Union a1, b1;+ a1.d = a;+ b1.d = b;+ a1.u64 |= b1.u64;+ return a1.d;+ } else {+ return fmin(a, b);+ }+}++/* precondition: a and b are not NaN */+static double js_fmax(double a, double b)+{+ if (a == 0 && b == 0) {+ JSFloat64Union a1, b1;+ a1.d = a;+ b1.d = b;+ a1.u64 &= b1.u64;+ return a1.d;+ } else {+ return fmax(a, b);+ }+}++static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ BOOL is_max = magic;+ double r, a;+ int i;+ uint32_t tag;++ if (unlikely(argc == 0)) {+ return __JS_NewFloat64(ctx, is_max ? -1.0 / 0.0 : 1.0 / 0.0);+ }++ tag = JS_VALUE_GET_TAG(argv[0]);+ if (tag == JS_TAG_INT) {+ int a1, r1 = JS_VALUE_GET_INT(argv[0]);+ for(i = 1; i < argc; i++) {+ tag = JS_VALUE_GET_TAG(argv[i]);+ if (tag != JS_TAG_INT) {+ r = r1;+ goto generic_case;+ }+ a1 = JS_VALUE_GET_INT(argv[i]);+ if (is_max)+ r1 = max_int(r1, a1);+ else+ r1 = min_int(r1, a1);++ }+ return JS_NewInt32(ctx, r1);+ } else {+ if (JS_ToFloat64(ctx, &r, argv[0]))+ return JS_EXCEPTION;+ i = 1;+ generic_case:+ while (i < argc) {+ if (JS_ToFloat64(ctx, &a, argv[i]))+ return JS_EXCEPTION;+ if (!isnan(r)) {+ if (isnan(a)) {+ r = a;+ } else {+ if (is_max)+ r = js_fmax(r, a);+ else+ r = js_fmin(r, a);+ }+ }+ i++;+ }+ return JS_NewFloat64(ctx, r);+ }+}++static double js_math_sign(double a)+{+ if (isnan(a) || a == 0.0)+ return a;+ if (a < 0)+ return -1;+ else+ return 1;+}++static double js_math_round(double a)+{+ JSFloat64Union u;+ uint64_t frac_mask, one;+ unsigned int e, s;++ u.d = a;+ e = (u.u64 >> 52) & 0x7ff;+ if (e < 1023) {+ /* abs(a) < 1 */+ if (e == (1023 - 1) && u.u64 != 0xbfe0000000000000) {+ /* abs(a) > 0.5 or a = 0.5: return +/-1.0 */+ u.u64 = (u.u64 & ((uint64_t)1 << 63)) | ((uint64_t)1023 << 52);+ } else {+ /* return +/-0.0 */+ u.u64 &= (uint64_t)1 << 63;+ }+ } else if (e < (1023 + 52)) {+ s = u.u64 >> 63;+ one = (uint64_t)1 << (52 - (e - 1023));+ frac_mask = one - 1;+ u.u64 += (one >> 1) - s;+ u.u64 &= ~frac_mask; /* truncate to an integer */+ }+ /* otherwise: abs(a) >= 2^52, or NaN, +/-Infinity: no change */+ return u.d;+}++static JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ double r, a;+ int i;++ r = 0;+ if (argc > 0) {+ if (JS_ToFloat64(ctx, &r, argv[0]))+ return JS_EXCEPTION;+ if (argc == 1) {+ r = fabs(r);+ } else {+ /* use the built-in function to minimize precision loss */+ for (i = 1; i < argc; i++) {+ if (JS_ToFloat64(ctx, &a, argv[i]))+ return JS_EXCEPTION;+ r = hypot(r, a);+ }+ }+ }+ return JS_NewFloat64(ctx, r);+}++static double js_math_fround(double a)+{+ return (float)a;+}++static JSValue js_math_imul(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ int a, b;++ if (JS_ToInt32(ctx, &a, argv[0]))+ return JS_EXCEPTION;+ if (JS_ToInt32(ctx, &b, argv[1]))+ return JS_EXCEPTION;+ /* purposely ignoring overflow */+ return JS_NewInt32(ctx, a * b);+}++static JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ uint32_t a, r;++ if (JS_ToUint32(ctx, &a, argv[0]))+ return JS_EXCEPTION;+ if (a == 0)+ r = 32;+ else+ r = clz32(a);+ return JS_NewInt32(ctx, r);+}++/* xorshift* random number generator by Marsaglia */+static uint64_t xorshift64star(uint64_t *pstate)+{+ uint64_t x;+ x = *pstate;+ x ^= x >> 12;+ x ^= x << 25;+ x ^= x >> 27;+ *pstate = x;+ return x * 0x2545F4914F6CDD1D;+}++static void js_random_init(JSContext *ctx)+{+ struct timeval tv;+ gettimeofday(&tv, NULL);+ ctx->random_state = ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec;+ /* the state must be non zero */+ if (ctx->random_state == 0)+ ctx->random_state = 1;+}++static JSValue js_math_random(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSFloat64Union u;+ uint64_t v;++ v = xorshift64star(&ctx->random_state);+ /* 1.0 <= u.d < 2 */+ u.u64 = ((uint64_t)0x3ff << 52) | (v >> 12);+ return __JS_NewFloat64(ctx, u.d - 1.0);+}++static const JSCFunctionListEntry js_math_funcs[] = {+ JS_CFUNC_MAGIC_DEF("min", 2, js_math_min_max, 0 ),+ JS_CFUNC_MAGIC_DEF("max", 2, js_math_min_max, 1 ),+ JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, fabs ),+ JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, floor ),+ JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, ceil ),+ JS_CFUNC_SPECIAL_DEF("round", 1, f_f, js_math_round ),+ JS_CFUNC_SPECIAL_DEF("sqrt", 1, f_f, sqrt ),++ JS_CFUNC_SPECIAL_DEF("acos", 1, f_f, acos ),+ JS_CFUNC_SPECIAL_DEF("asin", 1, f_f, asin ),+ JS_CFUNC_SPECIAL_DEF("atan", 1, f_f, atan ),+ JS_CFUNC_SPECIAL_DEF("atan2", 2, f_f_f, atan2 ),+ JS_CFUNC_SPECIAL_DEF("cos", 1, f_f, cos ),+ JS_CFUNC_SPECIAL_DEF("exp", 1, f_f, exp ),+ JS_CFUNC_SPECIAL_DEF("log", 1, f_f, log ),+ JS_CFUNC_SPECIAL_DEF("pow", 2, f_f_f, js_pow ),+ JS_CFUNC_SPECIAL_DEF("sin", 1, f_f, sin ),+ JS_CFUNC_SPECIAL_DEF("tan", 1, f_f, tan ),+ /* ES6 */+ JS_CFUNC_SPECIAL_DEF("trunc", 1, f_f, trunc ),+ JS_CFUNC_SPECIAL_DEF("sign", 1, f_f, js_math_sign ),+ JS_CFUNC_SPECIAL_DEF("cosh", 1, f_f, cosh ),+ JS_CFUNC_SPECIAL_DEF("sinh", 1, f_f, sinh ),+ JS_CFUNC_SPECIAL_DEF("tanh", 1, f_f, tanh ),+ JS_CFUNC_SPECIAL_DEF("acosh", 1, f_f, acosh ),+ JS_CFUNC_SPECIAL_DEF("asinh", 1, f_f, asinh ),+ JS_CFUNC_SPECIAL_DEF("atanh", 1, f_f, atanh ),+ JS_CFUNC_SPECIAL_DEF("expm1", 1, f_f, expm1 ),+ JS_CFUNC_SPECIAL_DEF("log1p", 1, f_f, log1p ),+ JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, log2 ),+ JS_CFUNC_SPECIAL_DEF("log10", 1, f_f, log10 ),+ JS_CFUNC_SPECIAL_DEF("cbrt", 1, f_f, cbrt ),+ JS_CFUNC_DEF("hypot", 2, js_math_hypot ),+ JS_CFUNC_DEF("random", 0, js_math_random ),+ JS_CFUNC_SPECIAL_DEF("fround", 1, f_f, js_math_fround ),+ JS_CFUNC_DEF("imul", 2, js_math_imul ),+ JS_CFUNC_DEF("clz32", 1, js_math_clz32 ),+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Math", JS_PROP_CONFIGURABLE ),+ JS_PROP_DOUBLE_DEF("E", 2.718281828459045, 0 ),+ JS_PROP_DOUBLE_DEF("LN10", 2.302585092994046, 0 ),+ JS_PROP_DOUBLE_DEF("LN2", 0.6931471805599453, 0 ),+ JS_PROP_DOUBLE_DEF("LOG2E", 1.4426950408889634, 0 ),+ JS_PROP_DOUBLE_DEF("LOG10E", 0.4342944819032518, 0 ),+ JS_PROP_DOUBLE_DEF("PI", 3.141592653589793, 0 ),+ JS_PROP_DOUBLE_DEF("SQRT1_2", 0.7071067811865476, 0 ),+ JS_PROP_DOUBLE_DEF("SQRT2", 1.4142135623730951, 0 ),+};++static const JSCFunctionListEntry js_math_obj[] = {+ JS_OBJECT_DEF("Math", js_math_funcs, countof(js_math_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),+};++/* Date */++#if 0+/* OS dependent: return the UTC time in ms since 1970. */+static JSValue js___date_now(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ int64_t d;+ struct timeval tv;+ gettimeofday(&tv, NULL);+ d = (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);+ return JS_NewInt64(ctx, d);+}+#endif++/* OS dependent: return the UTC time in microseconds since 1970. */+static JSValue js___date_clock(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ int64_t d;+ struct timeval tv;+ gettimeofday(&tv, NULL);+ d = (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;+ return JS_NewInt64(ctx, d);+}++/* OS dependent. d = argv[0] is in ms from 1970. Return the difference+ between local time and UTC time 'd' in minutes */+static int getTimezoneOffset(int64_t time) {+#if defined(_WIN32)+ /* XXX: TODO */+ return 0;+#else+ time_t ti;+ struct tm tm;++ time /= 1000; /* convert to seconds */+ if (sizeof(time_t) == 4) {+ /* on 32-bit systems, we need to clamp the time value to the+ range of `time_t`. This is better than truncating values to+ 32 bits and hopefully provides the same result as 64-bit+ implementation of localtime_r.+ */+ if ((time_t)-1 < 0) {+ if (time < INT32_MIN) {+ time = INT32_MIN;+ } else if (time > INT32_MAX) {+ time = INT32_MAX;+ }+ } else {+ if (time < 0) {+ time = 0;+ } else if (time > UINT32_MAX) {+ time = UINT32_MAX;+ }+ }+ }+ ti = time;+ localtime_r(&ti, &tm);+ return -tm.tm_gmtoff / 60;+#endif+}++#if 0+static JSValue js___date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ double dd;++ if (JS_ToFloat64(ctx, &dd, argv[0]))+ return JS_EXCEPTION;+ if (isnan(dd))+ return __JS_NewFloat64(ctx, dd);+ else+ return JS_NewInt32(ctx, getTimezoneOffset((int64_t)dd));+}++static JSValue js_get_prototype_from_ctor(JSContext *ctx, JSValueConst ctor,+ JSValueConst def_proto)+{+ JSValue proto;+ proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype);+ if (JS_IsException(proto))+ return proto;+ if (!JS_IsObject(proto)) {+ JS_FreeValue(ctx, proto);+ proto = JS_DupValue(ctx, def_proto);+ }+ return proto;+}++/* create a new date object */+static JSValue js___date_create(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue obj, proto;+ proto = js_get_prototype_from_ctor(ctx, argv[0], argv[1]);+ if (JS_IsException(proto))+ return proto;+ obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_DATE);+ JS_FreeValue(ctx, proto);+ if (!JS_IsException(obj))+ JS_SetObjectData(ctx, obj, JS_DupValue(ctx, argv[2]));+ return obj;+}+#endif++/* RegExp */++static void js_regexp_finalizer(JSRuntime *rt, JSValue val)+{+ JSObject *p = JS_VALUE_GET_OBJ(val);+ JSRegExp *re = &p->u.regexp;+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->bytecode));+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->pattern));+}++/* create a string containing the RegExp bytecode */+static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern,+ JSValueConst flags)+{+ const char *str;+ int re_flags, mask;+ uint8_t *re_bytecode_buf;+ size_t i, len;+ int re_bytecode_len;+ JSValue ret;+ char error_msg[64];++ re_flags = 0;+ if (!JS_IsUndefined(flags)) {+ str = JS_ToCStringLen(ctx, &len, flags);+ if (!str)+ return JS_EXCEPTION;+ /* XXX: re_flags = LRE_FLAG_OCTAL unless strict mode? */+ for (i = 0; i < len; i++) {+ switch(str[i]) {+ case 'g':+ mask = LRE_FLAG_GLOBAL;+ break;+ case 'i':+ mask = LRE_FLAG_IGNORECASE;+ break;+ case 'm':+ mask = LRE_FLAG_MULTILINE;+ break;+ case 's':+ mask = LRE_FLAG_DOTALL;+ break;+ case 'u':+ mask = LRE_FLAG_UTF16;+ break;+ case 'y':+ mask = LRE_FLAG_STICKY;+ break;+ default:+ goto bad_flags;+ }+ if ((re_flags & mask) != 0) {+ bad_flags:+ JS_FreeCString(ctx, str);+ return JS_ThrowSyntaxError(ctx, "invalid regular expression flags");+ }+ re_flags |= mask;+ }+ JS_FreeCString(ctx, str);+ }++ str = JS_ToCStringLen2(ctx, &len, pattern, !(re_flags & LRE_FLAG_UTF16));+ if (!str)+ return JS_EXCEPTION;+ re_bytecode_buf = lre_compile(&re_bytecode_len, error_msg,+ sizeof(error_msg), str, len, re_flags, ctx);+ JS_FreeCString(ctx, str);+ if (!re_bytecode_buf) {+ JS_ThrowSyntaxError(ctx, "%s", error_msg);+ return JS_EXCEPTION;+ }++ ret = js_new_string8(ctx, re_bytecode_buf, re_bytecode_len);+ js_free(ctx, re_bytecode_buf);+ return ret;+}++/* create a RegExp object from a string containing the RegExp bytecode+ and the source pattern */+static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor,+ JSValue pattern, JSValue bc)+{+ JSValue obj;+ JSObject *p;+ JSRegExp *re;++ /* sanity check */+ if (JS_VALUE_GET_TAG(bc) != JS_TAG_STRING ||+ JS_VALUE_GET_TAG(pattern) != JS_TAG_STRING) {+ JS_ThrowTypeError(ctx, "string expected");+ fail:+ JS_FreeValue(ctx, bc);+ JS_FreeValue(ctx, pattern);+ return JS_EXCEPTION;+ }++ obj = js_create_from_ctor(ctx, ctor, JS_CLASS_REGEXP);+ if (JS_IsException(obj))+ goto fail;+ p = JS_VALUE_GET_OBJ(obj);+ re = &p->u.regexp;+ re->pattern = JS_VALUE_GET_STRING(pattern);+ re->bytecode = JS_VALUE_GET_STRING(bc);+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0),+ JS_PROP_WRITABLE);+ return obj;+}++static JSRegExp *js_get_regexp(JSContext *ctx, JSValueConst obj, BOOL throw_error)+{+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {+ JSObject *p = JS_VALUE_GET_OBJ(obj);+ if (p->class_id == JS_CLASS_REGEXP)+ return &p->u.regexp;+ }+ if (throw_error) {+ JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP);+ }+ return NULL;+}++/* return < 0 if exception or TRUE/FALSE */+static int js_is_regexp(JSContext *ctx, JSValueConst obj)+{+ JSValue m;++ if (!JS_IsObject(obj))+ return FALSE;+ m = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_match);+ if (JS_IsException(m))+ return -1;+ if (!JS_IsUndefined(m))+ return JS_ToBoolFree(ctx, m);+ return js_get_regexp(ctx, obj, FALSE) != NULL;+}++static JSValue js_regexp_constructor(JSContext *ctx, JSValueConst new_target,+ int argc, JSValueConst *argv)+{+ JSValue pattern, flags, bc, val;+ JSValueConst pat, flags1;+ JSRegExp *re;+ int pat_is_regexp;++ pat = argv[0];+ flags1 = argv[1];+ pat_is_regexp = js_is_regexp(ctx, pat);+ if (pat_is_regexp < 0)+ return JS_EXCEPTION;+ if (JS_IsUndefined(new_target)) {+ /* called as a function */+ new_target = JS_GetActiveFunction(ctx);+ if (pat_is_regexp && JS_IsUndefined(flags1)) {+ JSValue ctor;+ BOOL res;+ ctor = JS_GetProperty(ctx, pat, JS_ATOM_constructor);+ if (JS_IsException(ctor))+ return ctor;+ res = js_same_value(ctx, ctor, new_target);+ JS_FreeValue(ctx, ctor);+ if (res)+ return JS_DupValue(ctx, pat);+ }+ }+ re = js_get_regexp(ctx, pat, FALSE);+ if (re) {+ pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));+ if (JS_IsUndefined(flags1)) {+ bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode));+ goto no_compilation;+ } else {+ flags = JS_ToString(ctx, flags1);+ if (JS_IsException(flags))+ goto fail;+ }+ } else {+ flags = JS_UNDEFINED;+ if (pat_is_regexp) {+ pattern = JS_GetProperty(ctx, pat, JS_ATOM_source);+ if (JS_IsException(pattern))+ goto fail;+ if (JS_IsUndefined(flags1)) {+ flags = JS_GetProperty(ctx, pat, JS_ATOM_flags);+ if (JS_IsException(flags))+ goto fail;+ } else {+ flags = JS_DupValue(ctx, flags1);+ }+ } else {+ pattern = JS_DupValue(ctx, pat);+ flags = JS_DupValue(ctx, flags1);+ }+ if (JS_IsUndefined(pattern)) {+ pattern = JS_AtomToString(ctx, JS_ATOM_empty_string);+ } else {+ val = pattern;+ pattern = JS_ToString(ctx, val);+ JS_FreeValue(ctx, val);+ if (JS_IsException(pattern))+ goto fail;+ }+ }+ bc = js_compile_regexp(ctx, pattern, flags);+ if (JS_IsException(bc))+ goto fail;+ JS_FreeValue(ctx, flags);+ no_compilation:+ return js_regexp_constructor_internal(ctx, new_target, pattern, bc);+ fail:+ JS_FreeValue(ctx, pattern);+ JS_FreeValue(ctx, flags);+ return JS_EXCEPTION;+}++static JSValue js_regexp_compile(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSRegExp *re1, *re;+ JSValueConst pattern1, flags1;+ JSValue bc, pattern;++ re = js_get_regexp(ctx, this_val, TRUE);+ if (!re)+ return JS_EXCEPTION;+ pattern1 = argv[0];+ flags1 = argv[1];+ re1 = js_get_regexp(ctx, pattern1, FALSE);+ if (re1) {+ if (!JS_IsUndefined(flags1))+ return JS_ThrowTypeError(ctx, "flags must be undefined");+ pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->pattern));+ bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->bytecode));+ } else {+ bc = JS_UNDEFINED;+ if (JS_IsUndefined(pattern1))+ pattern = JS_AtomToString(ctx, JS_ATOM_empty_string);+ else+ pattern = JS_ToString(ctx, pattern1);+ if (JS_IsException(pattern))+ goto fail;+ bc = js_compile_regexp(ctx, pattern, flags1);+ if (JS_IsException(bc))+ goto fail;+ }+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode));+ re->pattern = JS_VALUE_GET_STRING(pattern);+ re->bytecode = JS_VALUE_GET_STRING(bc);+ if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,+ JS_NewInt32(ctx, 0)) < 0)+ return JS_EXCEPTION;+ return JS_DupValue(ctx, this_val);+ fail:+ JS_FreeValue(ctx, pattern);+ JS_FreeValue(ctx, bc);+ return JS_EXCEPTION;+}++#if 0+static JSValue js_regexp_get___source(JSContext *ctx, JSValueConst this_val)+{+ JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);+ if (!re)+ return JS_EXCEPTION;+ return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));+}++static JSValue js_regexp_get___flags(JSContext *ctx, JSValueConst this_val)+{+ JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);+ int flags;++ if (!re)+ return JS_EXCEPTION;+ flags = lre_get_flags(re->bytecode->u.str8);+ return JS_NewInt32(ctx, flags);+}+#endif++static JSValue js_regexp_get_source(JSContext *ctx, JSValueConst this_val)+{+ JSRegExp *re;+ JSString *p;+ StringBuffer b_s, *b = &b_s;+ int i, n, c, c2, bra;++ if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)+ return JS_ThrowTypeErrorNotAnObject(ctx);++ if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP]))+ goto empty_regex;++ re = js_get_regexp(ctx, this_val, TRUE);+ if (!re)+ return JS_EXCEPTION;++ p = re->pattern;++ if (p->len == 0) {+ empty_regex:+ return JS_NewString(ctx, "(?:)");+ } + string_buffer_init2(ctx, b, p->len, p->is_wide_char);++ /* Escape '/' and newline sequences as needed */+ bra = 0;+ for (i = 0, n = p->len; i < n;) {+ c2 = -1;+ switch (c = string_get(p, i++)) {+ case '\\':+ if (i < n)+ c2 = string_get(p, i++);+ break;+ case ']':+ bra = 0;+ break;+ case '[':+ if (!bra) {+ if (i < n && string_get(p, i) == ']')+ c2 = string_get(p, i++);+ bra = 1;+ }+ break;+ case '\n':+ c = '\\';+ c2 = 'n';+ break;+ case '\r':+ c = '\\';+ c2 = 'r';+ break;+ case '/':+ if (!bra) {+ c = '\\';+ c2 = '/';+ }+ break;+ }+ string_buffer_putc16(b, c);+ if (c2 >= 0)+ string_buffer_putc16(b, c2);+ }+ return string_buffer_end(b);+}++static JSValue js_regexp_get_flag(JSContext *ctx, JSValueConst this_val, int mask)+{+ JSRegExp *re;+ int flags;++ if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)+ return JS_ThrowTypeErrorNotAnObject(ctx);++ re = js_get_regexp(ctx, this_val, FALSE);+ if (!re) {+ if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP]))+ return JS_UNDEFINED;+ else+ return JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP);+ }+ + flags = lre_get_flags(re->bytecode->u.str8);+ return JS_NewBool(ctx, (flags & mask) != 0);+}++static JSValue js_regexp_get_flags(JSContext *ctx, JSValueConst this_val)+{+ char str[8], *p = str;+ int res;++ if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)+ return JS_ThrowTypeErrorNotAnObject(ctx);++ res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_global));+ if (res < 0)+ goto exception;+ if (res)+ *p++ = 'g';+ res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "ignoreCase"));+ if (res < 0)+ goto exception;+ if (res)+ *p++ = 'i';+ res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "multiline"));+ if (res < 0)+ goto exception;+ if (res)+ *p++ = 'm';+ res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "dotAll"));+ if (res < 0)+ goto exception;+ if (res)+ *p++ = 's';+ res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_unicode));+ if (res < 0)+ goto exception;+ if (res)+ *p++ = 'u';+ res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "sticky"));+ if (res < 0)+ goto exception;+ if (res)+ *p++ = 'y';+ return JS_NewStringLen(ctx, str, p - str);++exception:+ return JS_EXCEPTION;+}++static JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue pattern, flags;+ StringBuffer b_s, *b = &b_s;++ if (!JS_IsObject(this_val))+ return JS_ThrowTypeErrorNotAnObject(ctx);++ string_buffer_init(ctx, b, 0);+ string_buffer_putc8(b, '/');+ pattern = JS_GetProperty(ctx, this_val, JS_ATOM_source);+ if (string_buffer_concat_value_free(b, pattern))+ goto fail;+ string_buffer_putc8(b, '/');+ flags = JS_GetProperty(ctx, this_val, JS_ATOM_flags);+ if (string_buffer_concat_value_free(b, flags))+ goto fail;+ return string_buffer_end(b);++fail:+ string_buffer_free(b);+ return JS_EXCEPTION;+}++BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size)+{+ JSContext *ctx = opaque;+ return js_check_stack_overflow(ctx->rt, alloca_size);+}++void *lre_realloc(void *opaque, void *ptr, size_t size)+{+ JSContext *ctx = opaque;+ /* No JS exception is raised here */+ return js_realloc_rt(ctx->rt, ptr, size);+}++static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);+ JSString *str;+ JSValue str_val, obj, val, groups = JS_UNDEFINED;+ uint8_t *re_bytecode;+ int ret;+ uint8_t **capture, *str_buf;+ int capture_count, shift, i, re_flags;+ int64_t last_index;+ const char *group_name_ptr;++ if (!re)+ return JS_EXCEPTION;+ str_val = JS_ToString(ctx, argv[0]);+ if (JS_IsException(str_val))+ return str_val;+ val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex);+ if (JS_IsException(val) ||+ JS_ToLengthFree(ctx, &last_index, val)) {+ JS_FreeValue(ctx, str_val);+ return JS_EXCEPTION;+ }+ re_bytecode = re->bytecode->u.str8;+ re_flags = lre_get_flags(re_bytecode);+ if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) {+ last_index = 0;+ }+ str = JS_VALUE_GET_STRING(str_val);+ capture_count = lre_get_capture_count(re_bytecode);+ capture = NULL;+ if (capture_count > 0) {+ capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2);+ if (!capture) {+ JS_FreeValue(ctx, str_val);+ return JS_EXCEPTION;+ }+ }+ shift = str->is_wide_char;+ str_buf = str->u.str8;+ if (last_index > str->len) {+ ret = 2;+ } else {+ ret = lre_exec(capture, re_bytecode,+ str_buf, last_index, str->len,+ shift, ctx);+ }+ obj = JS_NULL;+ if (ret != 1) {+ if (ret >= 0) {+ if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) {+ if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,+ JS_NewInt32(ctx, 0)) < 0)+ goto fail;+ }+ } else {+ JS_ThrowInternalError(ctx, "out of memory in regexp execution");+ goto fail;+ }+ JS_FreeValue(ctx, str_val);+ } else {+ int prop_flags;+ if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) {+ if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,+ JS_NewInt32(ctx, (capture[1] - str_buf) >> shift)) < 0)+ goto fail;+ }+ obj = JS_NewArray(ctx);+ if (JS_IsException(obj))+ goto fail;+ prop_flags = JS_PROP_C_W_E | JS_PROP_THROW;+ group_name_ptr = lre_get_groupnames(re_bytecode);+ if (group_name_ptr) {+ groups = JS_NewObjectProto(ctx, JS_NULL);+ if (JS_IsException(groups))+ goto fail;+ }++ for(i = 0; i < capture_count; i++) {+ int start, end;+ JSValue val;+ if (capture[2 * i] == NULL ||+ capture[2 * i + 1] == NULL) {+ val = JS_UNDEFINED;+ } else {+ start = (capture[2 * i] - str_buf) >> shift;+ end = (capture[2 * i + 1] - str_buf) >> shift;+ val = js_sub_string(ctx, str, start, end);+ if (JS_IsException(val))+ goto fail;+ }+ if (group_name_ptr && i > 0) {+ if (*group_name_ptr) {+ if (JS_DefinePropertyValueStr(ctx, groups, group_name_ptr,+ JS_DupValue(ctx, val),+ prop_flags) < 0) {+ JS_FreeValue(ctx, val);+ goto fail;+ }+ }+ group_name_ptr += strlen(group_name_ptr) + 1;+ }+ if (JS_DefinePropertyValueUint32(ctx, obj, i, val, prop_flags) < 0)+ goto fail;+ }+ if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_groups,+ groups, prop_flags) < 0)+ goto fail;+ if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_index,+ JS_NewInt32(ctx, (capture[0] - str_buf) >> shift), prop_flags) < 0)+ goto fail;+ if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_input, str_val, prop_flags) < 0)+ goto fail1;+ }+ js_free(ctx, capture);+ return obj;+fail:+ JS_FreeValue(ctx, groups);+ JS_FreeValue(ctx, str_val);+fail1:+ JS_FreeValue(ctx, obj);+ js_free(ctx, capture);+ return JS_EXCEPTION;+}++/* delete portions of a string that match a given regex */+static JSValue JS_RegExpDelete(JSContext *ctx, JSValueConst this_val, JSValueConst arg)+{+ JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);+ JSString *str;+ JSValue str_val, val;+ uint8_t *re_bytecode;+ int ret;+ uint8_t **capture, *str_buf;+ int capture_count, shift, re_flags;+ int next_src_pos, start, end;+ int64_t last_index;+ StringBuffer b_s, *b = &b_s;++ if (!re)+ return JS_EXCEPTION;++ string_buffer_init(ctx, b, 0);++ capture = NULL;+ str_val = JS_ToString(ctx, arg);+ if (JS_IsException(str_val))+ goto fail;+ str = JS_VALUE_GET_STRING(str_val);+ re_bytecode = re->bytecode->u.str8;+ re_flags = lre_get_flags(re_bytecode);+ if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) {+ last_index = 0;+ } else {+ val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex);+ if (JS_IsException(val) || JS_ToLengthFree(ctx, &last_index, val))+ goto fail;+ }+ capture_count = lre_get_capture_count(re_bytecode);+ if (capture_count > 0) {+ capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2);+ if (!capture)+ goto fail;+ }+ shift = str->is_wide_char;+ str_buf = str->u.str8;+ next_src_pos = 0;+ for (;;) {+ if (last_index > str->len)+ break;++ ret = lre_exec(capture, re_bytecode,+ str_buf, last_index, str->len, shift, ctx);+ if (ret != 1) {+ if (ret >= 0) {+ if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) {+ if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,+ JS_NewInt32(ctx, 0)) < 0)+ goto fail;+ }+ } else {+ JS_ThrowInternalError(ctx, "out of memory in regexp execution");+ goto fail;+ }+ break;+ }+ start = (capture[0] - str_buf) >> shift;+ end = (capture[1] - str_buf) >> shift;+ last_index = end;+ if (next_src_pos < start) {+ if (string_buffer_concat(b, str, next_src_pos, start))+ goto fail;+ }+ next_src_pos = end;+ if (!(re_flags & LRE_FLAG_GLOBAL)) {+ if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,+ JS_NewInt32(ctx, end)) < 0)+ goto fail;+ break;+ }+ if (end == start) {+ if (!(re_flags & LRE_FLAG_UTF16) || (unsigned)end >= str->len || !str->is_wide_char) {+ end++;+ } else {+ string_getc(str, &end);+ }+ }+ last_index = end;+ }+ if (string_buffer_concat(b, str, next_src_pos, str->len))+ goto fail;+ JS_FreeValue(ctx, str_val);+ js_free(ctx, capture);+ return string_buffer_end(b);+fail:+ JS_FreeValue(ctx, str_val);+ js_free(ctx, capture);+ string_buffer_free(b);+ return JS_EXCEPTION;+}++static JSValue JS_RegExpExec(JSContext *ctx, JSValueConst r, JSValueConst s)+{+ JSValue method, ret;++ method = JS_GetProperty(ctx, r, JS_ATOM_exec);+ if (JS_IsException(method))+ return method;+ if (JS_IsFunction(ctx, method)) {+ ret = JS_CallFree(ctx, method, r, 1, &s);+ if (JS_IsException(ret))+ return ret;+ if (!JS_IsObject(ret) && !JS_IsNull(ret)) {+ JS_FreeValue(ctx, ret);+ return JS_ThrowTypeError(ctx, "RegExp exec method must return an object or null");+ }+ return ret;+ }+ JS_FreeValue(ctx, method);+ return js_regexp_exec(ctx, r, 1, &s);+}++#if 0+static JSValue js_regexp___RegExpExec(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return JS_RegExpExec(ctx, argv[0], argv[1]);+}+static JSValue js_regexp___RegExpDelete(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return JS_RegExpDelete(ctx, argv[0], argv[1]);+}+#endif++static JSValue js_regexp_test(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue val;+ BOOL ret;++ val = JS_RegExpExec(ctx, this_val, argv[0]);+ if (JS_IsException(val))+ return JS_EXCEPTION;+ ret = !JS_IsNull(val);+ JS_FreeValue(ctx, val);+ return JS_NewBool(ctx, ret);+}++static JSValue js_regexp_Symbol_match(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ // [Symbol.match](str)+ JSValueConst rx = this_val;+ JSValue A, S, result, matchStr;+ int global, n, fullUnicode, isEmpty;+ JSString *p;++ if (!JS_IsObject(rx))+ return JS_ThrowTypeErrorNotAnObject(ctx);++ A = JS_UNDEFINED;+ result = JS_UNDEFINED;+ matchStr = JS_UNDEFINED;+ S = JS_ToString(ctx, argv[0]);+ if (JS_IsException(S))+ goto exception;++ global = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_global));+ if (global < 0)+ goto exception;++ if (!global) {+ A = JS_RegExpExec(ctx, rx, S);+ } else {+ fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode));+ if (fullUnicode < 0)+ goto exception;++ if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0)+ goto exception;+ A = JS_NewArray(ctx);+ if (JS_IsException(A))+ goto exception;+ n = 0;+ for(;;) {+ JS_FreeValue(ctx, result);+ result = JS_RegExpExec(ctx, rx, S);+ if (JS_IsException(result))+ goto exception;+ if (JS_IsNull(result))+ break;+ matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));+ if (JS_IsException(matchStr))+ goto exception;+ isEmpty = JS_IsEmptyString(matchStr);+ if (JS_SetPropertyInt64(ctx, A, n++, matchStr) < 0)+ goto exception;+ if (isEmpty) {+ int64_t thisIndex, nextIndex;+ if (JS_ToLengthFree(ctx, &thisIndex,+ JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0)+ goto exception;+ p = JS_VALUE_GET_STRING(S);+ nextIndex = string_advance_index(p, thisIndex, fullUnicode);+ if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0)+ goto exception;+ }+ }+ if (n == 0) {+ JS_FreeValue(ctx, A);+ A = JS_NULL;+ }+ }+ JS_FreeValue(ctx, result);+ JS_FreeValue(ctx, S);+ return A;++exception:+ JS_FreeValue(ctx, A);+ JS_FreeValue(ctx, result);+ JS_FreeValue(ctx, S);+ return JS_EXCEPTION;+}++typedef struct JSRegExpStringIteratorData {+ JSValue iterating_regexp;+ JSValue iterated_string;+ BOOL global;+ BOOL unicode;+ BOOL done;+} JSRegExpStringIteratorData;++static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val)+{+ JSObject *p = JS_VALUE_GET_OBJ(val);+ JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data;+ if (it) {+ JS_FreeValueRT(rt, it->iterating_regexp);+ JS_FreeValueRT(rt, it->iterated_string);+ js_free_rt(rt, it);+ }+}++static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func)+{+ JSObject *p = JS_VALUE_GET_OBJ(val);+ JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data;+ if (it) {+ JS_MarkValue(rt, it->iterating_regexp, mark_func);+ JS_MarkValue(rt, it->iterated_string, mark_func);+ }+}++static JSValue js_regexp_string_iterator_next(JSContext *ctx,+ JSValueConst this_val,+ int argc, JSValueConst *argv,+ BOOL *pdone, int magic)+{+ JSRegExpStringIteratorData *it;+ JSValueConst R, S;+ JSValue matchStr = JS_UNDEFINED, match = JS_UNDEFINED;+ JSString *sp;++ it = JS_GetOpaque2(ctx, this_val, JS_CLASS_REGEXP_STRING_ITERATOR);+ if (!it)+ goto exception;+ if (it->done) {+ *pdone = TRUE;+ return JS_UNDEFINED;+ }+ R = it->iterating_regexp;+ S = it->iterated_string;+ match = JS_RegExpExec(ctx, R, S);+ if (JS_IsException(match))+ goto exception;+ if (JS_IsNull(match)) {+ it->done = TRUE;+ *pdone = TRUE;+ return JS_UNDEFINED;+ } else if (it->global) {+ matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, match, 0));+ if (JS_IsException(matchStr))+ goto exception;+ if (JS_IsEmptyString(matchStr)) {+ int64_t thisIndex, nextIndex;+ if (JS_ToLengthFree(ctx, &thisIndex,+ JS_GetProperty(ctx, R, JS_ATOM_lastIndex)) < 0)+ goto exception;+ sp = JS_VALUE_GET_STRING(S);+ nextIndex = string_advance_index(sp, thisIndex, it->unicode);+ if (JS_SetProperty(ctx, R, JS_ATOM_lastIndex,+ JS_NewInt64(ctx, nextIndex)) < 0)+ goto exception;+ }+ JS_FreeValue(ctx, matchStr);+ } else {+ it->done = TRUE;+ }+ *pdone = FALSE;+ return match;+ exception:+ JS_FreeValue(ctx, match);+ JS_FreeValue(ctx, matchStr);+ *pdone = FALSE;+ return JS_EXCEPTION;+}++static JSValue js_regexp_Symbol_matchAll(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ // [Symbol.matchAll](str)+ JSValueConst R = this_val;+ JSValue S, C, flags, matcher, iter;+ JSValueConst args[2];+ JSString *strp;+ int64_t lastIndex;+ JSRegExpStringIteratorData *it;+ + if (!JS_IsObject(R))+ return JS_ThrowTypeErrorNotAnObject(ctx);++ C = JS_UNDEFINED;+ flags = JS_UNDEFINED;+ matcher = JS_UNDEFINED;+ iter = JS_UNDEFINED;+ + S = JS_ToString(ctx, argv[0]);+ if (JS_IsException(S))+ goto exception;+ C = JS_SpeciesConstructor(ctx, R, ctx->regexp_ctor);+ if (JS_IsException(C))+ goto exception;+ flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, R, JS_ATOM_flags));+ if (JS_IsException(flags))+ goto exception;+ args[0] = R;+ args[1] = flags;+ matcher = JS_CallConstructor(ctx, C, 2, args);+ if (JS_IsException(matcher))+ goto exception;+ if (JS_ToLengthFree(ctx, &lastIndex,+ JS_GetProperty(ctx, R, JS_ATOM_lastIndex)))+ goto exception;+ if (JS_SetProperty(ctx, matcher, JS_ATOM_lastIndex,+ JS_NewInt64(ctx, lastIndex)) < 0)+ goto exception;+ + iter = JS_NewObjectClass(ctx, JS_CLASS_REGEXP_STRING_ITERATOR);+ if (JS_IsException(iter))+ goto exception;+ it = js_malloc(ctx, sizeof(*it));+ if (!it)+ goto exception;+ it->iterating_regexp = matcher;+ it->iterated_string = S;+ strp = JS_VALUE_GET_STRING(flags);+ it->global = string_indexof_char(strp, 'g', 0) >= 0;+ it->unicode = string_indexof_char(strp, 'u', 0) >= 0;+ it->done = FALSE;+ JS_SetOpaque(iter, it);++ JS_FreeValue(ctx, C);+ JS_FreeValue(ctx, flags);+ return iter;+ exception:+ JS_FreeValue(ctx, S);+ JS_FreeValue(ctx, C);+ JS_FreeValue(ctx, flags);+ JS_FreeValue(ctx, matcher);+ JS_FreeValue(ctx, iter);+ return JS_EXCEPTION;+}++typedef struct ValueBuffer {+ JSContext *ctx;+ JSValue *arr;+ JSValue def[4];+ int len;+ int size;+ int error_status;+} ValueBuffer;++static int value_buffer_init(JSContext *ctx, ValueBuffer *b)+{+ b->ctx = ctx;+ b->len = 0;+ b->size = 4;+ b->error_status = 0;+ b->arr = b->def;+ return 0;+}++static void value_buffer_free(ValueBuffer *b)+{+ while (b->len > 0)+ JS_FreeValue(b->ctx, b->arr[--b->len]);+ if (b->arr != b->def)+ js_free(b->ctx, b->arr);+ b->arr = b->def;+ b->size = 4;+}++static int value_buffer_append(ValueBuffer *b, JSValue val)+{+ if (b->error_status)+ return -1;++ if (b->len >= b->size) {+ int new_size = (b->len + (b->len >> 1) + 31) & ~16;+ size_t slack;+ JSValue *new_arr;++ if (b->arr == b->def) {+ new_arr = js_realloc2(b->ctx, NULL, sizeof(*b->arr) * new_size, &slack);+ if (new_arr)+ memcpy(new_arr, b->def, sizeof b->def);+ } else {+ new_arr = js_realloc2(b->ctx, b->arr, sizeof(*b->arr) * new_size, &slack);+ }+ if (!new_arr) {+ value_buffer_free(b);+ JS_FreeValue(b->ctx, val);+ b->error_status = -1;+ return -1;+ }+ new_size += slack / sizeof(*new_arr);+ b->arr = new_arr;+ b->size = new_size;+ }+ b->arr[b->len++] = val;+ return 0;+}++static int js_is_standard_regexp(JSContext *ctx, JSValueConst rx)+{+ JSValue val;+ int res;++ val = JS_GetProperty(ctx, rx, JS_ATOM_constructor);+ if (JS_IsException(val))+ return -1;+ // rx.constructor === RegExp+ res = js_same_value(ctx, val, ctx->regexp_ctor);+ JS_FreeValue(ctx, val);+ if (res) {+ val = JS_GetProperty(ctx, rx, JS_ATOM_exec);+ if (JS_IsException(val))+ return -1;+ // rx.exec === RE_exec+ res = JS_IsCFunction(ctx, val, js_regexp_exec, 0);+ JS_FreeValue(ctx, val);+ }+ return res;+}++static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ // [Symbol.replace](str, rep)+ JSValueConst rx = this_val, rep = argv[1];+ JSValueConst args[6];+ JSValue str, rep_val, matched, tab, rep_str, namedCaptures, res;+ JSString *sp, *rp;+ StringBuffer b_s, *b = &b_s;+ ValueBuffer v_b, *results = &v_b;+ int nextSourcePosition, n, j, functionalReplace, is_global, fullUnicode;+ uint32_t nCaptures;+ int64_t position;++ if (!JS_IsObject(rx))+ return JS_ThrowTypeErrorNotAnObject(ctx);++ string_buffer_init(ctx, b, 0);+ value_buffer_init(ctx, results);++ rep_val = JS_UNDEFINED;+ matched = JS_UNDEFINED;+ tab = JS_UNDEFINED;+ rep_str = JS_UNDEFINED;+ namedCaptures = JS_UNDEFINED;++ str = JS_ToString(ctx, argv[0]);+ if (JS_IsException(str))+ goto exception;+ + sp = JS_VALUE_GET_STRING(str);+ rp = NULL;+ functionalReplace = JS_IsFunction(ctx, rep);+ if (!functionalReplace) {+ rep_val = JS_ToString(ctx, rep);+ if (JS_IsException(rep_val))+ goto exception;+ rp = JS_VALUE_GET_STRING(rep_val);+ }+ fullUnicode = 0;+ is_global = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_global));+ if (is_global < 0)+ goto exception;+ if (is_global) {+ fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode));+ if (fullUnicode < 0)+ goto exception;+ if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0)+ goto exception;+ }++ if (rp && rp->len == 0 && is_global && js_is_standard_regexp(ctx, rx)) {+ /* use faster version for simple cases */+ res = JS_RegExpDelete(ctx, rx, str);+ goto done;+ }+ for(;;) {+ JSValue result;+ result = JS_RegExpExec(ctx, rx, str);+ if (JS_IsException(result))+ goto exception;+ if (JS_IsNull(result))+ break;+ if (value_buffer_append(results, result) < 0)+ goto exception;+ if (!is_global)+ break;+ JS_FreeValue(ctx, matched);+ matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));+ if (JS_IsException(matched))+ goto exception;+ if (JS_IsEmptyString(matched)) {+ /* always advance of at least one char */+ int64_t thisIndex, nextIndex;+ if (JS_ToLengthFree(ctx, &thisIndex, JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0)+ goto exception;+ nextIndex = string_advance_index(sp, thisIndex, fullUnicode);+ if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0)+ goto exception;+ }+ }+ nextSourcePosition = 0;+ for(j = 0; j < results->len; j++) {+ JSValueConst result;+ result = results->arr[j];+ if (js_get_length32(ctx, &nCaptures, result) < 0)+ goto exception;+ JS_FreeValue(ctx, matched);+ matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));+ if (JS_IsException(matched))+ goto exception;+ if (JS_ToLengthFree(ctx, &position, JS_GetProperty(ctx, result, JS_ATOM_index)))+ goto exception;+ if (position > sp->len)+ position = sp->len;+ else if (position < 0)+ position = 0;+ /* ignore substition if going backward (can happen+ with custom regexp object) */+ JS_FreeValue(ctx, tab);+ tab = JS_NewArray(ctx);+ if (JS_IsException(tab))+ goto exception;+ if (JS_DefinePropertyValueInt64(ctx, tab, 0, JS_DupValue(ctx, matched),+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)+ goto exception;+ for(n = 1; n < nCaptures; n++) {+ JSValue capN;+ capN = JS_GetPropertyInt64(ctx, result, n);+ if (JS_IsException(capN))+ goto exception;+ if (!JS_IsUndefined(capN)) {+ capN = JS_ToStringFree(ctx, capN);+ if (JS_IsException(capN))+ goto exception;+ }+ if (JS_DefinePropertyValueInt64(ctx, tab, n, capN,+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)+ goto exception;+ }+ JS_FreeValue(ctx, namedCaptures);+ namedCaptures = JS_GetProperty(ctx, result, JS_ATOM_groups);+ if (JS_IsException(namedCaptures))+ goto exception;+ if (functionalReplace) {+ if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_NewInt32(ctx, position), JS_PROP_C_W_E | JS_PROP_THROW) < 0)+ goto exception;+ if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, str), JS_PROP_C_W_E | JS_PROP_THROW) < 0)+ goto exception;+ if (!JS_IsUndefined(namedCaptures)) {+ if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, namedCaptures), JS_PROP_C_W_E | JS_PROP_THROW) < 0)+ goto exception;+ }+ args[0] = JS_UNDEFINED;+ args[1] = tab;+ JS_FreeValue(ctx, rep_str);+ rep_str = JS_ToStringFree(ctx, js_function_apply(ctx, rep, 2, args, 0));+ } else {+ JSValue namedCaptures1;+ if (!JS_IsUndefined(namedCaptures)) {+ namedCaptures1 = JS_ToObject(ctx, namedCaptures);+ if (JS_IsException(namedCaptures1))+ goto exception;+ } else {+ namedCaptures1 = JS_UNDEFINED;+ }+ args[0] = matched;+ args[1] = str;+ args[2] = JS_NewInt32(ctx, position);+ args[3] = tab;+ args[4] = namedCaptures1;+ args[5] = rep_val;+ JS_FreeValue(ctx, rep_str);+ rep_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args);+ JS_FreeValue(ctx, namedCaptures1);+ }+ if (JS_IsException(rep_str))+ goto exception;+ if (position >= nextSourcePosition) {+ string_buffer_concat(b, sp, nextSourcePosition, position);+ string_buffer_concat_value(b, rep_str);+ nextSourcePosition = position + JS_VALUE_GET_STRING(matched)->len;+ }+ }+ string_buffer_concat(b, sp, nextSourcePosition, sp->len);+ res = string_buffer_end(b);+ goto done1;++exception:+ res = JS_EXCEPTION;+done:+ string_buffer_free(b);+done1:+ value_buffer_free(results);+ JS_FreeValue(ctx, rep_val);+ JS_FreeValue(ctx, matched);+ JS_FreeValue(ctx, tab);+ JS_FreeValue(ctx, rep_str);+ JS_FreeValue(ctx, namedCaptures);+ JS_FreeValue(ctx, str);+ return res;+}++static JSValue js_regexp_Symbol_search(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValueConst rx = this_val;+ JSValue str, previousLastIndex, currentLastIndex, result, index;++ if (!JS_IsObject(rx))+ return JS_ThrowTypeErrorNotAnObject(ctx);++ result = JS_UNDEFINED;+ currentLastIndex = JS_UNDEFINED;+ previousLastIndex = JS_UNDEFINED;+ str = JS_ToString(ctx, argv[0]);+ if (JS_IsException(str))+ goto exception;++ previousLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex);+ if (JS_IsException(previousLastIndex))+ goto exception;++ if (!js_same_value(ctx, previousLastIndex, JS_NewInt32(ctx, 0))) {+ if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) {+ goto exception;+ }+ }+ result = JS_RegExpExec(ctx, rx, str);+ if (JS_IsException(result))+ goto exception;+ currentLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex);+ if (JS_IsException(currentLastIndex))+ goto exception;+ if (js_same_value(ctx, currentLastIndex, previousLastIndex)) {+ JS_FreeValue(ctx, previousLastIndex);+ } else {+ if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, previousLastIndex) < 0) {+ previousLastIndex = JS_UNDEFINED;+ goto exception;+ }+ }+ JS_FreeValue(ctx, str);+ JS_FreeValue(ctx, currentLastIndex);++ if (JS_IsNull(result)) {+ return JS_NewInt32(ctx, -1);+ } else {+ index = JS_GetProperty(ctx, result, JS_ATOM_index);+ JS_FreeValue(ctx, result);+ return index;+ }++exception:+ JS_FreeValue(ctx, result);+ JS_FreeValue(ctx, str);+ JS_FreeValue(ctx, currentLastIndex);+ JS_FreeValue(ctx, previousLastIndex);+ return JS_EXCEPTION;+}++static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ // [Symbol.split](str, limit)+ JSValueConst rx = this_val;+ JSValueConst args[2];+ JSValue str, ctor, splitter, A, flags, z, sub;+ JSString *strp;+ uint32_t lim, size, p, q;+ int unicodeMatching;+ int64_t lengthA, e, numberOfCaptures, i;++ if (!JS_IsObject(rx))+ return JS_ThrowTypeErrorNotAnObject(ctx);++ ctor = JS_UNDEFINED;+ splitter = JS_UNDEFINED;+ A = JS_UNDEFINED;+ flags = JS_UNDEFINED;+ z = JS_UNDEFINED;+ str = JS_ToString(ctx, argv[0]);+ if (JS_IsException(str))+ goto exception;+ ctor = JS_SpeciesConstructor(ctx, rx, ctx->regexp_ctor);+ if (JS_IsException(ctor))+ goto exception;+ flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_flags));+ if (JS_IsException(flags))+ goto exception;+ strp = JS_VALUE_GET_STRING(flags);+ unicodeMatching = string_indexof_char(strp, 'u', 0) >= 0;+ if (string_indexof_char(strp, 'y', 0) < 0) {+ flags = JS_ConcatString3(ctx, "", flags, "y");+ if (JS_IsException(flags))+ goto exception;+ }+ args[0] = rx;+ args[1] = flags;+ splitter = JS_CallConstructor(ctx, ctor, 2, args);+ if (JS_IsException(splitter))+ goto exception;+ A = JS_NewArray(ctx);+ if (JS_IsException(A))+ goto exception;+ lengthA = 0;+ if (JS_IsUndefined(argv[1])) {+ lim = 0xffffffff;+ } else {+ if (JS_ToUint32(ctx, &lim, argv[1]) < 0)+ goto exception;+ if (lim == 0)+ goto done;+ }+ strp = JS_VALUE_GET_STRING(str);+ p = q = 0;+ size = strp->len;+ if (size == 0) {+ z = JS_RegExpExec(ctx, splitter, str);+ if (JS_IsException(z))+ goto exception;+ if (JS_IsNull(z))+ goto add_tail;+ goto done;+ }+ while (q < size) {+ if (JS_SetProperty(ctx, splitter, JS_ATOM_lastIndex, JS_NewInt32(ctx, q)) < 0)+ goto exception;+ JS_FreeValue(ctx, z); + z = JS_RegExpExec(ctx, splitter, str);+ if (JS_IsException(z))+ goto exception;+ if (JS_IsNull(z)) {+ q = string_advance_index(strp, q, unicodeMatching);+ } else {+ if (JS_ToLengthFree(ctx, &e, JS_GetProperty(ctx, splitter, JS_ATOM_lastIndex)))+ goto exception;+ if (e > size)+ e = size;+ if (e == p) {+ q = string_advance_index(strp, q, unicodeMatching);+ } else {+ sub = js_sub_string(ctx, strp, p, q);+ if (JS_IsException(sub))+ goto exception;+ if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub,+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)+ goto exception;+ if (lengthA == lim)+ goto done;+ p = e;+ if (js_get_length64(ctx, &numberOfCaptures, z))+ goto exception;+ for(i = 1; i < numberOfCaptures; i++) {+ sub = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, z, i));+ if (JS_IsException(sub))+ goto exception;+ if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0)+ goto exception;+ if (lengthA == lim)+ goto done;+ }+ q = p;+ }+ }+ }+add_tail:+ if (p > size)+ p = size;+ sub = js_sub_string(ctx, strp, p, size);+ if (JS_IsException(sub))+ goto exception;+ if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0)+ goto exception;+ goto done;+exception:+ JS_FreeValue(ctx, A);+ A = JS_EXCEPTION;+done:+ JS_FreeValue(ctx, str);+ JS_FreeValue(ctx, ctor);+ JS_FreeValue(ctx, splitter);+ JS_FreeValue(ctx, flags);+ JS_FreeValue(ctx, z); + return A;+}++static const JSCFunctionListEntry js_regexp_funcs[] = {+ JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),+ //JS_CFUNC_DEF("__RegExpExec", 2, js_regexp___RegExpExec ),+ //JS_CFUNC_DEF("__RegExpDelete", 2, js_regexp___RegExpDelete ),+};++static const JSCFunctionListEntry js_regexp_proto_funcs[] = {+ JS_CGETSET_DEF("flags", js_regexp_get_flags, NULL ),+ JS_CGETSET_DEF("source", js_regexp_get_source, NULL ),+ JS_CGETSET_MAGIC_DEF("global", js_regexp_get_flag, NULL, 1 ),+ JS_CGETSET_MAGIC_DEF("ignoreCase", js_regexp_get_flag, NULL, 2 ),+ JS_CGETSET_MAGIC_DEF("multiline", js_regexp_get_flag, NULL, 4 ),+ JS_CGETSET_MAGIC_DEF("dotAll", js_regexp_get_flag, NULL, 8 ),+ JS_CGETSET_MAGIC_DEF("unicode", js_regexp_get_flag, NULL, 16 ),+ JS_CGETSET_MAGIC_DEF("sticky", js_regexp_get_flag, NULL, 32 ),+ JS_CFUNC_DEF("exec", 1, js_regexp_exec ),+ JS_CFUNC_DEF("compile", 2, js_regexp_compile ),+ JS_CFUNC_DEF("test", 1, js_regexp_test ),+ JS_CFUNC_DEF("toString", 0, js_regexp_toString ),+ JS_CFUNC_DEF("[Symbol.replace]", 2, js_regexp_Symbol_replace ),+ JS_CFUNC_DEF("[Symbol.match]", 1, js_regexp_Symbol_match ),+ JS_CFUNC_DEF("[Symbol.matchAll]", 1, js_regexp_Symbol_matchAll ),+ JS_CFUNC_DEF("[Symbol.search]", 1, js_regexp_Symbol_search ),+ JS_CFUNC_DEF("[Symbol.split]", 2, js_regexp_Symbol_split ),+ //JS_CGETSET_DEF("__source", js_regexp_get___source, NULL ),+ //JS_CGETSET_DEF("__flags", js_regexp_get___flags, NULL ),+};++static const JSCFunctionListEntry js_regexp_string_iterator_proto_funcs[] = {+ JS_ITERATOR_NEXT_DEF("next", 0, js_regexp_string_iterator_next, 0 ),+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "RegExp String Iterator", JS_PROP_CONFIGURABLE ),+};++void JS_AddIntrinsicRegExpCompiler(JSContext *ctx)+{+ ctx->compile_regexp = js_compile_regexp;+}++void JS_AddIntrinsicRegExp(JSContext *ctx)+{+ JSValueConst obj;++ JS_AddIntrinsicRegExpCompiler(ctx);++ ctx->class_proto[JS_CLASS_REGEXP] = JS_NewObject(ctx);+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP], js_regexp_proto_funcs,+ countof(js_regexp_proto_funcs));+ obj = JS_NewGlobalCConstructor(ctx, "RegExp", js_regexp_constructor, 2,+ ctx->class_proto[JS_CLASS_REGEXP]);+ ctx->regexp_ctor = JS_DupValue(ctx, obj);+ JS_SetPropertyFunctionList(ctx, obj, js_regexp_funcs, countof(js_regexp_funcs));++ ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR] =+ JS_NewObjectProto(ctx, ctx->iterator_proto);+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR],+ js_regexp_string_iterator_proto_funcs,+ countof(js_regexp_string_iterator_proto_funcs));+}++/* JSON */++static int json_parse_expect(JSParseState *s, int tok)+{+ if (s->token.val != tok) {+ /* XXX: dump token correctly in all cases */+ return js_parse_error(s, "expecting '%c'", tok);+ }+ return json_next_token(s);+}++static JSValue json_parse_value(JSParseState *s)+{+ JSContext *ctx = s->ctx;+ JSValue val = JS_NULL;+ int ret;++ switch(s->token.val) {+ case '{':+ {+ JSValue prop_val;+ JSAtom prop_name;+ + if (json_next_token(s))+ goto fail;+ val = JS_NewObject(ctx);+ if (JS_IsException(val))+ goto fail;+ if (s->token.val != '}') {+ for(;;) {+ if (s->token.val == TOK_STRING) {+ prop_name = JS_ValueToAtom(ctx, s->token.u.str.str);+ if (prop_name == JS_ATOM_NULL)+ goto fail;+ } else if (s->ext_json && s->token.val == TOK_IDENT) {+ prop_name = JS_DupAtom(ctx, s->token.u.ident.atom);+ } else {+ js_parse_error(s, "expecting property name");+ goto fail;+ }+ if (json_next_token(s))+ goto fail1;+ if (json_parse_expect(s, ':'))+ goto fail1;+ prop_val = json_parse_value(s);+ if (JS_IsException(prop_val)) {+ fail1:+ JS_FreeAtom(ctx, prop_name);+ goto fail;+ }+ ret = JS_DefinePropertyValue(ctx, val, prop_name,+ prop_val, JS_PROP_C_W_E);+ JS_FreeAtom(ctx, prop_name);+ if (ret < 0)+ goto fail;++ if (s->token.val != ',')+ break;+ if (json_next_token(s))+ goto fail;+ if (s->ext_json && s->token.val == '}')+ break;+ }+ }+ if (json_parse_expect(s, '}'))+ goto fail;+ }+ break;+ case '[':+ {+ JSValue el;+ uint32_t idx;++ if (json_next_token(s))+ goto fail;+ val = JS_NewArray(ctx);+ if (JS_IsException(val))+ goto fail;+ if (s->token.val != ']') {+ idx = 0;+ for(;;) {+ el = json_parse_value(s);+ if (JS_IsException(el))+ goto fail;+ ret = JS_DefinePropertyValueUint32(ctx, val, idx, el, JS_PROP_C_W_E);+ if (ret < 0)+ goto fail;+ if (s->token.val != ',')+ break;+ if (json_next_token(s))+ goto fail;+ idx++;+ if (s->ext_json && s->token.val == ']')+ break;+ }+ }+ if (json_parse_expect(s, ']'))+ goto fail;+ }+ break;+ case TOK_STRING:+ val = JS_DupValue(ctx, s->token.u.str.str);+ if (json_next_token(s))+ goto fail;+ break;+ case TOK_NUMBER:+ val = s->token.u.num.val;+ if (json_next_token(s))+ goto fail;+ break;+ case TOK_IDENT:+ if (s->token.u.ident.atom == JS_ATOM_false ||+ s->token.u.ident.atom == JS_ATOM_true) {+ val = JS_NewBool(ctx, (s->token.u.ident.atom == JS_ATOM_true));+ } else if (s->token.u.ident.atom == JS_ATOM_null) {+ val = JS_NULL;+ } else {+ goto def_token;+ }+ if (json_next_token(s))+ goto fail;+ break;+ default:+ def_token:+ if (s->token.val == TOK_EOF) {+ js_parse_error(s, "unexpected end of input");+ } else {+ js_parse_error(s, "unexpected token: '%.*s'",+ (int)(s->buf_ptr - s->token.ptr), s->token.ptr);+ }+ goto fail;+ }+ return val;+ fail:+ JS_FreeValue(ctx, val);+ return JS_EXCEPTION;+}++JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len,+ const char *filename, int flags)+{+ JSParseState s1, *s = &s1;+ JSValue val = JS_UNDEFINED;++ js_parse_init(ctx, s, buf, buf_len, filename);+ s->ext_json = ((flags & JS_PARSE_JSON_EXT) != 0);+ if (json_next_token(s))+ goto fail;+ val = json_parse_value(s);+ if (JS_IsException(val))+ goto fail;+ if (s->token.val != TOK_EOF) {+ if (js_parse_error(s, "unexpected data at the end"))+ goto fail;+ }+ return val;+ fail:+ JS_FreeValue(ctx, val);+ free_token(s, &s->token);+ return JS_EXCEPTION;+}++JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len,+ const char *filename)+{+ return JS_ParseJSON2(ctx, buf, buf_len, filename, 0); +}++static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder,+ JSAtom name, JSValueConst reviver)+{+ JSValue val, new_el, name_val, res;+ JSValueConst args[2];+ int ret, is_array;+ uint32_t i, len = 0;+ JSAtom prop;+ JSPropertyEnum *atoms = NULL;++ if (js_check_stack_overflow(ctx->rt, 0)) {+ return JS_ThrowStackOverflow(ctx);+ }++ val = JS_GetProperty(ctx, holder, name);+ if (JS_IsException(val))+ return val;+ if (JS_IsObject(val)) {+ is_array = JS_IsArray(ctx, val);+ if (is_array < 0)+ goto fail;+ if (is_array) {+ if (js_get_length32(ctx, &len, val))+ goto fail;+ } else {+ ret = JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, JS_VALUE_GET_OBJ(val), JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK);+ if (ret < 0)+ goto fail;+ }+ for(i = 0; i < len; i++) {+ if (is_array) {+ prop = JS_NewAtomUInt32(ctx, i);+ if (prop == JS_ATOM_NULL)+ goto fail;+ } else {+ prop = JS_DupAtom(ctx, atoms[i].atom);+ }+ new_el = internalize_json_property(ctx, val, prop, reviver);+ if (JS_IsException(new_el)) {+ JS_FreeAtom(ctx, prop);+ goto fail;+ }+ if (JS_IsUndefined(new_el)) {+ ret = JS_DeleteProperty(ctx, val, prop, 0);+ } else {+ ret = JS_DefinePropertyValue(ctx, val, prop, new_el, JS_PROP_C_W_E);+ }+ JS_FreeAtom(ctx, prop);+ if (ret < 0)+ goto fail;+ }+ }+ js_free_prop_enum(ctx, atoms, len);+ atoms = NULL;+ name_val = JS_AtomToValue(ctx, name);+ if (JS_IsException(name_val))+ goto fail;+ args[0] = name_val;+ args[1] = val;+ res = JS_Call(ctx, reviver, holder, 2, args);+ JS_FreeValue(ctx, name_val);+ JS_FreeValue(ctx, val);+ return res;+ fail:+ js_free_prop_enum(ctx, atoms, len);+ JS_FreeValue(ctx, val);+ return JS_EXCEPTION;+}++static JSValue js_json_parse(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue obj, root;+ JSValueConst reviver;+ const char *str;+ size_t len;++ str = JS_ToCStringLen(ctx, &len, argv[0]);+ if (!str)+ return JS_EXCEPTION;+ obj = JS_ParseJSON(ctx, str, len, "<input>");+ JS_FreeCString(ctx, str);+ if (JS_IsException(obj))+ return obj;+ if (argc > 1 && JS_IsFunction(ctx, argv[1])) {+ reviver = argv[1];+ root = JS_NewObject(ctx);+ if (JS_IsException(root)) {+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+ }+ if (JS_DefinePropertyValue(ctx, root, JS_ATOM_empty_string, obj,+ JS_PROP_C_W_E) < 0) {+ JS_FreeValue(ctx, root);+ return JS_EXCEPTION;+ }+ obj = internalize_json_property(ctx, root, JS_ATOM_empty_string,+ reviver);+ JS_FreeValue(ctx, root);+ }+ return obj;+}++typedef struct JSONStringifyContext {+ JSValueConst replacer_func;+ JSValue stack;+ JSValue property_list;+ JSValue gap;+ JSValue empty;+ StringBuffer *b;+} JSONStringifyContext;++static JSValue JS_ToQuotedStringFree(JSContext *ctx, JSValue val) {+ JSValue r = JS_ToQuotedString(ctx, val);+ JS_FreeValue(ctx, val);+ return r;+}++static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc,+ JSValueConst holder, JSValue val, JSValueConst key)+{+ JSValue v;+ JSValueConst args[2];++ if (JS_IsObject(val)+#ifdef CONFIG_BIGNUM+ || JS_IsBigInt(ctx, val) /* XXX: probably useless */+#endif+ ) {+ JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON);+ if (JS_IsException(f))+ goto exception;+ if (JS_IsFunction(ctx, f)) {+ v = JS_CallFree(ctx, f, val, 1, &key);+ JS_FreeValue(ctx, val);+ val = v;+ if (JS_IsException(val))+ goto exception;+ } else {+ JS_FreeValue(ctx, f);+ }+ }++ if (!JS_IsUndefined(jsc->replacer_func)) {+ args[0] = key;+ args[1] = val;+ v = JS_Call(ctx, jsc->replacer_func, holder, 2, args);+ JS_FreeValue(ctx, val);+ val = v;+ if (JS_IsException(val))+ goto exception;+ }++ switch (JS_VALUE_GET_NORM_TAG(val)) {+ case JS_TAG_OBJECT:+ if (JS_IsFunction(ctx, val))+ break;+ case JS_TAG_STRING:+ case JS_TAG_INT:+ case JS_TAG_FLOAT64:+#ifdef CONFIG_BIGNUM+ case JS_TAG_BIG_FLOAT:+#endif+ case JS_TAG_BOOL:+ case JS_TAG_NULL:+#ifdef CONFIG_BIGNUM+ case JS_TAG_BIG_INT:+#endif+ case JS_TAG_EXCEPTION:+ return val;+ default:+ break;+ }+ JS_FreeValue(ctx, val);+ return JS_UNDEFINED;++exception:+ JS_FreeValue(ctx, val);+ return JS_EXCEPTION;+}++static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,+ JSValueConst holder, JSValue val,+ JSValueConst indent)+{+ JSValue indent1, sep, sep1, tab, v, prop;+ JSObject *p;+ int64_t i, len;+ int cl, ret;+ BOOL has_content;+ + indent1 = JS_UNDEFINED;+ sep = JS_UNDEFINED;+ sep1 = JS_UNDEFINED;+ tab = JS_UNDEFINED;+ prop = JS_UNDEFINED;++ switch (JS_VALUE_GET_NORM_TAG(val)) {+ case JS_TAG_OBJECT:+ p = JS_VALUE_GET_OBJ(val);+ cl = p->class_id;+ if (cl == JS_CLASS_STRING) {+ val = JS_ToStringFree(ctx, val);+ if (JS_IsException(val))+ goto exception;+ val = JS_ToQuotedStringFree(ctx, val);+ if (JS_IsException(val))+ goto exception;+ return string_buffer_concat_value_free(jsc->b, val);+ } else if (cl == JS_CLASS_NUMBER) {+ val = JS_ToNumberFree(ctx, val);+ if (JS_IsException(val))+ goto exception;+ return string_buffer_concat_value_free(jsc->b, val);+ } else if (cl == JS_CLASS_BOOLEAN) {+ ret = string_buffer_concat_value(jsc->b, p->u.object_data);+ JS_FreeValue(ctx, val);+ return ret;+ }+#ifdef CONFIG_BIGNUM+ else if (cl == JS_CLASS_BIG_FLOAT) {+ return string_buffer_concat_value_free(jsc->b, val);+ } else if (cl == JS_CLASS_BIG_INT) {+ JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify");+ goto exception;+ }+#endif+ v = js_array_includes(ctx, jsc->stack, 1, (JSValueConst *)&val);+ if (JS_IsException(v))+ goto exception;+ if (JS_ToBoolFree(ctx, v)) {+ JS_ThrowTypeError(ctx, "circular reference");+ goto exception;+ }+ indent1 = JS_ConcatString(ctx, JS_DupValue(ctx, indent), JS_DupValue(ctx, jsc->gap));+ if (JS_IsException(indent1))+ goto exception;+ if (!JS_IsEmptyString(jsc->gap)) {+ sep = JS_ConcatString3(ctx, "\n", JS_DupValue(ctx, indent1), "");+ if (JS_IsException(sep))+ goto exception;+ sep1 = JS_NewString(ctx, " ");+ if (JS_IsException(sep1))+ goto exception;+ } else {+ sep = JS_DupValue(ctx, jsc->empty);+ sep1 = JS_DupValue(ctx, jsc->empty);+ }+ v = js_array_push(ctx, jsc->stack, 1, (JSValueConst *)&val, 0);+ if (check_exception_free(ctx, v))+ goto exception;+ ret = JS_IsArray(ctx, val);+ if (ret < 0)+ goto exception;+ if (ret) {+ if (js_get_length64(ctx, &len, val))+ goto exception;+ string_buffer_putc8(jsc->b, '[');+ for(i = 0; i < len; i++) {+ if (i > 0)+ string_buffer_putc8(jsc->b, ',');+ string_buffer_concat_value(jsc->b, sep);+ v = JS_GetPropertyInt64(ctx, val, i);+ if (JS_IsException(v))+ goto exception;+ /* XXX: could do this string conversion only when needed */+ prop = JS_ToStringFree(ctx, JS_NewInt64(ctx, i));+ if (JS_IsException(prop))+ goto exception;+ v = js_json_check(ctx, jsc, val, v, prop);+ JS_FreeValue(ctx, prop);+ prop = JS_UNDEFINED;+ if (JS_IsException(v))+ goto exception;+ if (JS_IsUndefined(v))+ v = JS_NULL;+ if (js_json_to_str(ctx, jsc, val, v, indent1))+ goto exception;+ }+ if (len > 0 && !JS_IsEmptyString(jsc->gap)) {+ string_buffer_putc8(jsc->b, '\n');+ string_buffer_concat_value(jsc->b, indent);+ }+ string_buffer_putc8(jsc->b, ']');+ } else {+ if (!JS_IsUndefined(jsc->property_list))+ tab = JS_DupValue(ctx, jsc->property_list);+ else+ tab = js_object_keys(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val, JS_ITERATOR_KIND_KEY);+ if (JS_IsException(tab))+ goto exception;+ if (js_get_length64(ctx, &len, tab))+ goto exception;+ string_buffer_putc8(jsc->b, '{');+ has_content = FALSE;+ for(i = 0; i < len; i++) {+ JS_FreeValue(ctx, prop);+ prop = JS_GetPropertyInt64(ctx, tab, i);+ if (JS_IsException(prop))+ goto exception;+ v = JS_GetPropertyValue(ctx, val, JS_DupValue(ctx, prop));+ if (JS_IsException(v))+ goto exception;+ v = js_json_check(ctx, jsc, val, v, prop);+ if (JS_IsException(v))+ goto exception;+ if (!JS_IsUndefined(v)) {+ if (has_content)+ string_buffer_putc8(jsc->b, ',');+ prop = JS_ToQuotedStringFree(ctx, prop);+ if (JS_IsException(prop)) {+ JS_FreeValue(ctx, v);+ goto exception;+ }+ string_buffer_concat_value(jsc->b, sep);+ string_buffer_concat_value(jsc->b, prop);+ string_buffer_putc8(jsc->b, ':');+ string_buffer_concat_value(jsc->b, sep1);+ if (js_json_to_str(ctx, jsc, val, v, indent1))+ goto exception;+ has_content = TRUE;+ }+ }+ if (has_content && JS_VALUE_GET_STRING(jsc->gap)->len != 0) {+ string_buffer_putc8(jsc->b, '\n');+ string_buffer_concat_value(jsc->b, indent);+ }+ string_buffer_putc8(jsc->b, '}');+ }+ if (check_exception_free(ctx, js_array_pop(ctx, jsc->stack, 0, NULL, 0)))+ goto exception;+ JS_FreeValue(ctx, val);+ JS_FreeValue(ctx, tab);+ JS_FreeValue(ctx, sep);+ JS_FreeValue(ctx, sep1);+ JS_FreeValue(ctx, indent1);+ JS_FreeValue(ctx, prop);+ return 0;+ case JS_TAG_STRING:+ val = JS_ToQuotedStringFree(ctx, val);+ if (JS_IsException(val))+ goto exception;+ goto concat_value;+ case JS_TAG_FLOAT64:+ if (!isfinite(JS_VALUE_GET_FLOAT64(val))) {+ val = JS_NULL;+ }+ goto concat_value;+ case JS_TAG_INT:+#ifdef CONFIG_BIGNUM+ case JS_TAG_BIG_FLOAT:+#endif+ case JS_TAG_BOOL:+ case JS_TAG_NULL:+ concat_value:+ return string_buffer_concat_value_free(jsc->b, val);+#ifdef CONFIG_BIGNUM+ case JS_TAG_BIG_INT:+ JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify");+ goto exception;+#endif+ default:+ JS_FreeValue(ctx, val);+ return 0;+ }+ +exception:+ JS_FreeValue(ctx, val);+ JS_FreeValue(ctx, tab);+ JS_FreeValue(ctx, sep);+ JS_FreeValue(ctx, sep1);+ JS_FreeValue(ctx, indent1);+ JS_FreeValue(ctx, prop);+ return -1;+}++JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj,+ JSValueConst replacer, JSValueConst space0)+{+ StringBuffer b_s;+ JSONStringifyContext jsc_s, *jsc = &jsc_s;+ JSValue val, v, space, ret, wrapper;+ int res;+ int64_t i, j, n;++ jsc->replacer_func = JS_UNDEFINED;+ jsc->stack = JS_UNDEFINED;+ jsc->property_list = JS_UNDEFINED;+ jsc->gap = JS_UNDEFINED;+ jsc->b = &b_s;+ jsc->empty = JS_AtomToString(ctx, JS_ATOM_empty_string);+ ret = JS_UNDEFINED;+ wrapper = JS_UNDEFINED;++ string_buffer_init(ctx, jsc->b, 0);+ jsc->stack = JS_NewArray(ctx);+ if (JS_IsException(jsc->stack))+ goto exception;+ if (JS_IsFunction(ctx, replacer)) {+ jsc->replacer_func = replacer;+ } else {+ res = JS_IsArray(ctx, replacer);+ if (res < 0)+ goto exception;+ if (res) {+ /* XXX: enumeration is not fully correct */+ jsc->property_list = JS_NewArray(ctx);+ if (JS_IsException(jsc->property_list))+ goto exception;+ if (js_get_length64(ctx, &n, replacer))+ goto exception;+ for (i = j = 0; i < n; i++) {+ JSValue present;+ v = JS_GetPropertyInt64(ctx, replacer, i);+ if (JS_IsException(v))+ goto exception;+ if (JS_IsObject(v)) {+ JSObject *p = JS_VALUE_GET_OBJ(v);+ if (p->class_id == JS_CLASS_STRING ||+ p->class_id == JS_CLASS_NUMBER) {+ v = JS_ToStringFree(ctx, v);+ if (JS_IsException(v))+ goto exception;+ } else {+ JS_FreeValue(ctx, v);+ continue;+ }+ } else if (JS_IsNumber(v)) {+ v = JS_ToStringFree(ctx, v);+ if (JS_IsException(v))+ goto exception;+ } else if (!JS_IsString(v)) {+ JS_FreeValue(ctx, v);+ continue;+ }+ present = js_array_includes(ctx, jsc->property_list,+ 1, (JSValueConst *)&v);+ if (JS_IsException(present)) {+ JS_FreeValue(ctx, v);+ goto exception;+ }+ if (!JS_ToBoolFree(ctx, present)) {+ JS_SetPropertyInt64(ctx, jsc->property_list, j++, v);+ } else {+ JS_FreeValue(ctx, v);+ }+ }+ }+ }+ space = JS_DupValue(ctx, space0);+ if (JS_IsObject(space)) {+ JSObject *p = JS_VALUE_GET_OBJ(space);+ if (p->class_id == JS_CLASS_NUMBER) {+ space = JS_ToNumberFree(ctx, space);+ } else if (p->class_id == JS_CLASS_STRING) {+ space = JS_ToStringFree(ctx, space);+ }+ if (JS_IsException(space)) {+ JS_FreeValue(ctx, space);+ goto exception;+ }+ }+ if (JS_IsNumber(space)) {+ int n;+ if (JS_ToInt32Clamp(ctx, &n, space, 0, 10, 0))+ goto exception;+ jsc->gap = JS_NewStringLen(ctx, " ", n);+ } else if (JS_IsString(space)) {+ JSString *p = JS_VALUE_GET_STRING(space);+ jsc->gap = js_sub_string(ctx, p, 0, min_int(p->len, 10));+ } else {+ jsc->gap = JS_DupValue(ctx, jsc->empty);+ }+ JS_FreeValue(ctx, space);+ if (JS_IsException(jsc->gap))+ goto exception;+ wrapper = JS_NewObject(ctx);+ if (JS_IsException(wrapper))+ goto exception;+ if (JS_DefinePropertyValue(ctx, wrapper, JS_ATOM_empty_string,+ JS_DupValue(ctx, obj), JS_PROP_C_W_E) < 0)+ goto exception;+ val = JS_DupValue(ctx, obj);+ + val = js_json_check(ctx, jsc, wrapper, val, jsc->empty);+ if (JS_IsException(val))+ goto exception;+ if (JS_IsUndefined(val)) {+ ret = JS_UNDEFINED;+ goto done1;+ }+ if (js_json_to_str(ctx, jsc, wrapper, val, jsc->empty))+ goto exception;++ ret = string_buffer_end(jsc->b);+ goto done;++exception:+ ret = JS_EXCEPTION;+done1:+ string_buffer_free(jsc->b);+done:+ JS_FreeValue(ctx, wrapper);+ JS_FreeValue(ctx, jsc->empty);+ JS_FreeValue(ctx, jsc->gap);+ JS_FreeValue(ctx, jsc->property_list);+ JS_FreeValue(ctx, jsc->stack);+ return ret;+}++static JSValue js_json_stringify(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ // stringify(val, replacer, space)+ return JS_JSONStringify(ctx, argv[0], argv[1], argv[2]);+}++static const JSCFunctionListEntry js_json_funcs[] = {+ JS_CFUNC_DEF("parse", 2, js_json_parse ),+ JS_CFUNC_DEF("stringify", 3, js_json_stringify ),+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "JSON", JS_PROP_CONFIGURABLE ),+};++static const JSCFunctionListEntry js_json_obj[] = {+ JS_OBJECT_DEF("JSON", js_json_funcs, countof(js_json_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),+};++void JS_AddIntrinsicJSON(JSContext *ctx)+{+ /* add JSON as autoinit object */+ JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_json_obj, countof(js_json_obj));+}++/* Reflect */++static JSValue js_reflect_apply(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return js_function_apply(ctx, argv[0], max_int(0, argc - 1), argv + 1, 2);+}++static JSValue js_reflect_construct(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValueConst func, array_arg, new_target;+ JSValue *tab, ret;+ uint32_t len;++ func = argv[0];+ array_arg = argv[1];+ if (argc > 2) {+ new_target = argv[2];+ if (!JS_IsConstructor(ctx, new_target))+ return JS_ThrowTypeError(ctx, "not a constructor");+ } else {+ new_target = func;+ }+ tab = build_arg_list(ctx, &len, array_arg);+ if (!tab)+ return JS_EXCEPTION;+ ret = JS_CallConstructor2(ctx, func, new_target, len, (JSValueConst *)tab);+ free_arg_list(ctx, tab, len);+ return ret;+}++static JSValue js_reflect_deleteProperty(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValueConst obj;+ JSAtom atom;+ int ret;++ obj = argv[0];+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)+ return JS_ThrowTypeErrorNotAnObject(ctx);+ atom = JS_ValueToAtom(ctx, argv[1]);+ if (unlikely(atom == JS_ATOM_NULL))+ return JS_EXCEPTION;+ ret = JS_DeleteProperty(ctx, obj, atom, 0);+ JS_FreeAtom(ctx, atom);+ if (ret < 0)+ return JS_EXCEPTION;+ else+ return JS_NewBool(ctx, ret);+}++static JSValue js_reflect_get(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValueConst obj, prop, receiver;+ JSAtom atom;+ JSValue ret;++ obj = argv[0];+ prop = argv[1];+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)+ return JS_ThrowTypeErrorNotAnObject(ctx);+ if (argc > 2)+ receiver = argv[2];+ else+ receiver = obj;+ atom = JS_ValueToAtom(ctx, prop);+ if (unlikely(atom == JS_ATOM_NULL))+ return JS_EXCEPTION;+ ret = JS_GetPropertyInternal(ctx, obj, atom, receiver, FALSE);+ JS_FreeAtom(ctx, atom);+ return ret;+}++static JSValue js_reflect_has(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValueConst obj, prop;+ JSAtom atom;+ int ret;++ obj = argv[0];+ prop = argv[1];+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)+ return JS_ThrowTypeErrorNotAnObject(ctx);+ atom = JS_ValueToAtom(ctx, prop);+ if (unlikely(atom == JS_ATOM_NULL))+ return JS_EXCEPTION;+ ret = JS_HasProperty(ctx, obj, atom);+ JS_FreeAtom(ctx, atom);+ if (ret < 0)+ return JS_EXCEPTION;+ else+ return JS_NewBool(ctx, ret);+}++static JSValue js_reflect_set(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValueConst obj, prop, val, receiver;+ int ret;+ JSAtom atom;++ obj = argv[0];+ prop = argv[1];+ val = argv[2];+ if (argc > 3)+ receiver = argv[3];+ else+ receiver = obj;+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)+ return JS_ThrowTypeErrorNotAnObject(ctx);+ atom = JS_ValueToAtom(ctx, prop);+ if (unlikely(atom == JS_ATOM_NULL))+ return JS_EXCEPTION;+ ret = JS_SetPropertyGeneric(ctx, JS_VALUE_GET_OBJ(obj), atom,+ JS_DupValue(ctx, val), receiver, 0);+ JS_FreeAtom(ctx, atom);+ if (ret < 0)+ return JS_EXCEPTION;+ else+ return JS_NewBool(ctx, ret);+}++static JSValue js_reflect_setPrototypeOf(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ int ret;+ ret = JS_SetPrototypeInternal(ctx, argv[0], argv[1], FALSE);+ if (ret < 0)+ return JS_EXCEPTION;+ else+ return JS_NewBool(ctx, ret);+}++static JSValue js_reflect_ownKeys(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT)+ return JS_ThrowTypeErrorNotAnObject(ctx);+ return JS_GetOwnPropertyNames2(ctx, argv[0],+ JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK,+ JS_ITERATOR_KIND_KEY);+}++static const JSCFunctionListEntry js_reflect_funcs[] = {+ JS_CFUNC_DEF("apply", 3, js_reflect_apply ),+ JS_CFUNC_DEF("construct", 2, js_reflect_construct ),+ JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 1 ),+ JS_CFUNC_DEF("deleteProperty", 2, js_reflect_deleteProperty ),+ JS_CFUNC_DEF("get", 2, js_reflect_get ),+ JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 1 ),+ JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 1 ),+ JS_CFUNC_DEF("has", 2, js_reflect_has ),+ JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 1 ),+ JS_CFUNC_DEF("ownKeys", 1, js_reflect_ownKeys ),+ JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 1 ),+ JS_CFUNC_DEF("set", 3, js_reflect_set ),+ JS_CFUNC_DEF("setPrototypeOf", 2, js_reflect_setPrototypeOf ),+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Reflect", JS_PROP_CONFIGURABLE ),+};++static const JSCFunctionListEntry js_reflect_obj[] = {+ JS_OBJECT_DEF("Reflect", js_reflect_funcs, countof(js_reflect_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),+};++/* Proxy */++static void js_proxy_finalizer(JSRuntime *rt, JSValue val)+{+ JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY);+ if (s) {+ JS_FreeValueRT(rt, s->target);+ JS_FreeValueRT(rt, s->handler);+ js_free_rt(rt, s);+ }+}++static void js_proxy_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func)+{+ JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY);+ if (s) {+ JS_MarkValue(rt, s->target, mark_func);+ JS_MarkValue(rt, s->handler, mark_func);+ }+}++static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx)+{+ return JS_ThrowTypeError(ctx, "revoked proxy");+}++static JSProxyData *get_proxy_method(JSContext *ctx, JSValue *pmethod,+ JSValueConst obj, JSAtom name)+{+ JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY);+ JSValue method;++ /* safer to test recursion in all proxy methods */+ if (js_check_stack_overflow(ctx->rt, 0)) {+ JS_ThrowStackOverflow(ctx);+ return NULL;+ }+ + /* 's' should never be NULL */+ if (s->is_revoked) {+ JS_ThrowTypeErrorRevokedProxy(ctx);+ return NULL;+ }+ method = JS_GetProperty(ctx, s->handler, name);+ if (JS_IsException(method))+ return NULL;+ if (JS_IsNull(method))+ method = JS_UNDEFINED;+ *pmethod = method;+ return s;+}++static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj)+{+ JSProxyData *s;+ JSValue method, ret, proto1;+ int res;++ s = get_proxy_method(ctx, &method, obj, JS_ATOM_getPrototypeOf);+ if (!s)+ return JS_EXCEPTION;+ if (JS_IsUndefined(method))+ return JS_GetPrototype(ctx, s->target);+ ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);+ if (JS_IsException(ret))+ return ret;+ if (JS_VALUE_GET_TAG(ret) != JS_TAG_NULL &&+ JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {+ goto fail;+ }+ res = JS_IsExtensible(ctx, s->target);+ if (res < 0) {+ JS_FreeValue(ctx, ret);+ return JS_EXCEPTION;+ }+ if (!res) {+ /* check invariant */+ proto1 = JS_GetPrototype(ctx, s->target);+ if (JS_IsException(proto1)) {+ JS_FreeValue(ctx, ret);+ return JS_EXCEPTION;+ }+ if (JS_VALUE_GET_OBJ(proto1) != JS_VALUE_GET_OBJ(ret)) {+ JS_FreeValue(ctx, proto1);+ fail:+ JS_FreeValue(ctx, ret);+ return JS_ThrowTypeError(ctx, "proxy: inconsistent prototype");+ }+ JS_FreeValue(ctx, proto1);+ }+ return ret;+}++static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,+ JSValueConst proto_val, BOOL throw_flag)+{+ JSProxyData *s;+ JSValue method, ret, proto1;+ JSValueConst args[2];+ BOOL res;+ int res2;++ s = get_proxy_method(ctx, &method, obj, JS_ATOM_setPrototypeOf);+ if (!s)+ return -1;+ if (JS_IsUndefined(method))+ return JS_SetPrototypeInternal(ctx, s->target, proto_val, throw_flag);+ args[0] = s->target;+ args[1] = proto_val;+ ret = JS_CallFree(ctx, method, s->handler, 2, args);+ if (JS_IsException(ret))+ return -1;+ res = JS_ToBoolFree(ctx, ret);+ if (!res) {+ if (throw_flag) {+ JS_ThrowTypeError(ctx, "proxy: bad prototype");+ return -1;+ } else {+ return FALSE;+ }+ }+ res2 = JS_IsExtensible(ctx, s->target);+ if (res2 < 0)+ return -1;+ if (!res2) {+ proto1 = JS_GetPrototype(ctx, s->target);+ if (JS_IsException(proto1))+ return -1;+ if (JS_VALUE_GET_OBJ(proto_val) != JS_VALUE_GET_OBJ(proto1)) {+ JS_FreeValue(ctx, proto1);+ JS_ThrowTypeError(ctx, "proxy: inconsistent prototype");+ return -1;+ }+ JS_FreeValue(ctx, proto1);+ }+ return TRUE;+}++static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj)+{+ JSProxyData *s;+ JSValue method, ret;+ BOOL res;+ int res2;++ s = get_proxy_method(ctx, &method, obj, JS_ATOM_isExtensible);+ if (!s)+ return -1;+ if (JS_IsUndefined(method))+ return JS_IsExtensible(ctx, s->target);+ ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);+ if (JS_IsException(ret))+ return -1;+ res = JS_ToBoolFree(ctx, ret);+ res2 = JS_IsExtensible(ctx, s->target);+ if (res2 < 0)+ return res2;+ if (res != res2) {+ JS_ThrowTypeError(ctx, "proxy: inconsistent isExtensible");+ return -1;+ }+ return res;+}++static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj)+{+ JSProxyData *s;+ JSValue method, ret;+ BOOL res;+ int res2;++ s = get_proxy_method(ctx, &method, obj, JS_ATOM_preventExtensions);+ if (!s)+ return -1;+ if (JS_IsUndefined(method))+ return JS_PreventExtensions(ctx, s->target);+ ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);+ if (JS_IsException(ret))+ return -1;+ res = JS_ToBoolFree(ctx, ret);+ if (res) {+ res2 = JS_IsExtensible(ctx, s->target);+ if (res2 < 0)+ return res2;+ if (res2) {+ JS_ThrowTypeError(ctx, "proxy: inconsistent preventExtensions");+ return -1;+ }+ }+ return res;+}++static int js_proxy_has(JSContext *ctx, JSValueConst obj, JSAtom atom)+{+ JSProxyData *s;+ JSValue method, ret1, atom_val;+ int ret, res;+ JSObject *p;+ JSValueConst args[2];+ BOOL res2;++ s = get_proxy_method(ctx, &method, obj, JS_ATOM_has);+ if (!s)+ return -1;+ if (JS_IsUndefined(method))+ return JS_HasProperty(ctx, s->target, atom);+ atom_val = JS_AtomToValue(ctx, atom);+ if (JS_IsException(atom_val)) {+ JS_FreeValue(ctx, method);+ return -1;+ }+ args[0] = s->target;+ args[1] = atom_val;+ ret1 = JS_CallFree(ctx, method, s->handler, 2, args);+ JS_FreeValue(ctx, atom_val);+ if (JS_IsException(ret1))+ return -1;+ ret = JS_ToBoolFree(ctx, ret1);+ if (!ret) {+ JSPropertyDescriptor desc;+ p = JS_VALUE_GET_OBJ(s->target);+ res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);+ if (res < 0)+ return -1;+ if (res) {+ res2 = !(desc.flags & JS_PROP_CONFIGURABLE);+ js_free_desc(ctx, &desc);+ if (res2 || !p->extensible) {+ JS_ThrowTypeError(ctx, "proxy: inconsistent has");+ return -1;+ }+ }+ }+ return ret;+}++static JSValue js_proxy_get(JSContext *ctx, JSValueConst obj, JSAtom atom,+ JSValueConst receiver)+{+ JSProxyData *s;+ JSValue method, ret, atom_val;+ int res;+ JSValueConst args[3];+ JSPropertyDescriptor desc;++ s = get_proxy_method(ctx, &method, obj, JS_ATOM_get);+ if (!s)+ return JS_EXCEPTION;+ /* Note: recursion is possible thru the prototype of s->target */+ if (JS_IsUndefined(method))+ return JS_GetPropertyInternal(ctx, s->target, atom, receiver, FALSE);+ atom_val = JS_AtomToValue(ctx, atom);+ if (JS_IsException(atom_val)) {+ JS_FreeValue(ctx, method);+ return JS_EXCEPTION;+ }+ args[0] = s->target;+ args[1] = atom_val;+ args[2] = receiver;+ ret = JS_CallFree(ctx, method, s->handler, 3, args);+ JS_FreeValue(ctx, atom_val);+ if (JS_IsException(ret))+ return JS_EXCEPTION;+ res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);+ if (res < 0)+ return JS_EXCEPTION;+ if (res) {+ if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) {+ if (!js_same_value(ctx, desc.value, ret)) {+ goto fail;+ }+ } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET) {+ if (JS_IsUndefined(desc.getter) && !JS_IsUndefined(ret)) {+ fail:+ js_free_desc(ctx, &desc);+ JS_FreeValue(ctx, ret);+ return JS_ThrowTypeError(ctx, "proxy: inconsistent get");+ }+ }+ js_free_desc(ctx, &desc);+ }+ return ret;+}++static int js_proxy_set(JSContext *ctx, JSValueConst obj, JSAtom atom,+ JSValueConst value, JSValueConst receiver, int flags)+{+ JSProxyData *s;+ JSValue method, ret1, atom_val;+ int ret, res;+ JSValueConst args[4];++ s = get_proxy_method(ctx, &method, obj, JS_ATOM_set);+ if (!s)+ return -1;+ if (JS_IsUndefined(method)) {+ return JS_SetPropertyGeneric(ctx, JS_VALUE_GET_OBJ(s->target), atom,+ JS_DupValue(ctx, value), receiver,+ flags);+ }+ atom_val = JS_AtomToValue(ctx, atom);+ if (JS_IsException(atom_val)) {+ JS_FreeValue(ctx, method);+ return -1;+ }+ args[0] = s->target;+ args[1] = atom_val;+ args[2] = value;+ args[3] = receiver;+ ret1 = JS_CallFree(ctx, method, s->handler, 4, args);+ JS_FreeValue(ctx, atom_val);+ if (JS_IsException(ret1))+ return -1;+ ret = JS_ToBoolFree(ctx, ret1);+ if (ret) {+ JSPropertyDescriptor desc;+ res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);+ if (res < 0)+ return -1;+ if (res) {+ if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) {+ if (!js_same_value(ctx, desc.value, value)) {+ goto fail;+ }+ } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET && JS_IsUndefined(desc.setter)) {+ fail:+ js_free_desc(ctx, &desc);+ JS_ThrowTypeError(ctx, "proxy: inconsistent set");+ return -1;+ }+ js_free_desc(ctx, &desc);+ }+ } else {+ if ((flags & JS_PROP_THROW) ||+ ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {+ JS_ThrowTypeError(ctx, "proxy: cannot set property");+ return -1;+ }+ }+ return ret;+}++static JSValue js_create_desc(JSContext *ctx, JSValueConst val,+ JSValueConst getter, JSValueConst setter,+ int flags)+{+ JSValue ret;+ ret = JS_NewObject(ctx);+ if (JS_IsException(ret))+ return ret;+ if (flags & JS_PROP_HAS_GET) {+ JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, getter),+ JS_PROP_C_W_E);+ }+ if (flags & JS_PROP_HAS_SET) {+ JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, setter),+ JS_PROP_C_W_E);+ }+ if (flags & JS_PROP_HAS_VALUE) {+ JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, val),+ JS_PROP_C_W_E);+ }+ if (flags & JS_PROP_HAS_WRITABLE) {+ JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable,+ JS_NewBool(ctx, (flags & JS_PROP_WRITABLE) != 0),+ JS_PROP_C_W_E);+ }+ if (flags & JS_PROP_HAS_ENUMERABLE) {+ JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable,+ JS_NewBool(ctx, (flags & JS_PROP_ENUMERABLE) != 0),+ JS_PROP_C_W_E);+ }+ if (flags & JS_PROP_HAS_CONFIGURABLE) {+ JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable,+ JS_NewBool(ctx, (flags & JS_PROP_CONFIGURABLE) != 0),+ JS_PROP_C_W_E);+ }+ return ret;+}++static int js_proxy_get_own_property(JSContext *ctx, JSPropertyDescriptor *pdesc,+ JSValueConst obj, JSAtom prop)+{+ JSProxyData *s;+ JSValue method, trap_result_obj, prop_val;+ int res, target_desc_ret, ret;+ JSObject *p;+ JSValueConst args[2];+ JSPropertyDescriptor result_desc, target_desc;++ s = get_proxy_method(ctx, &method, obj, JS_ATOM_getOwnPropertyDescriptor);+ if (!s)+ return -1;+ p = JS_VALUE_GET_OBJ(s->target);+ if (JS_IsUndefined(method)) {+ return JS_GetOwnPropertyInternal(ctx, pdesc, p, prop);+ }+ prop_val = JS_AtomToValue(ctx, prop);+ if (JS_IsException(prop_val)) {+ JS_FreeValue(ctx, method);+ return -1;+ }+ args[0] = s->target;+ args[1] = prop_val;+ trap_result_obj = JS_CallFree(ctx, method, s->handler, 2, args);+ JS_FreeValue(ctx, prop_val);+ if (JS_IsException(trap_result_obj))+ return -1;+ if (!JS_IsObject(trap_result_obj) && !JS_IsUndefined(trap_result_obj)) {+ JS_FreeValue(ctx, trap_result_obj);+ goto fail;+ }+ target_desc_ret = JS_GetOwnPropertyInternal(ctx, &target_desc, p, prop);+ if (target_desc_ret < 0) {+ JS_FreeValue(ctx, trap_result_obj);+ return -1;+ }+ if (target_desc_ret)+ js_free_desc(ctx, &target_desc);+ if (JS_IsUndefined(trap_result_obj)) {+ if (target_desc_ret) {+ if (!(target_desc.flags & JS_PROP_CONFIGURABLE) || !p->extensible)+ goto fail;+ }+ ret = FALSE;+ } else {+ int flags1, extensible_target;+ extensible_target = JS_IsExtensible(ctx, s->target);+ if (extensible_target < 0) {+ JS_FreeValue(ctx, trap_result_obj);+ return -1;+ }+ res = js_obj_to_desc(ctx, &result_desc, trap_result_obj);+ JS_FreeValue(ctx, trap_result_obj);+ if (res < 0)+ return -1;+ + if (target_desc_ret) {+ /* convert result_desc.flags to defineProperty flags */+ flags1 = result_desc.flags | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE;+ if (result_desc.flags & JS_PROP_GETSET)+ flags1 |= JS_PROP_HAS_GET | JS_PROP_HAS_SET;+ else+ flags1 |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE;+ /* XXX: not complete check: need to compare value &+ getter/setter as in defineproperty */+ if (!check_define_prop_flags(target_desc.flags, flags1))+ goto fail1;+ } else {+ if (!extensible_target)+ goto fail1;+ }+ if (!(result_desc.flags & JS_PROP_CONFIGURABLE)) {+ if (!target_desc_ret || (target_desc.flags & JS_PROP_CONFIGURABLE))+ goto fail1;+ if ((result_desc.flags &+ (JS_PROP_GETSET | JS_PROP_WRITABLE)) == 0 &&+ target_desc_ret &&+ (target_desc.flags & JS_PROP_WRITABLE) != 0) {+ /* proxy-missing-checks */+ fail1:+ js_free_desc(ctx, &result_desc);+ fail:+ JS_ThrowTypeError(ctx, "proxy: inconsistent getOwnPropertyDescriptor");+ return -1;+ }+ }+ ret = TRUE;+ if (pdesc) {+ *pdesc = result_desc;+ } else {+ js_free_desc(ctx, &result_desc);+ }+ }+ return ret;+}++static int js_proxy_define_own_property(JSContext *ctx, JSValueConst obj,+ JSAtom prop, JSValueConst val,+ JSValueConst getter, JSValueConst setter,+ int flags)+{+ JSProxyData *s;+ JSValue method, ret1, prop_val, desc_val;+ int res, ret;+ JSObject *p;+ JSValueConst args[3];+ JSPropertyDescriptor desc;+ BOOL setting_not_configurable;++ s = get_proxy_method(ctx, &method, obj, JS_ATOM_defineProperty);+ if (!s)+ return -1;+ if (JS_IsUndefined(method)) {+ return JS_DefineProperty(ctx, s->target, prop, val, getter, setter, flags);+ }+ prop_val = JS_AtomToValue(ctx, prop);+ if (JS_IsException(prop_val)) {+ JS_FreeValue(ctx, method);+ return -1;+ }+ desc_val = js_create_desc(ctx, val, getter, setter, flags);+ if (JS_IsException(desc_val)) {+ JS_FreeValue(ctx, prop_val);+ JS_FreeValue(ctx, method);+ return -1;+ }+ args[0] = s->target;+ args[1] = prop_val;+ args[2] = desc_val;+ ret1 = JS_CallFree(ctx, method, s->handler, 3, args);+ JS_FreeValue(ctx, prop_val);+ JS_FreeValue(ctx, desc_val);+ if (JS_IsException(ret1))+ return -1;+ ret = JS_ToBoolFree(ctx, ret1);+ if (!ret) {+ if (flags & JS_PROP_THROW) {+ JS_ThrowTypeError(ctx, "proxy: defineProperty exception");+ return -1;+ } else {+ return 0;+ }+ }+ p = JS_VALUE_GET_OBJ(s->target);+ res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);+ if (res < 0)+ return -1;+ setting_not_configurable = ((flags & (JS_PROP_HAS_CONFIGURABLE |+ JS_PROP_CONFIGURABLE)) ==+ JS_PROP_HAS_CONFIGURABLE);+ if (!res) {+ if (!p->extensible || setting_not_configurable)+ goto fail;+ } else {+ if (!check_define_prop_flags(desc.flags, flags) ||+ ((desc.flags & JS_PROP_CONFIGURABLE) && setting_not_configurable)) {+ goto fail1;+ }+ if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {+ if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) ==+ JS_PROP_GETSET) {+ if ((flags & JS_PROP_HAS_GET) &&+ !js_same_value(ctx, getter, desc.getter)) {+ goto fail1;+ }+ if ((flags & JS_PROP_HAS_SET) &&+ !js_same_value(ctx, setter, desc.setter)) {+ goto fail1;+ }+ }+ } else if (flags & JS_PROP_HAS_VALUE) {+ if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) ==+ JS_PROP_WRITABLE && !(flags & JS_PROP_WRITABLE)) {+ /* missing-proxy-check feature */+ goto fail1;+ } else if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 &&+ !js_same_value(ctx, val, desc.value)) {+ goto fail1;+ }+ }+ if (flags & JS_PROP_HAS_WRITABLE) {+ if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE |+ JS_PROP_WRITABLE)) == JS_PROP_WRITABLE) {+ /* proxy-missing-checks */+ fail1:+ js_free_desc(ctx, &desc);+ fail:+ JS_ThrowTypeError(ctx, "proxy: inconsistent defineProperty");+ return -1;+ }+ }+ js_free_desc(ctx, &desc);+ }+ return 1;+}++static int js_proxy_delete_property(JSContext *ctx, JSValueConst obj,+ JSAtom atom)+{+ JSProxyData *s;+ JSValue method, ret, atom_val;+ int res, res2, is_extensible;+ JSValueConst args[2];++ s = get_proxy_method(ctx, &method, obj, JS_ATOM_deleteProperty);+ if (!s)+ return -1;+ if (JS_IsUndefined(method)) {+ return JS_DeleteProperty(ctx, s->target, atom, 0);+ }+ atom_val = JS_AtomToValue(ctx, atom);;+ if (JS_IsException(atom_val)) {+ JS_FreeValue(ctx, method);+ return -1;+ }+ args[0] = s->target;+ args[1] = atom_val;+ ret = JS_CallFree(ctx, method, s->handler, 2, args);+ JS_FreeValue(ctx, atom_val);+ if (JS_IsException(ret))+ return -1;+ res = JS_ToBoolFree(ctx, ret);+ if (res) {+ JSPropertyDescriptor desc;+ res2 = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);+ if (res2 < 0)+ return -1;+ if (res2) {+ if (!(desc.flags & JS_PROP_CONFIGURABLE))+ goto fail;+ is_extensible = JS_IsExtensible(ctx, s->target);+ if (is_extensible < 0)+ goto fail1;+ if (!is_extensible) {+ /* proxy-missing-checks */+ fail:+ JS_ThrowTypeError(ctx, "proxy: inconsistent deleteProperty");+ fail1:+ js_free_desc(ctx, &desc);+ return -1;+ }+ js_free_desc(ctx, &desc);+ }+ }+ return res;+}++/* return the index of the property or -1 if not found */+static int find_prop_key(const JSPropertyEnum *tab, int n, JSAtom atom)+{+ int i;+ for(i = 0; i < n; i++) {+ if (tab[i].atom == atom)+ return i;+ }+ return -1;+}++static int js_proxy_get_own_property_names(JSContext *ctx,+ JSPropertyEnum **ptab,+ uint32_t *plen,+ JSValueConst obj)+{+ JSProxyData *s;+ JSValue method, prop_array, val;+ uint32_t len, i, len2;+ JSPropertyEnum *tab, *tab2;+ JSAtom atom;+ JSPropertyDescriptor desc;+ int res, is_extensible, idx;++ s = get_proxy_method(ctx, &method, obj, JS_ATOM_ownKeys);+ if (!s)+ return -1;+ if (JS_IsUndefined(method)) {+ return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen,+ JS_VALUE_GET_OBJ(s->target),+ JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK);+ }+ prop_array = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);+ if (JS_IsException(prop_array))+ return -1;+ tab = NULL;+ len = 0;+ tab2 = NULL;+ len2 = 0;+ if (js_get_length32(ctx, &len, prop_array))+ goto fail;+ if (len > 0) {+ tab = js_mallocz(ctx, sizeof(tab[0]) * len);+ if (!tab)+ goto fail;+ }+ for(i = 0; i < len; i++) {+ val = JS_GetPropertyUint32(ctx, prop_array, i);+ if (JS_IsException(val))+ goto fail;+ if (!JS_IsString(val) && !JS_IsSymbol(val)) {+ JS_FreeValue(ctx, val);+ JS_ThrowTypeError(ctx, "proxy: properties must be strings or symbols");+ goto fail;+ }+ atom = JS_ValueToAtom(ctx, val);+ JS_FreeValue(ctx, val);+ if (atom == JS_ATOM_NULL)+ goto fail;+ tab[i].atom = atom;+ tab[i].is_enumerable = FALSE; /* XXX: redundant? */+ }++ /* check duplicate properties (XXX: inefficient, could store the+ * properties an a temporary object to use the hash) */+ for(i = 1; i < len; i++) {+ if (find_prop_key(tab, i, tab[i].atom) >= 0) {+ JS_ThrowTypeError(ctx, "proxy: duplicate property");+ goto fail;+ }+ }++ is_extensible = JS_IsExtensible(ctx, s->target);+ if (is_extensible < 0)+ goto fail;++ /* check if there are non configurable properties */+ if (s->is_revoked) {+ JS_ThrowTypeErrorRevokedProxy(ctx);+ goto fail;+ }+ if (JS_GetOwnPropertyNamesInternal(ctx, &tab2, &len2, JS_VALUE_GET_OBJ(s->target),+ JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK))+ goto fail;+ for(i = 0; i < len2; i++) {+ if (s->is_revoked) {+ JS_ThrowTypeErrorRevokedProxy(ctx);+ goto fail;+ }+ res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target),+ tab2[i].atom);+ if (res < 0)+ goto fail;+ if (res) { /* safety, property should be found */+ js_free_desc(ctx, &desc);+ if (!(desc.flags & JS_PROP_CONFIGURABLE) || !is_extensible) {+ idx = find_prop_key(tab, len, tab2[i].atom);+ if (idx < 0) {+ JS_ThrowTypeError(ctx, "proxy: target property must be present in proxy ownKeys");+ goto fail;+ }+ /* mark the property as found */+ if (!is_extensible)+ tab[idx].is_enumerable = TRUE;+ }+ }+ }+ if (!is_extensible) {+ /* check that all property in 'tab' were checked */+ for(i = 0; i < len; i++) {+ if (!tab[i].is_enumerable) {+ JS_ThrowTypeError(ctx, "proxy: property not present in target were returned by non extensible proxy");+ goto fail;+ }+ }+ }++ js_free_prop_enum(ctx, tab2, len2);+ JS_FreeValue(ctx, prop_array);+ *ptab = tab;+ *plen = len;+ return 0;+ fail:+ js_free_prop_enum(ctx, tab2, len2);+ js_free_prop_enum(ctx, tab, len);+ JS_FreeValue(ctx, prop_array);+ return -1;+}++static JSValue js_proxy_call_constructor(JSContext *ctx, JSValueConst func_obj,+ JSValueConst new_target,+ int argc, JSValueConst *argv)+{+ JSProxyData *s;+ JSValue method, arg_array, ret;+ JSValueConst args[3];++ s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_construct);+ if (!s)+ return JS_EXCEPTION;+ if (!JS_IsConstructor(ctx, s->target))+ return JS_ThrowTypeError(ctx, "not a constructor");+ if (JS_IsUndefined(method))+ return JS_CallConstructor2(ctx, s->target, new_target, argc, argv);+ arg_array = js_create_array(ctx, argc, argv);+ if (JS_IsException(arg_array)) {+ ret = JS_EXCEPTION;+ goto fail;+ }+ args[0] = s->target;+ args[1] = arg_array;+ args[2] = new_target;+ ret = JS_Call(ctx, method, s->handler, 3, args);+ if (!JS_IsException(ret) && JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {+ JS_FreeValue(ctx, ret);+ ret = JS_ThrowTypeErrorNotAnObject(ctx);+ }+ fail:+ JS_FreeValue(ctx, method);+ JS_FreeValue(ctx, arg_array);+ return ret;+}++static JSValue js_proxy_call(JSContext *ctx, JSValueConst func_obj,+ JSValueConst this_obj,+ int argc, JSValueConst *argv, int flags)+{+ JSProxyData *s;+ JSValue method, arg_array, ret;+ JSValueConst args[3];++ if (flags & JS_CALL_FLAG_CONSTRUCTOR)+ return js_proxy_call_constructor(ctx, func_obj, this_obj, argc, argv);+ + s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_apply);+ if (!s)+ return JS_EXCEPTION;+ if (!s->is_func) {+ JS_FreeValue(ctx, method);+ return JS_ThrowTypeError(ctx, "not a function");+ }+ if (JS_IsUndefined(method))+ return JS_Call(ctx, s->target, this_obj, argc, argv);+ arg_array = js_create_array(ctx, argc, argv);+ if (JS_IsException(arg_array)) {+ ret = JS_EXCEPTION;+ goto fail;+ }+ args[0] = s->target;+ args[1] = this_obj;+ args[2] = arg_array;+ ret = JS_Call(ctx, method, s->handler, 3, args);+ fail:+ JS_FreeValue(ctx, method);+ JS_FreeValue(ctx, arg_array);+ return ret;+}++static int js_proxy_isArray(JSContext *ctx, JSValueConst obj)+{+ JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY);+ if (!s)+ return FALSE;+ if (s->is_revoked) {+ JS_ThrowTypeErrorRevokedProxy(ctx);+ return -1;+ }+ return JS_IsArray(ctx, s->target);+}++static const JSClassExoticMethods js_proxy_exotic_methods = {+ .get_own_property = js_proxy_get_own_property,+ .define_own_property = js_proxy_define_own_property,+ .delete_property = js_proxy_delete_property,+ .get_own_property_names = js_proxy_get_own_property_names,+ .has_property = js_proxy_has,+ .get_property = js_proxy_get,+ .set_property = js_proxy_set,+};++static JSValue js_proxy_constructor(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValueConst target, handler;+ JSValue obj;+ JSProxyData *s;++ target = argv[0];+ handler = argv[1];+ if (JS_VALUE_GET_TAG(target) != JS_TAG_OBJECT ||+ JS_VALUE_GET_TAG(handler) != JS_TAG_OBJECT)+ return JS_ThrowTypeErrorNotAnObject(ctx);++ obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_PROXY);+ if (JS_IsException(obj))+ return obj;+ s = js_malloc(ctx, sizeof(JSProxyData));+ if (!s) {+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+ }+ s->target = JS_DupValue(ctx, target);+ s->handler = JS_DupValue(ctx, handler);+ s->is_func = JS_IsFunction(ctx, target);+ s->is_revoked = FALSE;+ JS_SetOpaque(obj, s);+ JS_SetConstructorBit(ctx, obj, JS_IsConstructor(ctx, target));+ return obj;+}++static JSValue js_proxy_revoke(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic,+ JSValue *func_data)+{+ JSProxyData *s = JS_GetOpaque(func_data[0], JS_CLASS_PROXY);+ if (s) {+ /* We do not free the handler and target in case they are+ referenced as constants in the C call stack */+ s->is_revoked = TRUE;+ JS_FreeValue(ctx, func_data[0]);+ func_data[0] = JS_NULL;+ }+ return JS_UNDEFINED;+}++static JSValue js_proxy_revoke_constructor(JSContext *ctx,+ JSValueConst proxy_obj)+{+ return JS_NewCFunctionData(ctx, js_proxy_revoke, 0, 0, 1, &proxy_obj);+}++static JSValue js_proxy_revocable(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue proxy_obj, revoke_obj = JS_UNDEFINED, obj;++ proxy_obj = js_proxy_constructor(ctx, JS_UNDEFINED, argc, argv);+ if (JS_IsException(proxy_obj))+ goto fail;+ revoke_obj = js_proxy_revoke_constructor(ctx, proxy_obj);+ if (JS_IsException(revoke_obj))+ goto fail;+ obj = JS_NewObject(ctx);+ if (JS_IsException(obj))+ goto fail;+ // XXX: exceptions?+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_proxy, proxy_obj, JS_PROP_C_W_E);+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_revoke, revoke_obj, JS_PROP_C_W_E);+ return obj;+ fail:+ JS_FreeValue(ctx, proxy_obj);+ JS_FreeValue(ctx, revoke_obj);+ return JS_EXCEPTION;+}++static const JSCFunctionListEntry js_proxy_funcs[] = {+ JS_CFUNC_DEF("revocable", 2, js_proxy_revocable ),+};++static const JSClassShortDef js_proxy_class_def[] = {+ { JS_ATOM_Object, js_proxy_finalizer, js_proxy_mark }, /* JS_CLASS_PROXY */+};++void JS_AddIntrinsicProxy(JSContext *ctx)+{+ JSRuntime *rt = ctx->rt;+ JSValue obj1;++ if (!JS_IsRegisteredClass(rt, JS_CLASS_PROXY)) {+ init_class_range(rt, js_proxy_class_def, JS_CLASS_PROXY,+ countof(js_proxy_class_def));+ rt->class_array[JS_CLASS_PROXY].exotic = &js_proxy_exotic_methods;+ rt->class_array[JS_CLASS_PROXY].call = js_proxy_call;+ }++ obj1 = JS_NewCFunction2(ctx, js_proxy_constructor, "Proxy", 2,+ JS_CFUNC_constructor, 0);+ JS_SetConstructorBit(ctx, obj1, TRUE);+ JS_SetPropertyFunctionList(ctx, obj1, js_proxy_funcs,+ countof(js_proxy_funcs));+ JS_DefinePropertyValueStr(ctx, ctx->global_obj, "Proxy",+ obj1, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+}++/* Symbol */++static JSValue js_symbol_constructor(JSContext *ctx, JSValueConst new_target,+ int argc, JSValueConst *argv)+{+ JSValue str;+ JSString *p;++ if (!JS_IsUndefined(new_target))+ return JS_ThrowTypeError(ctx, "not a constructor");+ if (argc == 0 || JS_IsUndefined(argv[0])) {+ p = NULL;+ } else {+ str = JS_ToString(ctx, argv[0]);+ if (JS_IsException(str))+ return JS_EXCEPTION;+ p = JS_VALUE_GET_STRING(str);+ }+ return JS_NewSymbol(ctx, p, JS_ATOM_TYPE_SYMBOL);+}++static JSValue js_thisSymbolValue(JSContext *ctx, JSValueConst this_val)+{+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_SYMBOL)+ return JS_DupValue(ctx, this_val);++ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {+ JSObject *p = JS_VALUE_GET_OBJ(this_val);+ if (p->class_id == JS_CLASS_SYMBOL) {+ if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_SYMBOL)+ return JS_DupValue(ctx, p->u.object_data);+ }+ }+ return JS_ThrowTypeError(ctx, "not a symbol");+}++static JSValue js_symbol_toString(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue val, ret;+ val = js_thisSymbolValue(ctx, this_val);+ if (JS_IsException(val))+ return val;+ /* XXX: use JS_ToStringInternal() with a flags */+ ret = js_string_constructor(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val);+ JS_FreeValue(ctx, val);+ return ret;+}++static JSValue js_symbol_valueOf(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return js_thisSymbolValue(ctx, this_val);+}++static JSValue js_symbol_get_description(JSContext *ctx, JSValueConst this_val)+{+ JSValue val, ret;+ JSAtomStruct *p;++ val = js_thisSymbolValue(ctx, this_val);+ if (JS_IsException(val))+ return val;+ p = JS_VALUE_GET_PTR(val);+ if (p->len == 0 && p->is_wide_char != 0) {+ ret = JS_UNDEFINED;+ } else {+ ret = JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p));+ }+ JS_FreeValue(ctx, val);+ return ret;+}++static const JSCFunctionListEntry js_symbol_proto_funcs[] = {+ JS_CFUNC_DEF("toString", 0, js_symbol_toString ),+ JS_CFUNC_DEF("valueOf", 0, js_symbol_valueOf ),+ // XXX: should have writable: false+ JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_symbol_valueOf ),+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Symbol", JS_PROP_CONFIGURABLE ),+ JS_CGETSET_DEF("description", js_symbol_get_description, NULL ),+};++static JSValue js_symbol_for(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue str;++ str = JS_ToString(ctx, argv[0]);+ if (JS_IsException(str))+ return JS_EXCEPTION;+ return JS_NewSymbol(ctx, JS_VALUE_GET_STRING(str), JS_ATOM_TYPE_GLOBAL_SYMBOL);+}++static JSValue js_symbol_keyFor(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSAtomStruct *p;++ if (!JS_IsSymbol(argv[0]))+ return JS_ThrowTypeError(ctx, "not a symbol");+ p = JS_VALUE_GET_PTR(argv[0]);+ if (p->atom_type != JS_ATOM_TYPE_GLOBAL_SYMBOL)+ return JS_UNDEFINED;+ return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));+}++static const JSCFunctionListEntry js_symbol_funcs[] = {+ JS_CFUNC_DEF("for", 1, js_symbol_for ),+ JS_CFUNC_DEF("keyFor", 1, js_symbol_keyFor ),+};++/* Set/Map/WeakSet/WeakMap */++typedef struct JSMapRecord {+ int ref_count; /* used during enumeration to avoid freeing the record */+ BOOL empty; /* TRUE if the record is deleted */+ struct JSMapState *map;+ struct JSMapRecord *next_weak_ref;+ struct list_head link;+ struct list_head hash_link;+ JSValue key;+ JSValue value;+} JSMapRecord;++typedef struct JSMapState {+ BOOL is_weak; /* TRUE if WeakSet/WeakMap */+ struct list_head records; /* list of JSMapRecord.link */+ uint32_t record_count;+ struct list_head *hash_table;+ uint32_t hash_size; /* must be a power of two */+ uint32_t record_count_threshold; /* count at which a hash table+ resize is needed */+} JSMapState;++#define MAGIC_SET (1 << 0)+#define MAGIC_WEAK (1 << 1)++static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target,+ int argc, JSValueConst *argv, int magic)+{+ JSMapState *s;+ JSValue obj, adder = JS_UNDEFINED, iter = JS_UNDEFINED, next_method = JS_UNDEFINED;+ JSValueConst arr;+ BOOL is_set, is_weak;++ is_set = magic & MAGIC_SET;+ is_weak = ((magic & MAGIC_WEAK) != 0);+ obj = js_create_from_ctor(ctx, new_target, JS_CLASS_MAP + magic);+ if (JS_IsException(obj))+ return JS_EXCEPTION;+ s = js_mallocz(ctx, sizeof(*s));+ if (!s)+ goto fail;+ init_list_head(&s->records);+ s->is_weak = is_weak;+ JS_SetOpaque(obj, s);+ s->hash_size = 1;+ s->hash_table = js_malloc(ctx, sizeof(s->hash_table[0]) * s->hash_size);+ if (!s->hash_table)+ goto fail;+ init_list_head(&s->hash_table[0]);+ s->record_count_threshold = 4;++ arr = JS_UNDEFINED;+ if (argc > 0)+ arr = argv[0];+ if (!JS_IsUndefined(arr) && !JS_IsNull(arr)) {+ JSValue item, ret;+ BOOL done;++ adder = JS_GetProperty(ctx, obj, is_set ? JS_ATOM_add : JS_ATOM_set);+ if (JS_IsException(adder))+ goto fail;+ if (!JS_IsFunction(ctx, adder)) {+ JS_ThrowTypeError(ctx, "set/add is not a function");+ goto fail;+ }++ iter = JS_GetIterator(ctx, arr, FALSE);+ if (JS_IsException(iter))+ goto fail;+ next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);+ if (JS_IsException(next_method))+ goto fail;++ for(;;) {+ item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);+ if (JS_IsException(item))+ goto fail;+ if (done) {+ JS_FreeValue(ctx, item);+ break;+ }+ if (is_set) {+ ret = JS_Call(ctx, adder, obj, 1, (JSValueConst *)&item);+ if (JS_IsException(ret)) {+ JS_FreeValue(ctx, item);+ goto fail;+ }+ } else {+ JSValue key, value;+ JSValueConst args[2];+ key = JS_UNDEFINED;+ value = JS_UNDEFINED;+ if (!JS_IsObject(item)) {+ JS_ThrowTypeErrorNotAnObject(ctx);+ goto fail1;+ }+ key = JS_GetPropertyUint32(ctx, item, 0);+ if (JS_IsException(key))+ goto fail1;+ value = JS_GetPropertyUint32(ctx, item, 1);+ if (JS_IsException(value))+ goto fail1;+ args[0] = key;+ args[1] = value;+ ret = JS_Call(ctx, adder, obj, 2, args);+ if (JS_IsException(ret)) {+ fail1:+ JS_FreeValue(ctx, item);+ JS_FreeValue(ctx, key);+ JS_FreeValue(ctx, value);+ goto fail;+ }+ JS_FreeValue(ctx, key);+ JS_FreeValue(ctx, value);+ }+ JS_FreeValue(ctx, ret);+ JS_FreeValue(ctx, item);+ }+ JS_FreeValue(ctx, next_method);+ JS_FreeValue(ctx, iter);+ JS_FreeValue(ctx, adder);+ }+ return obj;+ fail:+ if (JS_IsObject(iter)) {+ /* close the iterator object, preserving pending exception */+ JS_IteratorClose(ctx, iter, TRUE);+ }+ JS_FreeValue(ctx, next_method);+ JS_FreeValue(ctx, iter);+ JS_FreeValue(ctx, adder);+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++/* XXX: could normalize strings to speed up comparison */+static JSValueConst map_normalize_key(JSContext *ctx, JSValueConst key)+{+ uint32_t tag = JS_VALUE_GET_TAG(key);+ /* convert -0.0 to +0.0 */+ if (JS_TAG_IS_FLOAT64(tag) && JS_VALUE_GET_FLOAT64(key) == 0.0) {+ key = JS_NewInt32(ctx, 0);+ }+ return key;+}++/* XXX: better hash ? */+static uint32_t map_hash_key(JSContext *ctx, JSValueConst key)+{+ uint32_t tag = JS_VALUE_GET_NORM_TAG(key);+ uint32_t h;+ double d;+ JSFloat64Union u;++ switch(tag) {+ case JS_TAG_BOOL:+ h = JS_VALUE_GET_INT(key);+ break;+ case JS_TAG_STRING:+ h = hash_string(JS_VALUE_GET_STRING(key), 0);+ break;+ case JS_TAG_OBJECT:+ case JS_TAG_SYMBOL:+ h = (uintptr_t)JS_VALUE_GET_PTR(key) * 3163;+ break;+ case JS_TAG_INT:+ d = JS_VALUE_GET_INT(key) * 3163;+ goto hash_float64;+ case JS_TAG_FLOAT64:+ d = JS_VALUE_GET_FLOAT64(key);+ /* normalize the NaN */+ if (isnan(d))+ d = JS_FLOAT64_NAN;+ hash_float64:+ u.d = d;+ h = (u.u32[0] ^ u.u32[1]) * 3163;+ break;+ default:+ h = 0; /* XXX: bignum support */+ break;+ }+ h ^= tag;+ return h;+}++static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s,+ JSValueConst key)+{+ struct list_head *el;+ JSMapRecord *mr;+ uint32_t h;+ h = map_hash_key(ctx, key) & (s->hash_size - 1);+ list_for_each(el, &s->hash_table[h]) {+ mr = list_entry(el, JSMapRecord, hash_link);+ if (js_same_value_zero(ctx, mr->key, key))+ return mr;+ }+ return NULL;+}++static void map_hash_resize(JSContext *ctx, JSMapState *s)+{+ uint32_t new_hash_size, i, h;+ size_t slack;+ struct list_head *new_hash_table, *el;+ JSMapRecord *mr;++ /* XXX: no reporting of memory allocation failure */+ if (s->hash_size == 1)+ new_hash_size = 4;+ else+ new_hash_size = s->hash_size * 2;+ new_hash_table = js_realloc2(ctx, s->hash_table,+ sizeof(new_hash_table[0]) * new_hash_size, &slack);+ if (!new_hash_table)+ return;+ new_hash_size += slack / sizeof(*new_hash_table);++ for(i = 0; i < new_hash_size; i++)+ init_list_head(&new_hash_table[i]);++ list_for_each(el, &s->records) {+ mr = list_entry(el, JSMapRecord, link);+ if (!mr->empty) {+ h = map_hash_key(ctx, mr->key) & (new_hash_size - 1);+ list_add_tail(&mr->hash_link, &new_hash_table[h]);+ }+ }+ s->hash_table = new_hash_table;+ s->hash_size = new_hash_size;+ s->record_count_threshold = new_hash_size * 2;+}++static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s,+ JSValueConst key)+{+ uint32_t h;+ JSMapRecord *mr;++ mr = js_malloc(ctx, sizeof(*mr));+ if (!mr)+ return NULL;+ mr->ref_count = 1;+ mr->map = s;+ mr->empty = FALSE;+ if (s->is_weak) {+ JSObject *p = JS_VALUE_GET_OBJ(key);+ /* Add the weak reference */+ mr->next_weak_ref = p->first_weak_ref;+ p->first_weak_ref = mr;+ } else {+ JS_DupValue(ctx, key);+ }+ mr->key = (JSValue)key;+ h = map_hash_key(ctx, key) & (s->hash_size - 1);+ list_add_tail(&mr->hash_link, &s->hash_table[h]);+ list_add_tail(&mr->link, &s->records);+ s->record_count++;+ if (s->record_count >= s->record_count_threshold) {+ map_hash_resize(ctx, s);+ }+ return mr;+}++/* Remove the weak reference from the object weak+ reference list. we don't use a doubly linked list to+ save space, assuming a given object has few weak+ references to it */+static void delete_weak_ref(JSRuntime *rt, JSMapRecord *mr)+{+ JSMapRecord **pmr, *mr1;+ JSObject *p;++ p = JS_VALUE_GET_OBJ(mr->key);+ pmr = &p->first_weak_ref;+ for(;;) {+ mr1 = *pmr;+ assert(mr1 != NULL);+ if (mr1 == mr)+ break;+ pmr = &mr1->next_weak_ref;+ }+ *pmr = mr1->next_weak_ref;+}++static void map_delete_record(JSRuntime *rt, JSMapState *s, JSMapRecord *mr)+{+ if (mr->empty)+ return;+ list_del(&mr->hash_link);+ if (s->is_weak) {+ delete_weak_ref(rt, mr);+ } else {+ JS_FreeValueRT(rt, mr->key);+ }+ JS_FreeValueRT(rt, mr->value);+ if (--mr->ref_count == 0) {+ list_del(&mr->link);+ js_free_rt(rt, mr);+ } else {+ /* keep a zombie record for iterators */+ mr->empty = TRUE;+ mr->key = JS_UNDEFINED;+ mr->value = JS_UNDEFINED;+ }+ s->record_count--;+}++static void map_decref_record(JSRuntime *rt, JSMapRecord *mr)+{+ if (--mr->ref_count == 0) {+ /* the record can be safely removed */+ assert(mr->empty);+ list_del(&mr->link);+ js_free_rt(rt, mr);+ }+}++static void reset_weak_ref(JSRuntime *rt, JSObject *p)+{+ JSMapRecord *mr, *mr_next;+ JSMapState *s;+ + /* first pass to remove the records from the WeakMap/WeakSet+ lists */+ for(mr = p->first_weak_ref; mr != NULL; mr = mr->next_weak_ref) {+ s = mr->map;+ assert(s->is_weak);+ assert(!mr->empty); /* no iterator on WeakMap/WeakSet */+ list_del(&mr->hash_link);+ list_del(&mr->link);+ }+ + /* second pass to free the values to avoid modifying the weak+ reference list while traversing it. */+ for(mr = p->first_weak_ref; mr != NULL; mr = mr_next) {+ mr_next = mr->next_weak_ref;+ JS_FreeValueRT(rt, mr->value);+ js_free_rt(rt, mr);+ }++ p->first_weak_ref = NULL; /* fail safe */+}++static JSValue js_map_set(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);+ JSMapRecord *mr;+ JSValueConst key, value;++ if (!s)+ return JS_EXCEPTION;+ key = map_normalize_key(ctx, argv[0]);+ if (s->is_weak && !JS_IsObject(key))+ return JS_ThrowTypeErrorNotAnObject(ctx);+ if (magic & MAGIC_SET)+ value = JS_UNDEFINED;+ else+ value = argv[1];+ mr = map_find_record(ctx, s, key);+ if (mr) {+ JS_FreeValue(ctx, mr->value);+ } else {+ mr = map_add_record(ctx, s, key);+ if (!mr)+ return JS_EXCEPTION;+ }+ mr->value = JS_DupValue(ctx, value);+ return JS_DupValue(ctx, this_val);+}++static JSValue js_map_get(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);+ JSMapRecord *mr;+ JSValueConst key;++ if (!s)+ return JS_EXCEPTION;+ key = map_normalize_key(ctx, argv[0]);+ mr = map_find_record(ctx, s, key);+ if (!mr)+ return JS_UNDEFINED;+ else+ return JS_DupValue(ctx, mr->value);+}++static JSValue js_map_has(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);+ JSMapRecord *mr;+ JSValueConst key;++ if (!s)+ return JS_EXCEPTION;+ key = map_normalize_key(ctx, argv[0]);+ mr = map_find_record(ctx, s, key);+ return JS_NewBool(ctx, (mr != NULL));+}++static JSValue js_map_delete(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);+ JSMapRecord *mr;+ JSValueConst key;++ if (!s)+ return JS_EXCEPTION;+ key = map_normalize_key(ctx, argv[0]);+ mr = map_find_record(ctx, s, key);+ if (!mr)+ return JS_FALSE;+ map_delete_record(ctx->rt, s, mr);+ return JS_TRUE;+}++static JSValue js_map_clear(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);+ struct list_head *el, *el1;+ JSMapRecord *mr;++ if (!s)+ return JS_EXCEPTION;+ list_for_each_safe(el, el1, &s->records) {+ mr = list_entry(el, JSMapRecord, link);+ map_delete_record(ctx->rt, s, mr);+ }+ return JS_UNDEFINED;+}++static JSValue js_map_get_size(JSContext *ctx, JSValueConst this_val, int magic)+{+ JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);+ if (!s)+ return JS_EXCEPTION;+ return JS_NewUint32(ctx, s->record_count);+}++static JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);+ JSValueConst func, this_arg;+ JSValue ret, args[3];+ struct list_head *el;+ JSMapRecord *mr;++ if (!s)+ return JS_EXCEPTION;+ func = argv[0];+ if (argc > 1)+ this_arg = argv[1];+ else+ this_arg = JS_UNDEFINED;+ if (check_function(ctx, func))+ return JS_EXCEPTION;+ /* Note: the list can be modified while traversing it, but the+ current element is locked */+ el = s->records.next;+ while (el != &s->records) {+ mr = list_entry(el, JSMapRecord, link);+ if (!mr->empty) {+ mr->ref_count++;+ /* must duplicate in case the record is deleted */+ args[1] = JS_DupValue(ctx, mr->key);+ if (magic)+ args[0] = args[1];+ else+ args[0] = JS_DupValue(ctx, mr->value);+ args[2] = (JSValue)this_val;+ ret = JS_Call(ctx, func, this_arg, 3, (JSValueConst *)args);+ JS_FreeValue(ctx, args[0]);+ if (!magic)+ JS_FreeValue(ctx, args[1]);+ el = el->next;+ map_decref_record(ctx->rt, mr);+ if (JS_IsException(ret))+ return ret;+ JS_FreeValue(ctx, ret);+ } else {+ el = el->next;+ }+ }+ return JS_UNDEFINED;+}++static void js_map_finalizer(JSRuntime *rt, JSValue val)+{+ JSObject *p;+ JSMapState *s;+ struct list_head *el, *el1;+ JSMapRecord *mr;++ p = JS_VALUE_GET_OBJ(val);+ s = p->u.map_state;+ if (s) {+ /* if the object is deleted we are sure that no iterator is+ using it */+ list_for_each_safe(el, el1, &s->records) {+ mr = list_entry(el, JSMapRecord, link);+ if (!mr->empty) {+ if (s->is_weak)+ delete_weak_ref(rt, mr);+ else+ JS_FreeValueRT(rt, mr->key);+ JS_FreeValueRT(rt, mr->value);+ }+ js_free_rt(rt, mr);+ }+ js_free_rt(rt, s->hash_table);+ js_free_rt(rt, s);+ }+}++static void js_map_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)+{+ JSObject *p = JS_VALUE_GET_OBJ(val);+ JSMapState *s;+ struct list_head *el;+ JSMapRecord *mr;++ s = p->u.map_state;+ if (s) {+ list_for_each(el, &s->records) {+ mr = list_entry(el, JSMapRecord, link);+ if (!s->is_weak)+ JS_MarkValue(rt, mr->key, mark_func);+ JS_MarkValue(rt, mr->value, mark_func);+ }+ }+}++/* Map Iterator */++typedef struct JSMapIteratorData {+ JSValue obj;+ JSIteratorKindEnum kind;+ JSMapRecord *cur_record;+} JSMapIteratorData;++static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val)+{+ JSObject *p;+ JSMapIteratorData *it;++ p = JS_VALUE_GET_OBJ(val);+ it = p->u.map_iterator_data;+ if (it) {+ /* During the GC sweep phase the Map finalizer may be+ called before the Map iterator finalizer */+ if (JS_IsLiveObject(rt, it->obj) && it->cur_record) {+ map_decref_record(rt, it->cur_record);+ }+ JS_FreeValueRT(rt, it->obj);+ js_free_rt(rt, it);+ }+}++static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func)+{+ JSObject *p = JS_VALUE_GET_OBJ(val);+ JSMapIteratorData *it;+ it = p->u.map_iterator_data;+ if (it) {+ /* the record is already marked by the object */+ JS_MarkValue(rt, it->obj, mark_func);+ }+}++static JSValue js_create_map_iterator(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ JSIteratorKindEnum kind;+ JSMapState *s;+ JSMapIteratorData *it;+ JSValue enum_obj;++ kind = magic >> 2;+ magic &= 3;+ s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);+ if (!s)+ return JS_EXCEPTION;+ enum_obj = JS_NewObjectClass(ctx, JS_CLASS_MAP_ITERATOR + magic);+ if (JS_IsException(enum_obj))+ goto fail;+ it = js_malloc(ctx, sizeof(*it));+ if (!it) {+ JS_FreeValue(ctx, enum_obj);+ goto fail;+ }+ it->obj = JS_DupValue(ctx, this_val);+ it->kind = kind;+ it->cur_record = NULL;+ JS_SetOpaque(enum_obj, it);+ return enum_obj;+ fail:+ return JS_EXCEPTION;+}++static JSValue js_map_iterator_next(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv,+ BOOL *pdone, int magic)+{+ JSMapIteratorData *it;+ JSMapState *s;+ JSMapRecord *mr;+ struct list_head *el;++ it = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP_ITERATOR + magic);+ if (!it) {+ *pdone = FALSE;+ return JS_EXCEPTION;+ }+ if (JS_IsUndefined(it->obj))+ goto done;+ s = JS_GetOpaque(it->obj, JS_CLASS_MAP + magic);+ assert(s != NULL);+ if (!it->cur_record) {+ el = s->records.next;+ } else {+ mr = it->cur_record;+ el = mr->link.next;+ map_decref_record(ctx->rt, mr); /* the record can be freed here */+ }+ for(;;) {+ if (el == &s->records) {+ /* no more record */+ it->cur_record = NULL;+ JS_FreeValue(ctx, it->obj);+ it->obj = JS_UNDEFINED;+ done:+ /* end of enumeration */+ *pdone = TRUE;+ return JS_UNDEFINED;+ }+ mr = list_entry(el, JSMapRecord, link);+ if (!mr->empty)+ break;+ /* get the next record */+ el = mr->link.next;+ }++ /* lock the record so that it won't be freed */+ mr->ref_count++;+ it->cur_record = mr;+ *pdone = FALSE;++ if (it->kind == JS_ITERATOR_KIND_KEY) {+ return JS_DupValue(ctx, mr->key);+ } else {+ JSValueConst args[2];+ args[0] = mr->key;+ if (magic)+ args[1] = mr->key;+ else+ args[1] = mr->value;+ if (it->kind == JS_ITERATOR_KIND_VALUE) {+ return JS_DupValue(ctx, args[1]);+ } else {+ return js_create_array(ctx, 2, args);+ }+ }+}++static const JSCFunctionListEntry js_map_funcs[] = {+ JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),+};++static const JSCFunctionListEntry js_map_proto_funcs[] = {+ JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, 0 ),+ JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, 0 ),+ JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, 0 ),+ JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, 0 ),+ JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, 0 ),+ JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, 0),+ JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, 0 ),+ JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_VALUE << 2) | 0 ),+ JS_CFUNC_MAGIC_DEF("keys", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | 0 ),+ JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | 0 ),+ JS_ALIAS_DEF("[Symbol.iterator]", "entries" ),+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map", JS_PROP_CONFIGURABLE ),+};++static const JSCFunctionListEntry js_map_iterator_proto_funcs[] = {+ JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, 0 ),+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map Iterator", JS_PROP_CONFIGURABLE ),+};++static const JSCFunctionListEntry js_set_proto_funcs[] = {+ JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET ),+ JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET ),+ JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET ),+ JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, MAGIC_SET ),+ JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, MAGIC_SET ),+ JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, MAGIC_SET ),+ JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | MAGIC_SET ),+ JS_ALIAS_DEF("keys", "values" ),+ JS_ALIAS_DEF("[Symbol.iterator]", "values" ),+ JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | MAGIC_SET ),+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set", JS_PROP_CONFIGURABLE ),+};++static const JSCFunctionListEntry js_set_iterator_proto_funcs[] = {+ JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, MAGIC_SET ),+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set Iterator", JS_PROP_CONFIGURABLE ),+};++static const JSCFunctionListEntry js_weak_map_proto_funcs[] = {+ JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, MAGIC_WEAK ),+ JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, MAGIC_WEAK ),+ JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_WEAK ),+ JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_WEAK ),+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakMap", JS_PROP_CONFIGURABLE ),+};++static const JSCFunctionListEntry js_weak_set_proto_funcs[] = {+ JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET | MAGIC_WEAK ),+ JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET | MAGIC_WEAK ),+ JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET | MAGIC_WEAK ),+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakSet", JS_PROP_CONFIGURABLE ),+};++static const JSCFunctionListEntry * const js_map_proto_funcs_ptr[6] = {+ js_map_proto_funcs,+ js_set_proto_funcs,+ js_weak_map_proto_funcs,+ js_weak_set_proto_funcs,+ js_map_iterator_proto_funcs,+ js_set_iterator_proto_funcs,+};++static const uint8_t js_map_proto_funcs_count[6] = {+ countof(js_map_proto_funcs),+ countof(js_set_proto_funcs),+ countof(js_weak_map_proto_funcs),+ countof(js_weak_set_proto_funcs),+ countof(js_map_iterator_proto_funcs),+ countof(js_set_iterator_proto_funcs),+};++void JS_AddIntrinsicMapSet(JSContext *ctx)+{+ int i;+ JSValue obj1;+ char buf[ATOM_GET_STR_BUF_SIZE];++ for(i = 0; i < 4; i++) {+ const char *name = JS_AtomGetStr(ctx, buf, sizeof(buf),+ JS_ATOM_Map + i);+ ctx->class_proto[JS_CLASS_MAP + i] = JS_NewObject(ctx);+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP + i],+ js_map_proto_funcs_ptr[i],+ js_map_proto_funcs_count[i]);+ obj1 = JS_NewCFunctionMagic(ctx, js_map_constructor, name, 0,+ JS_CFUNC_constructor_magic, i);+ if (i < 2) {+ JS_SetPropertyFunctionList(ctx, obj1, js_map_funcs,+ countof(js_map_funcs));+ }+ JS_NewGlobalCConstructor2(ctx, obj1, name, ctx->class_proto[JS_CLASS_MAP + i]);+ }++ for(i = 0; i < 2; i++) {+ ctx->class_proto[JS_CLASS_MAP_ITERATOR + i] =+ JS_NewObjectProto(ctx, ctx->iterator_proto);+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP_ITERATOR + i],+ js_map_proto_funcs_ptr[i + 4],+ js_map_proto_funcs_count[i + 4]);+ }+}++/* Generator */+static const JSCFunctionListEntry js_generator_function_proto_funcs[] = {+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "GeneratorFunction", JS_PROP_CONFIGURABLE),+};++static const JSCFunctionListEntry js_generator_proto_funcs[] = {+ JS_ITERATOR_NEXT_DEF("next", 1, js_generator_next, GEN_MAGIC_NEXT ),+ JS_ITERATOR_NEXT_DEF("return", 1, js_generator_next, GEN_MAGIC_RETURN ),+ JS_ITERATOR_NEXT_DEF("throw", 1, js_generator_next, GEN_MAGIC_THROW ),+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Generator", JS_PROP_CONFIGURABLE),+};++/* Promise */++typedef enum JSPromiseStateEnum {+ JS_PROMISE_PENDING,+ JS_PROMISE_FULFILLED,+ JS_PROMISE_REJECTED,+} JSPromiseStateEnum;++typedef struct JSPromiseData {+ JSPromiseStateEnum promise_state;+ /* 0=fulfill, 1=reject, list of JSPromiseReactionData.link */+ struct list_head promise_reactions[2];+ BOOL is_handled; /* Note: only useful to debug */+ JSValue promise_result;+} JSPromiseData;++typedef struct JSPromiseFunctionDataResolved {+ int ref_count;+ BOOL already_resolved;+} JSPromiseFunctionDataResolved;++typedef struct JSPromiseFunctionData {+ JSValue promise;+ JSPromiseFunctionDataResolved *presolved;+} JSPromiseFunctionData;++typedef struct JSPromiseReactionData {+ struct list_head link; /* not used in promise_reaction_job */+ JSValue resolving_funcs[2];+ JSValue handler;+} JSPromiseReactionData;++static int js_create_resolving_functions(JSContext *ctx, JSValue *args,+ JSValueConst promise);++static void promise_reaction_data_free(JSRuntime *rt,+ JSPromiseReactionData *rd)+{+ JS_FreeValueRT(rt, rd->resolving_funcs[0]);+ JS_FreeValueRT(rt, rd->resolving_funcs[1]);+ JS_FreeValueRT(rt, rd->handler);+ js_free_rt(rt, rd);+}++static JSValue promise_reaction_job(JSContext *ctx, int argc,+ JSValueConst *argv)+{+ JSValueConst handler, arg, func;+ JSValue res, res2;+ BOOL is_reject;++ assert(argc == 5);+ handler = argv[2];+ is_reject = JS_ToBool(ctx, argv[3]);+ arg = argv[4];+#ifdef DUMP_PROMISE+ printf("promise_reaction_job: is_reject=%d\n", is_reject);+#endif++ if (JS_IsUndefined(handler)) {+ if (is_reject) {+ res = JS_Throw(ctx, JS_DupValue(ctx, arg));+ } else {+ res = JS_DupValue(ctx, arg);+ }+ } else {+ res = JS_Call(ctx, handler, JS_UNDEFINED, 1, &arg);+ }+ is_reject = JS_IsException(res);+ if (is_reject)+ res = JS_GetException(ctx);+ func = argv[is_reject];+ /* as an extension, we support undefined as value to avoid+ creating a dummy promise in the 'await' implementation of async+ functions */+ if (!JS_IsUndefined(func)) {+ res2 = JS_Call(ctx, func, JS_UNDEFINED,+ 1, (JSValueConst *)&res);+ } else {+ res2 = JS_UNDEFINED;+ }+ JS_FreeValue(ctx, res);++ return res2;+}++void JS_SetHostPromiseRejectionTracker(JSRuntime *rt,+ JSHostPromiseRejectionTracker *cb,+ void *opaque)+{+ rt->host_promise_rejection_tracker = cb;+ rt->host_promise_rejection_tracker_opaque = opaque;+}++static void fulfill_or_reject_promise(JSContext *ctx, JSValueConst promise,+ JSValueConst value, BOOL is_reject)+{+ JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);+ struct list_head *el, *el1;+ JSPromiseReactionData *rd;+ JSValueConst args[5];++ if (!s || s->promise_state != JS_PROMISE_PENDING)+ return; /* should never happen */+ set_value(ctx, &s->promise_result, JS_DupValue(ctx, value));+ s->promise_state = JS_PROMISE_FULFILLED + is_reject;+#ifdef DUMP_PROMISE+ printf("fulfill_or_reject_promise: is_reject=%d\n", is_reject);+#endif+ if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) {+ JSRuntime *rt = ctx->rt;+ if (rt->host_promise_rejection_tracker) {+ rt->host_promise_rejection_tracker(ctx, promise, value, FALSE,+ rt->host_promise_rejection_tracker_opaque);+ }+ }++ list_for_each_safe(el, el1, &s->promise_reactions[is_reject]) {+ rd = list_entry(el, JSPromiseReactionData, link);+ args[0] = rd->resolving_funcs[0];+ args[1] = rd->resolving_funcs[1];+ args[2] = rd->handler;+ args[3] = JS_NewBool(ctx, is_reject);+ args[4] = value;+ JS_EnqueueJob(ctx, promise_reaction_job, 5, args);+ list_del(&rd->link);+ promise_reaction_data_free(ctx->rt, rd);+ }++ list_for_each_safe(el, el1, &s->promise_reactions[1 - is_reject]) {+ rd = list_entry(el, JSPromiseReactionData, link);+ list_del(&rd->link);+ promise_reaction_data_free(ctx->rt, rd);+ }+}++static void reject_promise(JSContext *ctx, JSValueConst promise,+ JSValueConst value)+{+ fulfill_or_reject_promise(ctx, promise, value, TRUE);+}++static JSValue js_promise_resolve_thenable_job(JSContext *ctx,+ int argc, JSValueConst *argv)+{+ JSValueConst promise, thenable, then;+ JSValue args[2], res;++#ifdef DUMP_PROMISE+ printf("js_promise_resolve_thenable_job\n");+#endif+ assert(argc == 3);+ promise = argv[0];+ thenable = argv[1];+ then = argv[2];+ if (js_create_resolving_functions(ctx, args, promise) < 0)+ return JS_EXCEPTION;+ res = JS_Call(ctx, then, thenable, 2, (JSValueConst *)args);+ if (JS_IsException(res)) {+ JSValue error = JS_GetException(ctx);+ res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error);+ JS_FreeValue(ctx, error);+ }+ JS_FreeValue(ctx, args[0]);+ JS_FreeValue(ctx, args[1]);+ return res;+}++static void js_promise_resolve_function_free_resolved(JSRuntime *rt,+ JSPromiseFunctionDataResolved *sr)+{+ if (--sr->ref_count == 0) {+ js_free_rt(rt, sr);+ }+}++static int js_create_resolving_functions(JSContext *ctx,+ JSValue *resolving_funcs,+ JSValueConst promise)++{+ JSValue obj;+ JSPromiseFunctionData *s;+ JSPromiseFunctionDataResolved *sr;+ int i, ret;++ sr = js_malloc(ctx, sizeof(*sr));+ if (!sr)+ return -1;+ sr->ref_count = 1;+ sr->already_resolved = FALSE; /* must be shared between the two functions */+ ret = 0;+ for(i = 0; i < 2; i++) {+ obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,+ JS_CLASS_PROMISE_RESOLVE_FUNCTION + i);+ if (JS_IsException(obj))+ goto fail;+ s = js_malloc(ctx, sizeof(*s));+ if (!s) {+ JS_FreeValue(ctx, obj);+ fail:++ if (i != 0)+ JS_FreeValue(ctx, resolving_funcs[0]);+ ret = -1;+ break;+ }+ sr->ref_count++;+ s->presolved = sr;+ s->promise = JS_DupValue(ctx, promise);+ JS_SetOpaque(obj, s);+ js_function_set_properties(ctx, obj, JS_ATOM_empty_string, 1);+ resolving_funcs[i] = obj;+ }+ js_promise_resolve_function_free_resolved(ctx->rt, sr);+ return ret;+}++static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val)+{+ JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data;+ if (s) {+ js_promise_resolve_function_free_resolved(rt, s->presolved);+ JS_FreeValueRT(rt, s->promise);+ js_free_rt(rt, s);+ }+}++static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func)+{+ JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data;+ if (s) {+ JS_MarkValue(rt, s->promise, mark_func);+ }+}++static JSValue js_promise_resolve_function_call(JSContext *ctx,+ JSValueConst func_obj,+ JSValueConst this_val,+ int argc, JSValueConst *argv,+ int flags)+{+ JSObject *p = JS_VALUE_GET_OBJ(func_obj);+ JSPromiseFunctionData *s;+ JSValueConst resolution, args[3];+ JSValue then;+ BOOL is_reject;++ s = p->u.promise_function_data;+ if (!s || s->presolved->already_resolved)+ return JS_UNDEFINED;+ s->presolved->already_resolved = TRUE;+ is_reject = p->class_id - JS_CLASS_PROMISE_RESOLVE_FUNCTION;+ if (argc > 0)+ resolution = argv[0];+ else+ resolution = JS_UNDEFINED;+#ifdef DUMP_PROMISE+ printf("js_promise_resolving_function_call: is_reject=%d resolution=", is_reject);+ JS_DumpValue(ctx, resolution);+ printf("\n");+#endif+ if (is_reject || !JS_IsObject(resolution)) {+ goto done;+ } else if (js_same_value(ctx, resolution, s->promise)) {+ JS_ThrowTypeError(ctx, "promise self resolution");+ goto fail_reject;+ }+ then = JS_GetProperty(ctx, resolution, JS_ATOM_then);+ if (JS_IsException(then)) {+ JSValue error;+ fail_reject:+ error = JS_GetException(ctx);+ reject_promise(ctx, s->promise, error);+ JS_FreeValue(ctx, error);+ } else if (!JS_IsFunction(ctx, then)) {+ JS_FreeValue(ctx, then);+ done:+ fulfill_or_reject_promise(ctx, s->promise, resolution, is_reject);+ } else {+ args[0] = s->promise;+ args[1] = resolution;+ args[2] = then;+ JS_EnqueueJob(ctx, js_promise_resolve_thenable_job, 3, args);+ JS_FreeValue(ctx, then);+ }+ return JS_UNDEFINED;+}++static void js_promise_finalizer(JSRuntime *rt, JSValue val)+{+ JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE);+ struct list_head *el, *el1;+ int i;++ if (!s)+ return;+ for(i = 0; i < 2; i++) {+ list_for_each_safe(el, el1, &s->promise_reactions[i]) {+ JSPromiseReactionData *rd =+ list_entry(el, JSPromiseReactionData, link);+ promise_reaction_data_free(rt, rd);+ }+ }+ JS_FreeValueRT(rt, s->promise_result);+ js_free_rt(rt, s);+}++static void js_promise_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func)+{+ JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE);+ struct list_head *el;+ int i;++ if (!s)+ return;+ for(i = 0; i < 2; i++) {+ list_for_each(el, &s->promise_reactions[i]) {+ JSPromiseReactionData *rd =+ list_entry(el, JSPromiseReactionData, link);+ JS_MarkValue(rt, rd->resolving_funcs[0], mark_func);+ JS_MarkValue(rt, rd->resolving_funcs[1], mark_func);+ JS_MarkValue(rt, rd->handler, mark_func);+ }+ }+ JS_MarkValue(rt, s->promise_result, mark_func);+}++static JSValue js_promise_constructor(JSContext *ctx, JSValueConst new_target,+ int argc, JSValueConst *argv)+{+ JSValueConst executor;+ JSValue obj;+ JSPromiseData *s;+ JSValue args[2], ret;+ int i;++ executor = argv[0];+ if (check_function(ctx, executor))+ return JS_EXCEPTION;+ obj = js_create_from_ctor(ctx, new_target, JS_CLASS_PROMISE);+ if (JS_IsException(obj))+ return JS_EXCEPTION;+ s = js_mallocz(ctx, sizeof(*s));+ if (!s)+ goto fail;+ s->promise_state = JS_PROMISE_PENDING;+ s->is_handled = FALSE;+ for(i = 0; i < 2; i++)+ init_list_head(&s->promise_reactions[i]);+ s->promise_result = JS_UNDEFINED;+ JS_SetOpaque(obj, s);+ if (js_create_resolving_functions(ctx, args, obj))+ goto fail;+ ret = JS_Call(ctx, executor, JS_UNDEFINED, 2, (JSValueConst *)args);+ if (JS_IsException(ret)) {+ JSValue ret2, error;+ error = JS_GetException(ctx);+ ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error);+ JS_FreeValue(ctx, error);+ if (JS_IsException(ret2))+ goto fail1;+ JS_FreeValue(ctx, ret2);+ }+ JS_FreeValue(ctx, ret);+ JS_FreeValue(ctx, args[0]);+ JS_FreeValue(ctx, args[1]);+ return obj;+ fail1:+ JS_FreeValue(ctx, args[0]);+ JS_FreeValue(ctx, args[1]);+ fail:+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+}++static JSValue js_promise_executor(JSContext *ctx,+ JSValueConst this_val,+ int argc, JSValueConst *argv,+ int magic, JSValue *func_data)+{+ int i;++ for(i = 0; i < 2; i++) {+ if (!JS_IsUndefined(func_data[i]))+ return JS_ThrowTypeError(ctx, "resolving function already set");+ func_data[i] = JS_DupValue(ctx, argv[i]);+ }+ return JS_UNDEFINED;+}++static JSValue js_promise_executor_new(JSContext *ctx)+{+ JSValueConst func_data[2];++ func_data[0] = JS_UNDEFINED;+ func_data[1] = JS_UNDEFINED;+ return JS_NewCFunctionData(ctx, js_promise_executor, 2,+ 0, 2, func_data);+}++static JSValue js_new_promise_capability(JSContext *ctx,+ JSValue *resolving_funcs,+ JSValueConst ctor)+{+ JSValue executor, result_promise;+ JSCFunctionDataRecord *s;+ int i;++ executor = js_promise_executor_new(ctx);+ if (JS_IsException(executor))+ return executor;++ if (JS_IsUndefined(ctor)) {+ result_promise = js_promise_constructor(ctx, ctor, 1,+ (JSValueConst *)&executor);+ } else {+ result_promise = JS_CallConstructor(ctx, ctor, 1,+ (JSValueConst *)&executor);+ }+ if (JS_IsException(result_promise))+ goto fail;+ s = JS_GetOpaque(executor, JS_CLASS_C_FUNCTION_DATA);+ for(i = 0; i < 2; i++) {+ if (check_function(ctx, s->data[i]))+ goto fail;+ }+ for(i = 0; i < 2; i++)+ resolving_funcs[i] = JS_DupValue(ctx, s->data[i]);+ JS_FreeValue(ctx, executor);+ return result_promise;+ fail:+ JS_FreeValue(ctx, executor);+ JS_FreeValue(ctx, result_promise);+ return JS_EXCEPTION;+}++JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs)+{+ return js_new_promise_capability(ctx, resolving_funcs, JS_UNDEFINED);+}++static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ JSValue result_promise, resolving_funcs[2], ret;+ BOOL is_reject = magic;++ if (!JS_IsObject(this_val))+ return JS_ThrowTypeErrorNotAnObject(ctx);+ if (!is_reject && JS_GetOpaque(argv[0], JS_CLASS_PROMISE)) {+ JSValue ctor;+ BOOL is_same;+ ctor = JS_GetProperty(ctx, argv[0], JS_ATOM_constructor);+ if (JS_IsException(ctor))+ return ctor;+ is_same = js_same_value(ctx, ctor, this_val);+ JS_FreeValue(ctx, ctor);+ if (is_same)+ return JS_DupValue(ctx, argv[0]);+ }+ result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);+ if (JS_IsException(result_promise))+ return result_promise;+ ret = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 1, argv);+ JS_FreeValue(ctx, resolving_funcs[0]);+ JS_FreeValue(ctx, resolving_funcs[1]);+ if (JS_IsException(ret)) {+ JS_FreeValue(ctx, result_promise);+ return ret;+ }+ JS_FreeValue(ctx, ret);+ return result_promise;+}++#if 0+static JSValue js_promise___newPromiseCapability(JSContext *ctx,+ JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue result_promise, resolving_funcs[2], obj;+ JSValueConst ctor;+ ctor = argv[0];+ if (!JS_IsObject(ctor))+ return JS_ThrowTypeErrorNotAnObject(ctx);+ result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor);+ if (JS_IsException(result_promise))+ return result_promise;+ obj = JS_NewObject(ctx);+ if (JS_IsException(obj)) {+ JS_FreeValue(ctx, resolving_funcs[0]);+ JS_FreeValue(ctx, resolving_funcs[1]);+ JS_FreeValue(ctx, result_promise);+ return JS_EXCEPTION;+ }+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_promise, result_promise, JS_PROP_C_W_E);+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_resolve, resolving_funcs[0], JS_PROP_C_W_E);+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_reject, resolving_funcs[1], JS_PROP_C_W_E);+ return obj;+}+#endif++static __exception int remainingElementsCount_add(JSContext *ctx,+ JSValueConst resolve_element_env,+ int addend)+{+ JSValue val;+ int remainingElementsCount;++ val = JS_GetPropertyUint32(ctx, resolve_element_env, 0);+ if (JS_IsException(val))+ return -1;+ if (JS_ToInt32Free(ctx, &remainingElementsCount, val))+ return -1;+ remainingElementsCount += addend;+ if (JS_SetPropertyUint32(ctx, resolve_element_env, 0,+ JS_NewInt32(ctx, remainingElementsCount)) < 0)+ return -1;+ return (remainingElementsCount == 0);+}++#define PROMISE_MAGIC_all 0+#define PROMISE_MAGIC_allSettled 1+#define PROMISE_MAGIC_any 2++static JSValue js_promise_all_resolve_element(JSContext *ctx,+ JSValueConst this_val,+ int argc, JSValueConst *argv,+ int magic,+ JSValue *func_data)+{+ int resolve_type = magic & 3;+ int is_reject = magic & 4;+ BOOL alreadyCalled = JS_ToBool(ctx, func_data[0]);+ JSValueConst values = func_data[2];+ JSValueConst resolve = func_data[3];+ JSValueConst resolve_element_env = func_data[4];+ JSValue ret, obj;+ int is_zero, index;+ + if (JS_ToInt32(ctx, &index, func_data[1]))+ return JS_EXCEPTION;+ if (alreadyCalled)+ return JS_UNDEFINED;+ func_data[0] = JS_NewBool(ctx, TRUE);++ if (resolve_type == PROMISE_MAGIC_allSettled) {+ JSValue str;+ + obj = JS_NewObject(ctx);+ if (JS_IsException(obj))+ return JS_EXCEPTION;+ str = JS_NewString(ctx, is_reject ? "rejected" : "fulfilled");+ if (JS_IsException(str))+ goto fail1;+ if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_status,+ str,+ JS_PROP_C_W_E) < 0)+ goto fail1;+ if (JS_DefinePropertyValue(ctx, obj,+ is_reject ? JS_ATOM_reason : JS_ATOM_value,+ JS_DupValue(ctx, argv[0]),+ JS_PROP_C_W_E) < 0) {+ fail1:+ JS_FreeValue(ctx, obj);+ return JS_EXCEPTION;+ }+ } else {+ obj = JS_DupValue(ctx, argv[0]);+ }+ if (JS_DefinePropertyValueUint32(ctx, values, index,+ obj, JS_PROP_C_W_E) < 0)+ return JS_EXCEPTION;+ + is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1);+ if (is_zero < 0)+ return JS_EXCEPTION;+ if (is_zero) {+ if (resolve_type == PROMISE_MAGIC_any) {+ JSValue error;+ error = js_aggregate_error_constructor(ctx, values);+ if (JS_IsException(error))+ return JS_EXCEPTION;+ ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&error);+ JS_FreeValue(ctx, error);+ } else {+ ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&values);+ }+ if (JS_IsException(ret))+ return ret;+ JS_FreeValue(ctx, ret);+ }+ return JS_UNDEFINED;+}++/* magic = 0: Promise.all 1: Promise.allSettled */+static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ JSValue result_promise, resolving_funcs[2], item, next_promise, ret;+ JSValue next_method = JS_UNDEFINED, values = JS_UNDEFINED;+ JSValue resolve_element_env = JS_UNDEFINED, resolve_element, reject_element;+ JSValue promise_resolve = JS_UNDEFINED, iter = JS_UNDEFINED;+ JSValueConst then_args[2], resolve_element_data[5];+ BOOL done;+ int index, is_zero, is_promise_any = (magic == PROMISE_MAGIC_any);+ + if (!JS_IsObject(this_val))+ return JS_ThrowTypeErrorNotAnObject(ctx);+ result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);+ if (JS_IsException(result_promise))+ return result_promise;+ promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve);+ if (JS_IsException(promise_resolve) ||+ check_function(ctx, promise_resolve))+ goto fail_reject;+ iter = JS_GetIterator(ctx, argv[0], FALSE);+ if (JS_IsException(iter)) {+ JSValue error;+ fail_reject:+ error = JS_GetException(ctx);+ ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1,+ (JSValueConst *)&error);+ JS_FreeValue(ctx, error);+ if (JS_IsException(ret))+ goto fail;+ JS_FreeValue(ctx, ret);+ } else {+ next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);+ if (JS_IsException(next_method))+ goto fail_reject;+ values = JS_NewArray(ctx);+ if (JS_IsException(values))+ goto fail_reject;+ resolve_element_env = JS_NewArray(ctx);+ if (JS_IsException(resolve_element_env))+ goto fail_reject;+ /* remainingElementsCount field */+ if (JS_DefinePropertyValueUint32(ctx, resolve_element_env, 0,+ JS_NewInt32(ctx, 1),+ JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0)+ goto fail_reject;+ + index = 0;+ for(;;) {+ /* XXX: conformance: should close the iterator if error on 'done'+ access, but not on 'value' access */+ item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);+ if (JS_IsException(item))+ goto fail_reject;+ if (done)+ break;+ next_promise = JS_Call(ctx, promise_resolve, + this_val, 1, (JSValueConst *)&item);+ JS_FreeValue(ctx, item);+ if (JS_IsException(next_promise)) {+ fail_reject1:+ JS_IteratorClose(ctx, iter, TRUE);+ goto fail_reject;+ }+ resolve_element_data[0] = JS_NewBool(ctx, FALSE);+ resolve_element_data[1] = (JSValueConst)JS_NewInt32(ctx, index);+ resolve_element_data[2] = values;+ resolve_element_data[3] = resolving_funcs[is_promise_any];+ resolve_element_data[4] = resolve_element_env;+ resolve_element =+ JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1,+ magic, 5, resolve_element_data);+ if (JS_IsException(resolve_element)) {+ JS_FreeValue(ctx, next_promise);+ goto fail_reject1;+ }+ + if (magic == PROMISE_MAGIC_allSettled) {+ reject_element =+ JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1,+ magic | 4, 5, resolve_element_data);+ if (JS_IsException(reject_element)) {+ JS_FreeValue(ctx, next_promise);+ goto fail_reject1;+ }+ } else if (magic == PROMISE_MAGIC_any) {+ if (JS_DefinePropertyValueUint32(ctx, values, index,+ JS_UNDEFINED, JS_PROP_C_W_E) < 0)+ goto fail_reject1;+ reject_element = resolve_element;+ resolve_element = JS_DupValue(ctx, resolving_funcs[0]);+ } else {+ reject_element = JS_DupValue(ctx, resolving_funcs[1]);+ }++ if (remainingElementsCount_add(ctx, resolve_element_env, 1) < 0) {+ JS_FreeValue(ctx, next_promise);+ JS_FreeValue(ctx, resolve_element);+ JS_FreeValue(ctx, reject_element);+ goto fail_reject1;+ }++ then_args[0] = resolve_element;+ then_args[1] = reject_element;+ ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, then_args);+ JS_FreeValue(ctx, resolve_element);+ JS_FreeValue(ctx, reject_element);+ if (check_exception_free(ctx, ret))+ goto fail_reject1;+ index++;+ }++ is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1);+ if (is_zero < 0)+ goto fail_reject;+ if (is_zero) {+ if (magic == PROMISE_MAGIC_any) {+ JSValue error;+ error = js_aggregate_error_constructor(ctx, values);+ if (JS_IsException(error))+ goto fail_reject;+ JS_FreeValue(ctx, values);+ values = error;+ }+ ret = JS_Call(ctx, resolving_funcs[is_promise_any], JS_UNDEFINED,+ 1, (JSValueConst *)&values);+ if (check_exception_free(ctx, ret))+ goto fail_reject;+ }+ }+ done:+ JS_FreeValue(ctx, promise_resolve);+ JS_FreeValue(ctx, resolve_element_env);+ JS_FreeValue(ctx, values);+ JS_FreeValue(ctx, next_method);+ JS_FreeValue(ctx, iter);+ JS_FreeValue(ctx, resolving_funcs[0]);+ JS_FreeValue(ctx, resolving_funcs[1]);+ return result_promise;+ fail:+ JS_FreeValue(ctx, result_promise);+ result_promise = JS_EXCEPTION;+ goto done;+}++static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue result_promise, resolving_funcs[2], item, next_promise, ret;+ JSValue next_method = JS_UNDEFINED, iter = JS_UNDEFINED;+ JSValue promise_resolve = JS_UNDEFINED;+ BOOL done;++ if (!JS_IsObject(this_val))+ return JS_ThrowTypeErrorNotAnObject(ctx);+ result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);+ if (JS_IsException(result_promise))+ return result_promise;+ promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve);+ if (JS_IsException(promise_resolve) ||+ check_function(ctx, promise_resolve))+ goto fail_reject;+ iter = JS_GetIterator(ctx, argv[0], FALSE);+ if (JS_IsException(iter)) {+ JSValue error;+ fail_reject:+ error = JS_GetException(ctx);+ ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1,+ (JSValueConst *)&error);+ JS_FreeValue(ctx, error);+ if (JS_IsException(ret))+ goto fail;+ JS_FreeValue(ctx, ret);+ } else {+ next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);+ if (JS_IsException(next_method))+ goto fail_reject;++ for(;;) {+ /* XXX: conformance: should close the iterator if error on 'done'+ access, but not on 'value' access */+ item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);+ if (JS_IsException(item))+ goto fail_reject;+ if (done)+ break;+ next_promise = JS_Call(ctx, promise_resolve,+ this_val, 1, (JSValueConst *)&item);+ JS_FreeValue(ctx, item);+ if (JS_IsException(next_promise)) {+ fail_reject1:+ JS_IteratorClose(ctx, iter, TRUE);+ goto fail_reject;+ }+ ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2,+ (JSValueConst *)resolving_funcs);+ if (check_exception_free(ctx, ret))+ goto fail_reject1;+ }+ }+ done:+ JS_FreeValue(ctx, promise_resolve);+ JS_FreeValue(ctx, next_method);+ JS_FreeValue(ctx, iter);+ JS_FreeValue(ctx, resolving_funcs[0]);+ JS_FreeValue(ctx, resolving_funcs[1]);+ return result_promise;+ fail:+ //JS_FreeValue(ctx, next_method); // why not???+ JS_FreeValue(ctx, result_promise);+ result_promise = JS_EXCEPTION;+ goto done;+}++static __exception int perform_promise_then(JSContext *ctx,+ JSValueConst promise,+ JSValueConst *resolve_reject,+ JSValueConst *cap_resolving_funcs)+{+ JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);+ JSPromiseReactionData *rd_array[2], *rd;+ int i, j;++ rd_array[0] = NULL;+ rd_array[1] = NULL;+ for(i = 0; i < 2; i++) {+ JSValueConst handler;+ rd = js_mallocz(ctx, sizeof(*rd));+ if (!rd) {+ if (i == 1)+ promise_reaction_data_free(ctx->rt, rd_array[0]);+ return -1;+ }+ for(j = 0; j < 2; j++)+ rd->resolving_funcs[j] = JS_DupValue(ctx, cap_resolving_funcs[j]);+ handler = resolve_reject[i];+ if (!JS_IsFunction(ctx, handler))+ handler = JS_UNDEFINED;+ rd->handler = JS_DupValue(ctx, handler);+ rd_array[i] = rd;+ }++ if (s->promise_state == JS_PROMISE_PENDING) {+ for(i = 0; i < 2; i++)+ list_add_tail(&rd_array[i]->link, &s->promise_reactions[i]);+ } else {+ JSValueConst args[5];+ if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) {+ JSRuntime *rt = ctx->rt;+ if (rt->host_promise_rejection_tracker) {+ rt->host_promise_rejection_tracker(ctx, promise, s->promise_result,+ TRUE, rt->host_promise_rejection_tracker_opaque);+ }+ }+ i = s->promise_state - JS_PROMISE_FULFILLED;+ rd = rd_array[i];+ args[0] = rd->resolving_funcs[0];+ args[1] = rd->resolving_funcs[1];+ args[2] = rd->handler;+ args[3] = JS_NewBool(ctx, i);+ args[4] = s->promise_result;+ JS_EnqueueJob(ctx, promise_reaction_job, 5, args);+ for(i = 0; i < 2; i++)+ promise_reaction_data_free(ctx->rt, rd_array[i]);+ }+ s->is_handled = TRUE;+ return 0;+}++static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue ctor, result_promise, resolving_funcs[2];+ JSPromiseData *s;+ int i, ret;++ s = JS_GetOpaque2(ctx, this_val, JS_CLASS_PROMISE);+ if (!s)+ return JS_EXCEPTION;++ ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);+ if (JS_IsException(ctor))+ return ctor;+ result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor);+ JS_FreeValue(ctx, ctor);+ if (JS_IsException(result_promise))+ return result_promise;+ ret = perform_promise_then(ctx, this_val, argv,+ (JSValueConst *)resolving_funcs);+ for(i = 0; i < 2; i++)+ JS_FreeValue(ctx, resolving_funcs[i]);+ if (ret) {+ JS_FreeValue(ctx, result_promise);+ return JS_EXCEPTION;+ }+ return result_promise;+}++static JSValue js_promise_catch(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValueConst args[2];+ args[0] = JS_UNDEFINED;+ args[1] = argv[0];+ return JS_Invoke(ctx, this_val, JS_ATOM_then, 2, args);+}++static JSValue js_promise_finally_value_thunk(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv,+ int magic, JSValue *func_data)+{+ return JS_DupValue(ctx, func_data[0]);+}++static JSValue js_promise_finally_thrower(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv,+ int magic, JSValue *func_data)+{+ return JS_Throw(ctx, JS_DupValue(ctx, func_data[0]));+}++static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv,+ int magic, JSValue *func_data)+{+ JSValueConst ctor = func_data[0];+ JSValueConst onFinally = func_data[1];+ JSValue res, promise, ret, then_func;++ res = JS_Call(ctx, onFinally, JS_UNDEFINED, 0, NULL);+ if (JS_IsException(res))+ return res;+ promise = js_promise_resolve(ctx, ctor, 1, (JSValueConst *)&res, 0);+ JS_FreeValue(ctx, res);+ if (JS_IsException(promise))+ return promise;+ if (magic == 0) {+ then_func = JS_NewCFunctionData(ctx, js_promise_finally_value_thunk, 0,+ 0, 1, argv);+ } else {+ then_func = JS_NewCFunctionData(ctx, js_promise_finally_thrower, 0,+ 0, 1, argv);+ }+ if (JS_IsException(then_func)) {+ JS_FreeValue(ctx, promise);+ return then_func;+ }+ ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, (JSValueConst *)&then_func);+ JS_FreeValue(ctx, then_func);+ return ret;+}++static JSValue js_promise_finally(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValueConst onFinally = argv[0];+ JSValue ctor, ret;+ JSValue then_funcs[2];+ JSValueConst func_data[2];+ int i;++ ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);+ if (JS_IsException(ctor))+ return ctor;+ if (!JS_IsFunction(ctx, onFinally)) {+ then_funcs[0] = JS_DupValue(ctx, onFinally);+ then_funcs[1] = JS_DupValue(ctx, onFinally);+ } else {+ func_data[0] = ctor;+ func_data[1] = onFinally;+ for(i = 0; i < 2; i++) {+ then_funcs[i] = JS_NewCFunctionData(ctx, js_promise_then_finally_func, 1, i, 2, func_data);+ if (JS_IsException(then_funcs[i])) {+ if (i == 1)+ JS_FreeValue(ctx, then_funcs[0]);+ JS_FreeValue(ctx, ctor);+ return JS_EXCEPTION;+ }+ }+ }+ JS_FreeValue(ctx, ctor);+ ret = JS_Invoke(ctx, this_val, JS_ATOM_then, 2, (JSValueConst *)then_funcs);+ JS_FreeValue(ctx, then_funcs[0]);+ JS_FreeValue(ctx, then_funcs[1]);+ return ret;+}++static const JSCFunctionListEntry js_promise_funcs[] = {+ JS_CFUNC_MAGIC_DEF("resolve", 1, js_promise_resolve, 0 ),+ JS_CFUNC_MAGIC_DEF("reject", 1, js_promise_resolve, 1 ),+ JS_CFUNC_MAGIC_DEF("all", 1, js_promise_all, PROMISE_MAGIC_all ),+ JS_CFUNC_MAGIC_DEF("allSettled", 1, js_promise_all, PROMISE_MAGIC_allSettled ),+ JS_CFUNC_MAGIC_DEF("any", 1, js_promise_all, PROMISE_MAGIC_any ),+ JS_CFUNC_DEF("race", 1, js_promise_race ),+ //JS_CFUNC_DEF("__newPromiseCapability", 1, js_promise___newPromiseCapability ),+ JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL),+};++static const JSCFunctionListEntry js_promise_proto_funcs[] = {+ JS_CFUNC_DEF("then", 2, js_promise_then ),+ JS_CFUNC_DEF("catch", 1, js_promise_catch ),+ JS_CFUNC_DEF("finally", 1, js_promise_finally ),+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Promise", JS_PROP_CONFIGURABLE ),+};++/* AsyncFunction */+static const JSCFunctionListEntry js_async_function_proto_funcs[] = {+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncFunction", JS_PROP_CONFIGURABLE ),+};++static JSValue js_async_from_sync_iterator_unwrap(JSContext *ctx,+ JSValueConst this_val,+ int argc, JSValueConst *argv,+ int magic, JSValue *func_data)+{+ return js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]),+ JS_ToBool(ctx, func_data[0]));+}++static JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx,+ BOOL done)+{+ JSValueConst func_data[1];++ func_data[0] = (JSValueConst)JS_NewBool(ctx, done);+ return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_unwrap,+ 1, 0, 1, func_data);+}++/* AsyncIteratorPrototype */++static const JSCFunctionListEntry js_async_iterator_proto_funcs[] = {+ JS_CFUNC_DEF("[Symbol.asyncIterator]", 0, js_iterator_proto_iterator ),+};++/* AsyncFromSyncIteratorPrototype */++typedef struct JSAsyncFromSyncIteratorData {+ JSValue sync_iter;+ JSValue next_method;+} JSAsyncFromSyncIteratorData;++static void js_async_from_sync_iterator_finalizer(JSRuntime *rt, JSValue val)+{+ JSAsyncFromSyncIteratorData *s =+ JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);+ if (s) {+ JS_FreeValueRT(rt, s->sync_iter);+ JS_FreeValueRT(rt, s->next_method);+ js_free_rt(rt, s);+ }+}++static void js_async_from_sync_iterator_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func)+{+ JSAsyncFromSyncIteratorData *s =+ JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);+ if (s) {+ JS_MarkValue(rt, s->sync_iter, mark_func);+ JS_MarkValue(rt, s->next_method, mark_func);+ }+}++static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx,+ JSValueConst sync_iter)+{+ JSValue async_iter, next_method;+ JSAsyncFromSyncIteratorData *s;++ next_method = JS_GetProperty(ctx, sync_iter, JS_ATOM_next);+ if (JS_IsException(next_method))+ return JS_EXCEPTION;+ async_iter = JS_NewObjectClass(ctx, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);+ if (JS_IsException(async_iter)) {+ JS_FreeValue(ctx, next_method);+ return async_iter;+ }+ s = js_mallocz(ctx, sizeof(*s));+ if (!s) {+ JS_FreeValue(ctx, async_iter);+ JS_FreeValue(ctx, next_method);+ return JS_EXCEPTION;+ }+ s->sync_iter = JS_DupValue(ctx, sync_iter);+ s->next_method = next_method;+ JS_SetOpaque(async_iter, s);+ return async_iter;+}++static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv,+ int magic)+{+ JSValue promise, resolving_funcs[2], value, err, method;+ JSAsyncFromSyncIteratorData *s;+ int done;+ int is_reject;++ promise = JS_NewPromiseCapability(ctx, resolving_funcs);+ if (JS_IsException(promise))+ return JS_EXCEPTION;+ s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);+ if (!s) {+ JS_ThrowTypeError(ctx, "not an Async-from-Sync Iterator");+ goto reject;+ }++ if (magic == GEN_MAGIC_NEXT) {+ method = JS_DupValue(ctx, s->next_method);+ } else {+ method = JS_GetProperty(ctx, s->sync_iter,+ magic == GEN_MAGIC_RETURN ? JS_ATOM_return :+ JS_ATOM_throw);+ if (JS_IsException(method))+ goto reject;+ if (JS_IsUndefined(method) || JS_IsNull(method)) {+ if (magic == GEN_MAGIC_RETURN) {+ err = js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), TRUE);+ is_reject = 0;+ } else {+ err = JS_DupValue(ctx, argv[0]);+ is_reject = 1;+ }+ goto done_resolve;+ }+ }+ value = JS_IteratorNext2(ctx, s->sync_iter, method,+ argc >= 1 ? 1 : 0, argv, &done);+ JS_FreeValue(ctx, method);+ if (JS_IsException(value))+ goto reject;+ if (done == 2) {+ JSValue obj = value;+ value = JS_IteratorGetCompleteValue(ctx, obj, &done);+ JS_FreeValue(ctx, obj);+ if (JS_IsException(value))+ goto reject;+ }++ if (JS_IsException(value)) {+ JSValue res2;+ reject:+ err = JS_GetException(ctx);+ is_reject = 1;+ done_resolve:+ res2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED,+ 1, (JSValueConst *)&err);+ JS_FreeValue(ctx, err);+ JS_FreeValue(ctx, res2);+ JS_FreeValue(ctx, resolving_funcs[0]);+ JS_FreeValue(ctx, resolving_funcs[1]);+ return promise;+ }+ {+ JSValue value_wrapper_promise, resolve_reject[2];+ int res;++ value_wrapper_promise = js_promise_resolve(ctx, ctx->promise_ctor,+ 1, (JSValueConst *)&value, 0);+ if (JS_IsException(value_wrapper_promise)) {+ JS_FreeValue(ctx, value);+ goto reject;+ }++ resolve_reject[0] =+ js_async_from_sync_iterator_unwrap_func_create(ctx, done);+ if (JS_IsException(resolve_reject[0])) {+ JS_FreeValue(ctx, value_wrapper_promise);+ goto fail;+ }+ JS_FreeValue(ctx, value);+ resolve_reject[1] = JS_UNDEFINED;++ res = perform_promise_then(ctx, value_wrapper_promise,+ (JSValueConst *)resolve_reject,+ (JSValueConst *)resolving_funcs);+ JS_FreeValue(ctx, resolve_reject[0]);+ JS_FreeValue(ctx, value_wrapper_promise);+ JS_FreeValue(ctx, resolving_funcs[0]);+ JS_FreeValue(ctx, resolving_funcs[1]);+ if (res) {+ JS_FreeValue(ctx, promise);+ return JS_EXCEPTION;+ }+ }+ return promise;+ fail:+ JS_FreeValue(ctx, value);+ JS_FreeValue(ctx, resolving_funcs[0]);+ JS_FreeValue(ctx, resolving_funcs[1]);+ JS_FreeValue(ctx, promise);+ return JS_EXCEPTION;+}++static const JSCFunctionListEntry js_async_from_sync_iterator_proto_funcs[] = {+ JS_CFUNC_MAGIC_DEF("next", 1, js_async_from_sync_iterator_next, GEN_MAGIC_NEXT ),+ JS_CFUNC_MAGIC_DEF("return", 1, js_async_from_sync_iterator_next, GEN_MAGIC_RETURN ),+ JS_CFUNC_MAGIC_DEF("throw", 1, js_async_from_sync_iterator_next, GEN_MAGIC_THROW ),+};++/* AsyncGeneratorFunction */++static const JSCFunctionListEntry js_async_generator_function_proto_funcs[] = {+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGeneratorFunction", JS_PROP_CONFIGURABLE ),+};++/* AsyncGenerator prototype */++static const JSCFunctionListEntry js_async_generator_proto_funcs[] = {+ JS_CFUNC_MAGIC_DEF("next", 1, js_async_generator_next, GEN_MAGIC_NEXT ),+ JS_CFUNC_MAGIC_DEF("return", 1, js_async_generator_next, GEN_MAGIC_RETURN ),+ JS_CFUNC_MAGIC_DEF("throw", 1, js_async_generator_next, GEN_MAGIC_THROW ),+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGenerator", JS_PROP_CONFIGURABLE ),+};++static JSClassShortDef const js_async_class_def[] = {+ { JS_ATOM_Promise, js_promise_finalizer, js_promise_mark }, /* JS_CLASS_PROMISE */+ { JS_ATOM_PromiseResolveFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_RESOLVE_FUNCTION */+ { JS_ATOM_PromiseRejectFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_REJECT_FUNCTION */+ { JS_ATOM_AsyncFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_FUNCTION */+ { JS_ATOM_AsyncFunctionResolve, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_RESOLVE */+ { JS_ATOM_AsyncFunctionReject, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_REJECT */+ { JS_ATOM_empty_string, js_async_from_sync_iterator_finalizer, js_async_from_sync_iterator_mark }, /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */+ { JS_ATOM_AsyncGeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_GENERATOR_FUNCTION */+ { JS_ATOM_AsyncGenerator, js_async_generator_finalizer, js_async_generator_mark }, /* JS_CLASS_ASYNC_GENERATOR */+};++void JS_AddIntrinsicPromise(JSContext *ctx)+{+ JSRuntime *rt = ctx->rt;+ JSValue obj1;++ if (!JS_IsRegisteredClass(rt, JS_CLASS_PROMISE)) {+ init_class_range(rt, js_async_class_def, JS_CLASS_PROMISE,+ countof(js_async_class_def));+ rt->class_array[JS_CLASS_PROMISE_RESOLVE_FUNCTION].call = js_promise_resolve_function_call;+ rt->class_array[JS_CLASS_PROMISE_REJECT_FUNCTION].call = js_promise_resolve_function_call;+ rt->class_array[JS_CLASS_ASYNC_FUNCTION].call = js_async_function_call;+ rt->class_array[JS_CLASS_ASYNC_FUNCTION_RESOLVE].call = js_async_function_resolve_call;+ rt->class_array[JS_CLASS_ASYNC_FUNCTION_REJECT].call = js_async_function_resolve_call;+ rt->class_array[JS_CLASS_ASYNC_GENERATOR_FUNCTION].call = js_async_generator_function_call;+ }++ /* Promise */+ ctx->class_proto[JS_CLASS_PROMISE] = JS_NewObject(ctx);+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_PROMISE],+ js_promise_proto_funcs,+ countof(js_promise_proto_funcs));+ obj1 = JS_NewCFunction2(ctx, js_promise_constructor, "Promise", 1,+ JS_CFUNC_constructor, 0);+ ctx->promise_ctor = JS_DupValue(ctx, obj1);+ JS_SetPropertyFunctionList(ctx, obj1,+ js_promise_funcs,+ countof(js_promise_funcs));+ JS_NewGlobalCConstructor2(ctx, obj1, "Promise",+ ctx->class_proto[JS_CLASS_PROMISE]);++ /* AsyncFunction */+ ctx->class_proto[JS_CLASS_ASYNC_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto);+ obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor,+ "AsyncFunction", 1,+ JS_CFUNC_constructor_or_func_magic, JS_FUNC_ASYNC,+ ctx->function_ctor);+ JS_SetPropertyFunctionList(ctx,+ ctx->class_proto[JS_CLASS_ASYNC_FUNCTION],+ js_async_function_proto_funcs,+ countof(js_async_function_proto_funcs));+ JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_FUNCTION],+ 0, JS_PROP_CONFIGURABLE);+ JS_FreeValue(ctx, obj1);++ /* AsyncIteratorPrototype */+ ctx->async_iterator_proto = JS_NewObject(ctx);+ JS_SetPropertyFunctionList(ctx, ctx->async_iterator_proto,+ js_async_iterator_proto_funcs,+ countof(js_async_iterator_proto_funcs));++ /* AsyncFromSyncIteratorPrototype */+ ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR] =+ JS_NewObjectProto(ctx, ctx->async_iterator_proto);+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR],+ js_async_from_sync_iterator_proto_funcs,+ countof(js_async_from_sync_iterator_proto_funcs));++ /* AsyncGeneratorPrototype */+ ctx->class_proto[JS_CLASS_ASYNC_GENERATOR] =+ JS_NewObjectProto(ctx, ctx->async_iterator_proto);+ JS_SetPropertyFunctionList(ctx,+ ctx->class_proto[JS_CLASS_ASYNC_GENERATOR],+ js_async_generator_proto_funcs,+ countof(js_async_generator_proto_funcs));++ /* AsyncGeneratorFunction */+ ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION] =+ JS_NewObjectProto(ctx, ctx->function_proto);+ obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor,+ "AsyncGeneratorFunction", 1,+ JS_CFUNC_constructor_or_func_magic,+ JS_FUNC_ASYNC_GENERATOR,+ ctx->function_ctor);+ JS_SetPropertyFunctionList(ctx,+ ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],+ js_async_generator_function_proto_funcs,+ countof(js_async_generator_function_proto_funcs));+ JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],+ ctx->class_proto[JS_CLASS_ASYNC_GENERATOR],+ JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE);+ JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],+ 0, JS_PROP_CONFIGURABLE);+ JS_FreeValue(ctx, obj1);+}++/* URI handling */++static int string_get_hex(JSString *p, int k, int n) {+ int c = 0, h;+ while (n-- > 0) {+ if ((h = from_hex(string_get(p, k++))) < 0)+ return -1;+ c = (c << 4) | h;+ }+ return c;+}++static int isURIReserved(int c) {+ return c < 0x100 && memchr(";/?:@&=+$,#", c, sizeof(";/?:@&=+$,#") - 1) != NULL;+}++static int __attribute__((format(printf, 2, 3))) js_throw_URIError(JSContext *ctx, const char *fmt, ...)+{+ va_list ap;++ va_start(ap, fmt);+ JS_ThrowError(ctx, JS_URI_ERROR, fmt, ap);+ va_end(ap);+ return -1;+}++static int hex_decode(JSContext *ctx, JSString *p, int k) {+ int c;++ if (k >= p->len || string_get(p, k) != '%')+ return js_throw_URIError(ctx, "expecting %%");+ if (k + 2 >= p->len || (c = string_get_hex(p, k + 1, 2)) < 0)+ return js_throw_URIError(ctx, "expecting hex digit");++ return c;+}++static JSValue js_global_decodeURI(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int isComponent)+{+ JSValue str;+ StringBuffer b_s, *b = &b_s;+ JSString *p;+ int k, c, c1, n, c_min;++ str = JS_ToString(ctx, argv[0]);+ if (JS_IsException(str))+ return str;++ string_buffer_init(ctx, b, 0);++ p = JS_VALUE_GET_STRING(str);+ for (k = 0; k < p->len;) {+ c = string_get(p, k);+ if (c == '%') {+ c = hex_decode(ctx, p, k);+ if (c < 0)+ goto fail;+ k += 3;+ if (c < 0x80) {+ if (!isComponent && isURIReserved(c)) {+ c = '%';+ k -= 2;+ }+ } else {+ /* Decode URI-encoded UTF-8 sequence */+ if (c >= 0xc0 && c <= 0xdf) {+ n = 1;+ c_min = 0x80;+ c &= 0x1f;+ } else if (c >= 0xe0 && c <= 0xef) {+ n = 2;+ c_min = 0x800;+ c &= 0xf;+ } else if (c >= 0xf0 && c <= 0xf7) {+ n = 3;+ c_min = 0x10000;+ c &= 0x7;+ } else {+ n = 0;+ c_min = 1;+ c = 0;+ }+ while (n-- > 0) {+ c1 = hex_decode(ctx, p, k);+ if (c1 < 0)+ goto fail;+ k += 3;+ if ((c1 & 0xc0) != 0x80) {+ c = 0;+ break;+ }+ c = (c << 6) | (c1 & 0x3f);+ }+ if (c < c_min || c > 0x10FFFF ||+ (c >= 0xd800 && c < 0xe000)) {+ js_throw_URIError(ctx, "malformed UTF-8");+ goto fail;+ }+ }+ } else {+ k++;+ }+ string_buffer_putc(b, c);+ }+ JS_FreeValue(ctx, str);+ return string_buffer_end(b);++fail:+ JS_FreeValue(ctx, str);+ string_buffer_free(b);+ return JS_EXCEPTION;+}++static int isUnescaped(int c) {+ static char const unescaped_chars[] =+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"+ "abcdefghijklmnopqrstuvwxyz"+ "0123456789"+ "@*_+-./";+ return c < 0x100 &&+ memchr(unescaped_chars, c, sizeof(unescaped_chars) - 1);+}++static int isURIUnescaped(int c, int isComponent) {+ return c < 0x100 &&+ ((c >= 0x61 && c <= 0x7a) ||+ (c >= 0x41 && c <= 0x5a) ||+ (c >= 0x30 && c <= 0x39) ||+ memchr("-_.!~*'()", c, sizeof("-_.!~*'()") - 1) != NULL ||+ (!isComponent && isURIReserved(c)));+}++static int encodeURI_hex(StringBuffer *b, int c) {+ uint8_t buf[6];+ int n = 0;+ const char *hex = "0123456789ABCDEF";++ buf[n++] = '%';+ if (c >= 256) {+ buf[n++] = 'u';+ buf[n++] = hex[(c >> 12) & 15];+ buf[n++] = hex[(c >> 8) & 15];+ }+ buf[n++] = hex[(c >> 4) & 15];+ buf[n++] = hex[(c >> 0) & 15];+ return string_buffer_write8(b, buf, n);+}++static JSValue js_global_encodeURI(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv,+ int isComponent)+{+ JSValue str;+ StringBuffer b_s, *b = &b_s;+ JSString *p;+ int k, c, c1;++ str = JS_ToString(ctx, argv[0]);+ if (JS_IsException(str))+ return str;++ p = JS_VALUE_GET_STRING(str);+ string_buffer_init(ctx, b, p->len);+ for (k = 0; k < p->len;) {+ c = string_get(p, k);+ k++;+ if (isURIUnescaped(c, isComponent)) {+ string_buffer_putc16(b, c);+ } else {+ if (c >= 0xdc00 && c <= 0xdfff) {+ js_throw_URIError(ctx, "invalid character");+ goto fail;+ } else if (c >= 0xd800 && c <= 0xdbff) {+ if (k >= p->len) {+ js_throw_URIError(ctx, "expecting surrogate pair");+ goto fail;+ }+ c1 = string_get(p, k);+ k++;+ if (c1 < 0xdc00 || c1 > 0xdfff) {+ js_throw_URIError(ctx, "expecting surrogate pair");+ goto fail;+ }+ c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;+ }+ if (c < 0x80) {+ encodeURI_hex(b, c);+ } else {+ /* XXX: use C UTF-8 conversion ? */+ if (c < 0x800) {+ encodeURI_hex(b, (c >> 6) | 0xc0);+ } else {+ if (c < 0x10000) {+ encodeURI_hex(b, (c >> 12) | 0xe0);+ } else {+ encodeURI_hex(b, (c >> 18) | 0xf0);+ encodeURI_hex(b, ((c >> 12) & 0x3f) | 0x80);+ }+ encodeURI_hex(b, ((c >> 6) & 0x3f) | 0x80);+ }+ encodeURI_hex(b, (c & 0x3f) | 0x80);+ }+ }+ }+ JS_FreeValue(ctx, str);+ return string_buffer_end(b);++fail:+ JS_FreeValue(ctx, str);+ string_buffer_free(b);+ return JS_EXCEPTION;+}++static JSValue js_global_escape(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue str;+ StringBuffer b_s, *b = &b_s;+ JSString *p;+ int i, len, c;++ str = JS_ToString(ctx, argv[0]);+ if (JS_IsException(str))+ return str;++ p = JS_VALUE_GET_STRING(str);+ string_buffer_init(ctx, b, p->len);+ for (i = 0, len = p->len; i < len; i++) {+ c = string_get(p, i);+ if (isUnescaped(c)) {+ string_buffer_putc16(b, c);+ } else {+ encodeURI_hex(b, c);+ }+ }+ JS_FreeValue(ctx, str);+ return string_buffer_end(b);+}++static JSValue js_global_unescape(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue str;+ StringBuffer b_s, *b = &b_s;+ JSString *p;+ int i, len, c, n;++ str = JS_ToString(ctx, argv[0]);+ if (JS_IsException(str))+ return str;++ string_buffer_init(ctx, b, 0);+ p = JS_VALUE_GET_STRING(str);+ for (i = 0, len = p->len; i < len; i++) {+ c = string_get(p, i);+ if (c == '%') {+ if (i + 6 <= len+ && string_get(p, i + 1) == 'u'+ && (n = string_get_hex(p, i + 2, 4)) >= 0) {+ c = n;+ i += 6 - 1;+ } else+ if (i + 3 <= len+ && (n = string_get_hex(p, i + 1, 2)) >= 0) {+ c = n;+ i += 3 - 1;+ }+ }+ string_buffer_putc16(b, c);+ }+ JS_FreeValue(ctx, str);+ return string_buffer_end(b);+}++/* global object */++static const JSCFunctionListEntry js_global_funcs[] = {+ JS_CFUNC_DEF("parseInt", 2, js_parseInt ),+ JS_CFUNC_DEF("parseFloat", 1, js_parseFloat ),+ JS_CFUNC_DEF("isNaN", 1, js_global_isNaN ),+ JS_CFUNC_DEF("isFinite", 1, js_global_isFinite ),++ JS_CFUNC_MAGIC_DEF("decodeURI", 1, js_global_decodeURI, 0 ),+ JS_CFUNC_MAGIC_DEF("decodeURIComponent", 1, js_global_decodeURI, 1 ),+ JS_CFUNC_MAGIC_DEF("encodeURI", 1, js_global_encodeURI, 0 ),+ JS_CFUNC_MAGIC_DEF("encodeURIComponent", 1, js_global_encodeURI, 1 ),+ JS_CFUNC_DEF("escape", 1, js_global_escape ),+ JS_CFUNC_DEF("unescape", 1, js_global_unescape ),+ JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ),+ JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),+ JS_PROP_UNDEFINED_DEF("undefined", 0 ),++ /* for the 'Date' implementation */+ JS_CFUNC_DEF("__date_clock", 0, js___date_clock ),+ //JS_CFUNC_DEF("__date_now", 0, js___date_now ),+ //JS_CFUNC_DEF("__date_getTimezoneOffset", 1, js___date_getTimezoneOffset ),+ //JS_CFUNC_DEF("__date_create", 3, js___date_create ),+};++/* Date */++static int64_t math_mod(int64_t a, int64_t b) {+ /* return positive modulo */+ int64_t m = a % b;+ return m + (m < 0) * b;+}++static int64_t floor_div(int64_t a, int64_t b) {+ /* integer division rounding toward -Infinity */+ int64_t m = a % b;+ return (a - (m + (m < 0) * b)) / b;+}++static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv);++static __exception int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst this_val)+{+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {+ JSObject *p = JS_VALUE_GET_OBJ(this_val);+ if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data))+ return JS_ToFloat64(ctx, valp, p->u.object_data);+ }+ JS_ThrowTypeError(ctx, "not a Date object");+ return -1;+}++static JSValue JS_SetThisTimeValue(JSContext *ctx, JSValueConst this_val, double v)+{+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {+ JSObject *p = JS_VALUE_GET_OBJ(this_val);+ if (p->class_id == JS_CLASS_DATE) {+ JS_FreeValue(ctx, p->u.object_data);+ p->u.object_data = JS_NewFloat64(ctx, v);+ return JS_DupValue(ctx, p->u.object_data);+ }+ }+ return JS_ThrowTypeError(ctx, "not a Date object");+}++static int64_t days_from_year(int64_t y) {+ return 365 * (y - 1970) + floor_div(y - 1969, 4) -+ floor_div(y - 1901, 100) + floor_div(y - 1601, 400);+}++static int64_t days_in_year(int64_t y) {+ return 365 + !(y % 4) - !(y % 100) + !(y % 400);+}++/* return the year, update days */+static int64_t year_from_days(int64_t *days) {+ int64_t y, d1, nd, d = *days;+ y = floor_div(d * 10000, 3652425) + 1970;+ /* the initial approximation is very good, so only a few+ iterations are necessary */+ for(;;) {+ d1 = d - days_from_year(y);+ if (d1 < 0) {+ y--;+ d1 += days_in_year(y);+ } else {+ nd = days_in_year(y);+ if (d1 < nd)+ break;+ d1 -= nd;+ y++;+ }+ }+ *days = d1;+ return y;+}++static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };+static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";+static char const day_names[] = "SunMonTueWedThuFriSat";++static __exception int get_date_fields(JSContext *ctx, JSValueConst obj,+ double fields[9], int is_local, int force)+{+ double dval;+ int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0;++ if (JS_ThisTimeValue(ctx, &dval, obj))+ return -1;++ if (isnan(dval)) {+ if (!force)+ return FALSE; /* NaN */+ d = 0; /* initialize all fields to 0 */+ } else {+ d = dval;+ if (is_local) {+ tz = -getTimezoneOffset(d);+ d += tz * 60000;+ }+ }++ /* result is >= 0, we can use % */+ h = math_mod(d, 86400000);+ days = (d - h) / 86400000;+ ms = h % 1000;+ h = (h - ms) / 1000;+ s = h % 60;+ h = (h - s) / 60;+ m = h % 60;+ h = (h - m) / 60;+ wd = math_mod(days + 4, 7); /* week day */+ y = year_from_days(&days);++ for(i = 0; i < 11; i++) {+ md = month_days[i];+ if (i == 1)+ md += days_in_year(y) - 365;+ if (days < md)+ break;+ days -= md;+ }+ fields[0] = y;+ fields[1] = i;+ fields[2] = days + 1;+ fields[3] = h;+ fields[4] = m;+ fields[5] = s;+ fields[6] = ms;+ fields[7] = wd;+ fields[8] = tz;+ return TRUE;+}++static double time_clip(double t) {+ if (t >= -8.64e15 && t <= 8.64e15)+ return trunc(t) + 0.0; /* convert -0 to +0 */+ else+ return NAN;+}++/* The spec mandates the use of 'double' and it fixes the order+ of the operations */+static double set_date_fields(double fields[], int is_local) {+ int64_t y;+ double days, d, h, m1;+ int i, m, md;+ + m1 = fields[1];+ m = fmod(m1, 12);+ if (m < 0)+ m += 12;+ y = (int64_t)(fields[0] + floor(m1 / 12));+ days = days_from_year(y);++ for(i = 0; i < m; i++) {+ md = month_days[i];+ if (i == 1)+ md += days_in_year(y) - 365;+ days += md;+ }+ days += fields[2] - 1;+ h = fields[3] * 3600000 + fields[4] * 60000 + + fields[5] * 1000 + fields[6];+ d = days * 86400000 + h;+ if (is_local)+ d += getTimezoneOffset(d) * 60000;+ return time_clip(d);+}++static JSValue get_date_field(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ // get_date_field(obj, n, is_local)+ double fields[9];+ int res, n, is_local;++ is_local = magic & 0x0F;+ n = (magic >> 4) & 0x0F;+ res = get_date_fields(ctx, this_val, fields, is_local, 0);+ if (res < 0)+ return JS_EXCEPTION;+ if (!res)+ return JS_NAN;++ if (magic & 0x100) { // getYear+ fields[0] -= 1900;+ }+ return JS_NewFloat64(ctx, fields[n]);+}++static JSValue set_date_field(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ // _field(obj, first_field, end_field, args, is_local)+ double fields[9];+ int res, first_field, end_field, is_local, i, n;+ double d, a;++ d = NAN;+ first_field = (magic >> 8) & 0x0F;+ end_field = (magic >> 4) & 0x0F;+ is_local = magic & 0x0F;++ res = get_date_fields(ctx, this_val, fields, is_local, first_field == 0);+ if (res < 0)+ return JS_EXCEPTION;+ if (res && argc > 0) {+ n = end_field - first_field;+ if (argc < n)+ n = argc;+ for(i = 0; i < n; i++) {+ if (JS_ToFloat64(ctx, &a, argv[i]))+ return JS_EXCEPTION;+ if (!isfinite(a))+ goto done;+ fields[first_field + i] = trunc(a);+ }+ d = set_date_fields(fields, is_local);+ }+done:+ return JS_SetThisTimeValue(ctx, this_val, d);+}++/* fmt:+ 0: toUTCString: "Tue, 02 Jan 2018 23:04:46 GMT"+ 1: toString: "Wed Jan 03 2018 00:05:22 GMT+0100 (CET)"+ 2: toISOString: "2018-01-02T23:02:56.927Z"+ 3: toLocaleString: "1/2/2018, 11:40:40 PM"+ part: 1=date, 2=time 3=all+ XXX: should use a variant of strftime().+ */+static JSValue get_date_string(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ // _string(obj, fmt, part)+ char buf[64];+ double fields[9];+ int res, fmt, part, pos;+ int y, mon, d, h, m, s, ms, wd, tz;++ fmt = (magic >> 4) & 0x0F;+ part = magic & 0x0F;++ res = get_date_fields(ctx, this_val, fields, fmt & 1, 0);+ if (res < 0)+ return JS_EXCEPTION;+ if (!res) {+ if (fmt == 2)+ return JS_ThrowRangeError(ctx, "Date value is NaN");+ else+ return JS_NewString(ctx, "Invalid Date");+ }++ y = fields[0];+ mon = fields[1];+ d = fields[2];+ h = fields[3];+ m = fields[4];+ s = fields[5];+ ms = fields[6];+ wd = fields[7];+ tz = fields[8];++ pos = 0;++ if (part & 1) { /* date part */+ switch(fmt) {+ case 0:+ pos += snprintf(buf + pos, sizeof(buf) - pos,+ "%.3s, %02d %.3s %0*d ",+ day_names + wd * 3, d,+ month_names + mon * 3, 4 + (y < 0), y);+ break;+ case 1:+ pos += snprintf(buf + pos, sizeof(buf) - pos,+ "%.3s %.3s %02d %0*d",+ day_names + wd * 3,+ month_names + mon * 3, d, 4 + (y < 0), y);+ if (part == 3) {+ buf[pos++] = ' ';+ }+ break;+ case 2:+ if (y >= 0 && y <= 9999) {+ pos += snprintf(buf + pos, sizeof(buf) - pos,+ "%04d", y);+ } else {+ pos += snprintf(buf + pos, sizeof(buf) - pos,+ "%+07d", y);+ }+ pos += snprintf(buf + pos, sizeof(buf) - pos,+ "-%02d-%02dT", mon + 1, d);+ break;+ case 3:+ pos += snprintf(buf + pos, sizeof(buf) - pos,+ "%02d/%02d/%0*d", mon + 1, d, 4 + (y < 0), y);+ if (part == 3) {+ buf[pos++] = ',';+ buf[pos++] = ' ';+ }+ break;+ }+ }+ if (part & 2) { /* time part */+ switch(fmt) {+ case 0:+ pos += snprintf(buf + pos, sizeof(buf) - pos,+ "%02d:%02d:%02d GMT", h, m, s);+ break;+ case 1:+ pos += snprintf(buf + pos, sizeof(buf) - pos,+ "%02d:%02d:%02d GMT", h, m, s);+ if (tz < 0) {+ buf[pos++] = '-';+ tz = -tz;+ } else {+ buf[pos++] = '+';+ }+ /* tz is >= 0, can use % */+ pos += snprintf(buf + pos, sizeof(buf) - pos,+ "%02d%02d", tz / 60, tz % 60);+ /* XXX: tack the time zone code? */+ break;+ case 2:+ pos += snprintf(buf + pos, sizeof(buf) - pos,+ "%02d:%02d:%02d.%03dZ", h, m, s, ms);+ break;+ case 3:+ pos += snprintf(buf + pos, sizeof(buf) - pos,+ "%02d:%02d:%02d %cM", (h + 1) % 12 - 1, m, s,+ (h < 12) ? 'A' : 'P');+ break;+ }+ }+ return JS_NewStringLen(ctx, buf, pos);+}++/* OS dependent: return the UTC time in ms since 1970. */+static int64_t date_now(void) {+ struct timeval tv;+ gettimeofday(&tv, NULL);+ return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);+}++static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target,+ int argc, JSValueConst *argv)+{+ // Date(y, mon, d, h, m, s, ms)+ JSValue rv;+ int i, n;+ double a, val;++ if (JS_IsUndefined(new_target)) {+ /* invoked as function */+ argc = 0;+ }+ n = argc;+ if (n == 0) {+ val = date_now();+ } else if (n == 1) {+ JSValue v, dv;+ if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) {+ JSObject *p = JS_VALUE_GET_OBJ(argv[0]);+ if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) {+ if (JS_ToFloat64(ctx, &val, p->u.object_data))+ return JS_EXCEPTION;+ val = time_clip(val);+ goto has_val;+ }+ }+ v = JS_ToPrimitive(ctx, argv[0], HINT_NONE);+ if (JS_IsString(v)) {+ dv = js_Date_parse(ctx, JS_UNDEFINED, 1, (JSValueConst *)&v);+ JS_FreeValue(ctx, v);+ if (JS_IsException(dv))+ return JS_EXCEPTION;+ if (JS_ToFloat64Free(ctx, &val, dv))+ return JS_EXCEPTION;+ } else {+ if (JS_ToFloat64Free(ctx, &val, v))+ return JS_EXCEPTION;+ }+ val = time_clip(val);+ } else {+ double fields[] = { 0, 0, 1, 0, 0, 0, 0 };+ if (n > 7)+ n = 7;+ for(i = 0; i < n; i++) {+ if (JS_ToFloat64(ctx, &a, argv[i]))+ return JS_EXCEPTION;+ if (!isfinite(a))+ break;+ fields[i] = trunc(a);+ if (i == 0 && fields[0] >= 0 && fields[0] < 100)+ fields[0] += 1900;+ }+ val = (i == n) ? set_date_fields(fields, 1) : NAN;+ }+has_val:+#if 0+ JSValueConst args[3];+ args[0] = new_target;+ args[1] = ctx->class_proto[JS_CLASS_DATE];+ args[2] = JS_NewFloat64(ctx, val);+ rv = js___date_create(ctx, JS_UNDEFINED, 3, args);+#else+ rv = js_create_from_ctor(ctx, new_target, JS_CLASS_DATE);+ if (!JS_IsException(rv))+ JS_SetObjectData(ctx, rv, JS_NewFloat64(ctx, val));+#endif+ if (!JS_IsException(rv) && JS_IsUndefined(new_target)) {+ /* invoked as a function, return (new Date()).toString(); */+ JSValue s;+ s = get_date_string(ctx, rv, 0, NULL, 0x13);+ JS_FreeValue(ctx, rv);+ rv = s;+ }+ return rv;+}++static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ // UTC(y, mon, d, h, m, s, ms)+ double fields[] = { 0, 0, 1, 0, 0, 0, 0 };+ int i, n;+ double a;++ n = argc;+ if (n == 0)+ return JS_NAN;+ if (n > 7)+ n = 7;+ for(i = 0; i < n; i++) {+ if (JS_ToFloat64(ctx, &a, argv[i]))+ return JS_EXCEPTION;+ if (!isfinite(a))+ return JS_NAN;+ fields[i] = trunc(a);+ if (i == 0 && fields[0] >= 0 && fields[0] < 100)+ fields[0] += 1900;+ }+ return JS_NewFloat64(ctx, set_date_fields(fields, 0));+}++static void string_skip_spaces(JSString *sp, int *pp) {+ while (*pp < sp->len && string_get(sp, *pp) == ' ')+ *pp += 1;+}++static void string_skip_non_spaces(JSString *sp, int *pp) {+ while (*pp < sp->len && string_get(sp, *pp) != ' ')+ *pp += 1;+}++/* parse a numeric field */+static int string_get_field(JSString *sp, int *pp, int64_t *pval) {+ int64_t v = 0;+ int c, p = *pp;++ /* skip non digits, should only skip spaces? */+ while (p < sp->len) {+ c = string_get(sp, p);+ if (c >= '0' && c <= '9')+ break;+ p++;+ }+ if (p >= sp->len)+ return -1;+ while (p < sp->len) {+ c = string_get(sp, p);+ if (!(c >= '0' && c <= '9'))+ break;+ v = v * 10 + c - '0';+ p++;+ }+ *pval = v;+ *pp = p;+ return 0;+}++/* parse a fixed width numeric field */+static int string_get_digits(JSString *sp, int *pp, int n, int64_t *pval) {+ int64_t v = 0;+ int i, c, p = *pp;++ for(i = 0; i < n; i++) {+ if (p >= sp->len)+ return -1;+ c = string_get(sp, p);+ if (!(c >= '0' && c <= '9'))+ return -1;+ v = v * 10 + c - '0';+ p++;+ }+ *pval = v;+ *pp = p;+ return 0;+}++/* parse a signed numeric field */+static int string_get_signed_field(JSString *sp, int *pp, int64_t *pval) {+ int sgn, res;++ if (*pp >= sp->len)+ return -1;++ sgn = string_get(sp, *pp);+ if (sgn == '-' || sgn == '+')+ *pp += 1;++ res = string_get_field(sp, pp, pval);+ if (res == 0 && sgn == '-')+ *pval = -*pval;+ return res;+}++static int find_abbrev(JSString *sp, int p, const char *list, int count) {+ int n, i;++ if (p + 3 <= sp->len) {+ for (n = 0; n < count; n++) {+ for (i = 0; i < 3; i++) {+ if (string_get(sp, p + i) != month_names[n * 3 + i])+ goto next;+ }+ return n;+ next:;+ }+ }+ return -1;+}++static int string_get_month(JSString *sp, int *pp, int64_t *pval) {+ int n;++ string_skip_spaces(sp, pp);+ n = find_abbrev(sp, *pp, month_names, 12);+ if (n < 0)+ return -1;++ *pval = n;+ *pp += 3;+ return 0;+}++static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ // parse(s)+ JSValue s, rv;+ int64_t fields[] = { 0, 1, 1, 0, 0, 0, 0 };+ double fields1[7];+ int64_t tz, hh, mm;+ double d;+ int p, i, c, sgn;+ JSString *sp;+ BOOL is_local;+ + rv = JS_NAN;++ s = JS_ToString(ctx, argv[0]);+ if (JS_IsException(s))+ return JS_EXCEPTION;+ + sp = JS_VALUE_GET_STRING(s);+ p = 0;+ if (p < sp->len && (((c = string_get(sp, p)) >= '0' && c <= '9') || c == '+' || c == '-')) {+ /* ISO format */+ /* year field can be negative */+ /* XXX: could be stricter */+ if (string_get_signed_field(sp, &p, &fields[0]))+ goto done;++ is_local = TRUE;+ for (i = 1; i < 6; i++) {+ if (string_get_field(sp, &p, &fields[i]))+ break;+ }+ if (i <= 3) {+ /* no time: UTC by default */+ is_local = FALSE;+ } else if (i == 6 && p < sp->len && string_get(sp, p) == '.') {+ /* parse milliseconds as a fractional part, round to nearest */+ /* XXX: the spec does not indicate which rounding should be used */+ int mul = 1000, ms = 0;+ while (++p < sp->len) {+ int c = string_get(sp, p);+ if (!(c >= '0' && c <= '9'))+ break;+ if (mul == 1 && c >= '5')+ ms += 1;+ ms += (c - '0') * (mul /= 10);+ }+ fields[6] = ms;+ }+ fields[1] -= 1;++ /* parse the time zone offset if present: [+-]HH:mm */+ tz = 0;+ if (p < sp->len) {+ sgn = string_get(sp, p);+ if (sgn == '+' || sgn == '-') {+ if (string_get_field(sp, &p, &hh))+ goto done;+ if (string_get_field(sp, &p, &mm))+ goto done;+ tz = hh * 60 + mm;+ if (sgn == '-')+ tz = -tz;+ is_local = FALSE;+ } else if (sgn == 'Z') {+ is_local = FALSE;+ }+ }+ } else {+ /* toString or toUTCString format */+ /* skip the day of the week */+ string_skip_non_spaces(sp, &p);+ string_skip_spaces(sp, &p);+ if (p >= sp->len)+ goto done;+ c = string_get(sp, p);+ if (c >= '0' && c <= '9') {+ /* day of month first */+ if (string_get_field(sp, &p, &fields[2]))+ goto done;+ if (string_get_month(sp, &p, &fields[1]))+ goto done;+ } else {+ /* month first */+ if (string_get_month(sp, &p, &fields[1]))+ goto done;+ if (string_get_field(sp, &p, &fields[2]))+ goto done;+ }+ string_skip_spaces(sp, &p);+ if (string_get_signed_field(sp, &p, &fields[0]))+ goto done;++ /* hour, min, seconds */+ for(i = 0; i < 3; i++) {+ if (string_get_field(sp, &p, &fields[3 + i]))+ goto done;+ }+ // XXX: parse optional milliseconds?++ /* parse the time zone offset if present: [+-]HHmm */+ is_local = FALSE;+ tz = 0;+ for (tz = 0; p < sp->len; p++) {+ sgn = string_get(sp, p);+ if (sgn == '+' || sgn == '-') {+ p++;+ if (string_get_digits(sp, &p, 2, &hh))+ goto done;+ if (string_get_digits(sp, &p, 2, &mm))+ goto done;+ tz = hh * 60 + mm;+ if (sgn == '-')+ tz = -tz;+ break;+ }+ }+ }+ for(i = 0; i < 7; i++)+ fields1[i] = fields[i];+ d = set_date_fields(fields1, is_local) - tz * 60000;+ rv = JS_NewFloat64(ctx, d);++done:+ JS_FreeValue(ctx, s);+ return rv;+}++static JSValue js_Date_now(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ // now()+ return JS_NewInt64(ctx, date_now());+}++static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ // Symbol_toPrimitive(hint)+ JSValueConst obj = this_val;+ JSAtom hint = JS_ATOM_NULL;+ int hint_num;++ if (!JS_IsObject(obj))+ return JS_ThrowTypeErrorNotAnObject(ctx);++ if (JS_IsString(argv[0])) {+ hint = JS_ValueToAtom(ctx, argv[0]);+ if (hint == JS_ATOM_NULL)+ return JS_EXCEPTION;+ JS_FreeAtom(ctx, hint);+ }+ switch (hint) {+ case JS_ATOM_number:+#ifdef CONFIG_BIGNUM+ case JS_ATOM_integer:+#endif+ hint_num = HINT_NUMBER;+ break;+ case JS_ATOM_string:+ case JS_ATOM_default:+ hint_num = HINT_STRING;+ break;+ default:+ return JS_ThrowTypeError(ctx, "invalid hint");+ }+ return JS_ToPrimitive(ctx, obj, hint_num | HINT_FORCE_ORDINARY);+}++static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ // getTimezoneOffset()+ double v;++ if (JS_ThisTimeValue(ctx, &v, this_val))+ return JS_EXCEPTION;+ if (isnan(v))+ return JS_NAN;+ else+ return JS_NewInt64(ctx, getTimezoneOffset((int64_t)trunc(v)));+}++static JSValue js_date_getTime(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ // getTime()+ double v;++ if (JS_ThisTimeValue(ctx, &v, this_val))+ return JS_EXCEPTION;+ return JS_NewFloat64(ctx, v);+}++static JSValue js_date_setTime(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ // setTime(v)+ double v;++ if (JS_ThisTimeValue(ctx, &v, this_val) || JS_ToFloat64(ctx, &v, argv[0]))+ return JS_EXCEPTION;+ return JS_SetThisTimeValue(ctx, this_val, time_clip(v));+}++static JSValue js_date_setYear(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ // setYear(y)+ double y;+ JSValueConst args[1];++ if (JS_ThisTimeValue(ctx, &y, this_val) || JS_ToFloat64(ctx, &y, argv[0]))+ return JS_EXCEPTION;+ y = +y;+ if (isfinite(y)) {+ y = trunc(y);+ if (y >= 0 && y < 100)+ y += 1900;+ }+ args[0] = JS_NewFloat64(ctx, y);+ return set_date_field(ctx, this_val, 1, args, 0x011);+}++static JSValue js_date_toJSON(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ // toJSON(key)+ JSValue obj, tv, method, rv;+ double d;++ rv = JS_EXCEPTION;+ tv = JS_UNDEFINED;++ obj = JS_ToObject(ctx, this_val);+ tv = JS_ToPrimitive(ctx, obj, HINT_NUMBER);+ if (JS_IsException(tv))+ goto exception;+ if (JS_IsNumber(tv)) {+ if (JS_ToFloat64(ctx, &d, tv) < 0)+ goto exception;+ if (!isfinite(d)) {+ rv = JS_NULL;+ goto done;+ }+ }+ method = JS_GetPropertyStr(ctx, obj, "toISOString");+ if (JS_IsException(method))+ goto exception;+ if (!JS_IsFunction(ctx, method)) {+ JS_ThrowTypeError(ctx, "object needs toISOString method");+ JS_FreeValue(ctx, method);+ goto exception;+ }+ rv = JS_CallFree(ctx, method, obj, 0, NULL);+exception:+done:+ JS_FreeValue(ctx, obj);+ JS_FreeValue(ctx, tv);+ return rv;+}++static const JSCFunctionListEntry js_date_funcs[] = {+ JS_CFUNC_DEF("now", 0, js_Date_now ),+ JS_CFUNC_DEF("parse", 1, js_Date_parse ),+ JS_CFUNC_DEF("UTC", 7, js_Date_UTC ),+};++static const JSCFunctionListEntry js_date_proto_funcs[] = {+ JS_CFUNC_DEF("valueOf", 0, js_date_getTime ),+ JS_CFUNC_MAGIC_DEF("toString", 0, get_date_string, 0x13 ),+ JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_date_Symbol_toPrimitive ),+ JS_CFUNC_MAGIC_DEF("toUTCString", 0, get_date_string, 0x03 ),+ JS_ALIAS_DEF("toGMTString", "toUTCString" ),+ JS_CFUNC_MAGIC_DEF("toISOString", 0, get_date_string, 0x23 ),+ JS_CFUNC_MAGIC_DEF("toDateString", 0, get_date_string, 0x11 ),+ JS_CFUNC_MAGIC_DEF("toTimeString", 0, get_date_string, 0x12 ),+ JS_CFUNC_MAGIC_DEF("toLocaleString", 0, get_date_string, 0x33 ),+ JS_CFUNC_MAGIC_DEF("toLocaleDateString", 0, get_date_string, 0x31 ),+ JS_CFUNC_MAGIC_DEF("toLocaleTimeString", 0, get_date_string, 0x32 ),+ JS_CFUNC_DEF("getTimezoneOffset", 0, js_date_getTimezoneOffset ),+ JS_CFUNC_DEF("getTime", 0, js_date_getTime ),+ JS_CFUNC_MAGIC_DEF("getYear", 0, get_date_field, 0x101 ),+ JS_CFUNC_MAGIC_DEF("getFullYear", 0, get_date_field, 0x01 ),+ JS_CFUNC_MAGIC_DEF("getUTCFullYear", 0, get_date_field, 0x00 ),+ JS_CFUNC_MAGIC_DEF("getMonth", 0, get_date_field, 0x11 ),+ JS_CFUNC_MAGIC_DEF("getUTCMonth", 0, get_date_field, 0x10 ),+ JS_CFUNC_MAGIC_DEF("getDate", 0, get_date_field, 0x21 ),+ JS_CFUNC_MAGIC_DEF("getUTCDate", 0, get_date_field, 0x20 ),+ JS_CFUNC_MAGIC_DEF("getHours", 0, get_date_field, 0x31 ),+ JS_CFUNC_MAGIC_DEF("getUTCHours", 0, get_date_field, 0x30 ),+ JS_CFUNC_MAGIC_DEF("getMinutes", 0, get_date_field, 0x41 ),+ JS_CFUNC_MAGIC_DEF("getUTCMinutes", 0, get_date_field, 0x40 ),+ JS_CFUNC_MAGIC_DEF("getSeconds", 0, get_date_field, 0x51 ),+ JS_CFUNC_MAGIC_DEF("getUTCSeconds", 0, get_date_field, 0x50 ),+ JS_CFUNC_MAGIC_DEF("getMilliseconds", 0, get_date_field, 0x61 ),+ JS_CFUNC_MAGIC_DEF("getUTCMilliseconds", 0, get_date_field, 0x60 ),+ JS_CFUNC_MAGIC_DEF("getDay", 0, get_date_field, 0x71 ),+ JS_CFUNC_MAGIC_DEF("getUTCDay", 0, get_date_field, 0x70 ),+ JS_CFUNC_DEF("setTime", 1, js_date_setTime ),+ JS_CFUNC_MAGIC_DEF("setMilliseconds", 1, set_date_field, 0x671 ),+ JS_CFUNC_MAGIC_DEF("setUTCMilliseconds", 1, set_date_field, 0x670 ),+ JS_CFUNC_MAGIC_DEF("setSeconds", 2, set_date_field, 0x571 ),+ JS_CFUNC_MAGIC_DEF("setUTCSeconds", 2, set_date_field, 0x570 ),+ JS_CFUNC_MAGIC_DEF("setMinutes", 3, set_date_field, 0x471 ),+ JS_CFUNC_MAGIC_DEF("setUTCMinutes", 3, set_date_field, 0x470 ),+ JS_CFUNC_MAGIC_DEF("setHours", 4, set_date_field, 0x371 ),+ JS_CFUNC_MAGIC_DEF("setUTCHours", 4, set_date_field, 0x370 ),+ JS_CFUNC_MAGIC_DEF("setDate", 1, set_date_field, 0x231 ),+ JS_CFUNC_MAGIC_DEF("setUTCDate", 1, set_date_field, 0x230 ),+ JS_CFUNC_MAGIC_DEF("setMonth", 2, set_date_field, 0x131 ),+ JS_CFUNC_MAGIC_DEF("setUTCMonth", 2, set_date_field, 0x130 ),+ JS_CFUNC_DEF("setYear", 1, js_date_setYear ),+ JS_CFUNC_MAGIC_DEF("setFullYear", 3, set_date_field, 0x031 ),+ JS_CFUNC_MAGIC_DEF("setUTCFullYear", 3, set_date_field, 0x030 ),+ JS_CFUNC_DEF("toJSON", 1, js_date_toJSON ),+};++void JS_AddIntrinsicDate(JSContext *ctx)+{+ JSValueConst obj;++ /* Date */+ ctx->class_proto[JS_CLASS_DATE] = JS_NewObject(ctx);+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATE], js_date_proto_funcs,+ countof(js_date_proto_funcs));+ obj = JS_NewGlobalCConstructor(ctx, "Date", js_date_constructor, 7,+ ctx->class_proto[JS_CLASS_DATE]);+ JS_SetPropertyFunctionList(ctx, obj, js_date_funcs, countof(js_date_funcs));+}++/* eval */++void JS_AddIntrinsicEval(JSContext *ctx)+{+ ctx->eval_internal = __JS_EvalInternal;+}++#ifdef CONFIG_BIGNUM++/* Operators */++static void js_operator_set_finalizer(JSRuntime *rt, JSValue val)+{+ JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET);+ int i, j;+ JSBinaryOperatorDefEntry *ent;+ + if (opset) {+ for(i = 0; i < JS_OVOP_COUNT; i++) {+ if (opset->self_ops[i])+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i]));+ }+ for(j = 0; j < opset->left.count; j++) {+ ent = &opset->left.tab[j];+ for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {+ if (ent->ops[i])+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]));+ }+ }+ js_free_rt(rt, opset->left.tab);+ for(j = 0; j < opset->right.count; j++) {+ ent = &opset->right.tab[j];+ for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {+ if (ent->ops[i])+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]));+ }+ }+ js_free_rt(rt, opset->right.tab);+ js_free_rt(rt, opset);+ }+}++static void js_operator_set_mark(JSRuntime *rt, JSValueConst val,+ JS_MarkFunc *mark_func)+{+ JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET);+ int i, j;+ JSBinaryOperatorDefEntry *ent;+ + if (opset) {+ for(i = 0; i < JS_OVOP_COUNT; i++) {+ if (opset->self_ops[i])+ JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i]),+ mark_func);+ }+ for(j = 0; j < opset->left.count; j++) {+ ent = &opset->left.tab[j];+ for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {+ if (ent->ops[i])+ JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]),+ mark_func);+ }+ }+ for(j = 0; j < opset->right.count; j++) {+ ent = &opset->right.tab[j];+ for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {+ if (ent->ops[i])+ JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]),+ mark_func);+ }+ }+ }+}+++/* create an OperatorSet object */+static JSValue js_operators_create_internal(JSContext *ctx,+ int argc, JSValueConst *argv,+ BOOL is_primitive)+{+ JSValue opset_obj, prop, obj;+ JSOperatorSetData *opset, *opset1;+ JSBinaryOperatorDef *def;+ JSValueConst arg;+ int i, j;+ JSBinaryOperatorDefEntry *new_tab;+ JSBinaryOperatorDefEntry *ent;+ uint32_t op_count;++ if (ctx->rt->operator_count == UINT32_MAX) {+ return JS_ThrowTypeError(ctx, "too many operators");+ }+ opset_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_OPERATOR_SET);+ if (JS_IsException(opset_obj))+ goto fail;+ opset = js_mallocz(ctx, sizeof(*opset));+ if (!opset)+ goto fail;+ JS_SetOpaque(opset_obj, opset);+ if (argc >= 1) {+ arg = argv[0];+ /* self operators */+ for(i = 0; i < JS_OVOP_COUNT; i++) {+ prop = JS_GetPropertyStr(ctx, arg, js_overloadable_operator_names[i]);+ if (JS_IsException(prop))+ goto fail;+ if (!JS_IsUndefined(prop)) {+ if (check_function(ctx, prop)) {+ JS_FreeValue(ctx, prop);+ goto fail;+ }+ opset->self_ops[i] = JS_VALUE_GET_OBJ(prop);+ }+ }+ }+ /* left & right operators */+ for(j = 1; j < argc; j++) {+ arg = argv[j];+ prop = JS_GetPropertyStr(ctx, arg, "left");+ if (JS_IsException(prop))+ goto fail;+ def = &opset->right;+ if (JS_IsUndefined(prop)) {+ prop = JS_GetPropertyStr(ctx, arg, "right");+ if (JS_IsException(prop))+ goto fail;+ if (JS_IsUndefined(prop)) {+ JS_ThrowTypeError(ctx, "left or right property must be present");+ goto fail;+ }+ def = &opset->left;+ }+ /* get the operator set */+ obj = JS_GetProperty(ctx, prop, JS_ATOM_prototype);+ JS_FreeValue(ctx, prop);+ if (JS_IsException(obj))+ goto fail;+ prop = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet);+ JS_FreeValue(ctx, obj);+ if (JS_IsException(prop))+ goto fail;+ opset1 = JS_GetOpaque2(ctx, prop, JS_CLASS_OPERATOR_SET);+ if (!opset1) {+ JS_FreeValue(ctx, prop);+ goto fail;+ }+ op_count = opset1->operator_counter;+ JS_FreeValue(ctx, prop);+ + /* we assume there are few entries */+ new_tab = js_realloc(ctx, def->tab,+ (def->count + 1) * sizeof(def->tab[0]));+ if (!new_tab)+ goto fail;+ def->tab = new_tab;+ def->count++;+ ent = def->tab + def->count - 1;+ memset(ent, 0, sizeof(def->tab[0]));+ ent->operator_index = op_count;+ + for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {+ prop = JS_GetPropertyStr(ctx, arg,+ js_overloadable_operator_names[i]);+ if (JS_IsException(prop))+ goto fail;+ if (!JS_IsUndefined(prop)) {+ if (check_function(ctx, prop)) {+ JS_FreeValue(ctx, prop);+ goto fail;+ }+ ent->ops[i] = JS_VALUE_GET_OBJ(prop);+ }+ }+ }+ opset->is_primitive = is_primitive;+ opset->operator_counter = ctx->rt->operator_count++;+ return opset_obj;+ fail:+ JS_FreeValue(ctx, opset_obj);+ return JS_EXCEPTION;+}++static JSValue js_operators_create(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return js_operators_create_internal(ctx, argc, argv, FALSE);+}++static JSValue js_operators_updateBigIntOperators(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue opset_obj, prop;+ JSOperatorSetData *opset;+ const JSOverloadableOperatorEnum ops[2] = { JS_OVOP_DIV, JS_OVOP_POW };+ JSOverloadableOperatorEnum op;+ int i;+ + opset_obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_BIG_INT],+ JS_ATOM_Symbol_operatorSet);+ if (JS_IsException(opset_obj))+ goto fail;+ opset = JS_GetOpaque2(ctx, opset_obj, JS_CLASS_OPERATOR_SET);+ if (!opset)+ goto fail;+ for(i = 0; i < countof(ops); i++) {+ op = ops[i];+ prop = JS_GetPropertyStr(ctx, argv[0],+ js_overloadable_operator_names[op]);+ if (JS_IsException(prop))+ goto fail;+ if (!JS_IsUndefined(prop)) {+ if (!JS_IsNull(prop) && check_function(ctx, prop)) {+ JS_FreeValue(ctx, prop);+ goto fail;+ }+ if (opset->self_ops[op])+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[op]));+ if (JS_IsNull(prop)) {+ opset->self_ops[op] = NULL;+ } else {+ opset->self_ops[op] = JS_VALUE_GET_PTR(prop);+ }+ }+ }+ JS_FreeValue(ctx, opset_obj);+ return JS_UNDEFINED;+ fail:+ JS_FreeValue(ctx, opset_obj);+ return JS_EXCEPTION;+}++static int js_operators_set_default(JSContext *ctx, JSValueConst obj)+{+ JSValue opset_obj;++ if (!JS_IsObject(obj)) /* in case the prototype is not defined */+ return 0;+ opset_obj = js_operators_create_internal(ctx, 0, NULL, TRUE);+ if (JS_IsException(opset_obj))+ return -1;+ /* cannot be modified by the user */+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_operatorSet,+ opset_obj, 0);+ return 0;+}++static JSValue js_dummy_operators_ctor(JSContext *ctx, JSValueConst new_target,+ int argc, JSValueConst *argv)+{+ return js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);+}++static JSValue js_global_operators(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue func_obj, proto, opset_obj;++ func_obj = JS_UNDEFINED;+ proto = JS_NewObject(ctx);+ if (JS_IsException(proto))+ return JS_EXCEPTION;+ opset_obj = js_operators_create_internal(ctx, argc, argv, FALSE);+ if (JS_IsException(opset_obj))+ goto fail;+ JS_DefinePropertyValue(ctx, proto, JS_ATOM_Symbol_operatorSet,+ opset_obj, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+ func_obj = JS_NewCFunction2(ctx, js_dummy_operators_ctor, "Operators",+ 0, JS_CFUNC_constructor, 0);+ if (JS_IsException(func_obj))+ goto fail;+ JS_SetConstructor2(ctx, func_obj, proto,+ 0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+ JS_FreeValue(ctx, proto);+ return func_obj;+ fail:+ JS_FreeValue(ctx, proto);+ JS_FreeValue(ctx, func_obj);+ return JS_EXCEPTION;+}++static const JSCFunctionListEntry js_operators_funcs[] = {+ JS_CFUNC_DEF("create", 1, js_operators_create ),+ JS_CFUNC_DEF("updateBigIntOperators", 2, js_operators_updateBigIntOperators ),+};++/* must be called after all overloadable base types are initialized */+void JS_AddIntrinsicOperators(JSContext *ctx)+{+ JSValue obj;++ ctx->allow_operator_overloading = TRUE;+ obj = JS_NewCFunction(ctx, js_global_operators, "Operators", 1);+ JS_SetPropertyFunctionList(ctx, obj,+ js_operators_funcs,+ countof(js_operators_funcs));+ JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_Operators,+ obj,+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+ /* add default operatorSets */+ js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BOOLEAN]);+ js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_NUMBER]);+ js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_STRING]);+ js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_INT]);+ js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT]);+ js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL]);+}++/* BigInt */++static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val)+{+ uint32_t tag;++ redo:+ tag = JS_VALUE_GET_NORM_TAG(val);+ switch(tag) {+ case JS_TAG_INT:+ case JS_TAG_BOOL:+ val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val));+ break;+ case JS_TAG_BIG_INT:+ break;+ case JS_TAG_FLOAT64:+ case JS_TAG_BIG_FLOAT:+ {+ bf_t *a, a_s;+ + a = JS_ToBigFloat(ctx, &a_s, val);+ if (!bf_is_finite(a)) {+ JS_FreeValue(ctx, val);+ val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to bigint");+ } else {+ JSValue val1 = JS_NewBigInt(ctx);+ bf_t *r;+ int ret;+ if (JS_IsException(val1)) {+ JS_FreeValue(ctx, val);+ return JS_EXCEPTION;+ }+ r = JS_GetBigInt(val1);+ ret = bf_set(r, a);+ ret |= bf_rint(r, BF_RNDZ);+ JS_FreeValue(ctx, val);+ if (ret & BF_ST_MEM_ERROR) {+ JS_FreeValue(ctx, val1);+ val = JS_ThrowOutOfMemory(ctx);+ } else if (ret & BF_ST_INEXACT) {+ JS_FreeValue(ctx, val1);+ val = JS_ThrowRangeError(ctx, "cannot convert to bigint: not an integer");+ } else {+ val = JS_CompactBigInt(ctx, val1);+ }+ }+ if (a == &a_s)+ bf_delete(a);+ }+ break;+ case JS_TAG_BIG_DECIMAL:+ val = JS_ToStringFree(ctx, val);+ if (JS_IsException(val))+ break;+ goto redo;+ case JS_TAG_STRING:+ val = JS_StringToBigIntErr(ctx, val);+ break;+ case JS_TAG_OBJECT:+ val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);+ if (JS_IsException(val))+ break;+ goto redo;+ case JS_TAG_NULL:+ case JS_TAG_UNDEFINED:+ default:+ JS_FreeValue(ctx, val);+ return JS_ThrowTypeError(ctx, "cannot convert to bigint");+ }+ return val;+}++static JSValue js_bigint_constructor(JSContext *ctx,+ JSValueConst new_target,+ int argc, JSValueConst *argv)+{+ if (!JS_IsUndefined(new_target))+ return JS_ThrowTypeError(ctx, "not a constructor");+ return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0]));+}++static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val)+{+ if (JS_IsBigInt(ctx, this_val))+ return JS_DupValue(ctx, this_val);++ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {+ JSObject *p = JS_VALUE_GET_OBJ(this_val);+ if (p->class_id == JS_CLASS_BIG_INT) {+ if (JS_IsBigInt(ctx, p->u.object_data))+ return JS_DupValue(ctx, p->u.object_data);+ }+ }+ return JS_ThrowTypeError(ctx, "not a bigint");+}++static JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue val;+ int base;+ JSValue ret;++ val = js_thisBigIntValue(ctx, this_val);+ if (JS_IsException(val))+ return val;+ if (argc == 0 || JS_IsUndefined(argv[0])) {+ base = 10;+ } else {+ base = js_get_radix(ctx, argv[0]);+ if (base < 0)+ goto fail;+ }+ ret = js_bigint_to_string1(ctx, val, base);+ JS_FreeValue(ctx, val);+ return ret;+ fail:+ JS_FreeValue(ctx, val);+ return JS_EXCEPTION;+}++static JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return js_thisBigIntValue(ctx, this_val);+}++static JSValue js_bigint_div(JSContext *ctx,+ JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ bf_t a_s, b_s, *a, *b, *r, *q;+ int status;+ JSValue q_val, r_val;+ + q_val = JS_NewBigInt(ctx);+ if (JS_IsException(q_val))+ return JS_EXCEPTION;+ r_val = JS_NewBigInt(ctx);+ if (JS_IsException(r_val))+ goto fail;+ b = NULL;+ a = JS_ToBigInt(ctx, &a_s, argv[0]);+ if (!a)+ goto fail;+ b = JS_ToBigInt(ctx, &b_s, argv[1]);+ if (!b) {+ JS_FreeBigInt(ctx, a, &a_s);+ goto fail;+ }+ q = JS_GetBigInt(q_val);+ r = JS_GetBigInt(r_val);+ status = bf_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ, magic & 0xf);+ JS_FreeBigInt(ctx, a, &a_s);+ JS_FreeBigInt(ctx, b, &b_s);+ if (unlikely(status)) {+ throw_bf_exception(ctx, status);+ goto fail;+ }+ q_val = JS_CompactBigInt(ctx, q_val);+ if (magic & 0x10) {+ JSValue ret;+ ret = JS_NewArray(ctx);+ if (JS_IsException(ret))+ goto fail;+ JS_SetPropertyUint32(ctx, ret, 0, q_val);+ JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, r_val));+ return ret;+ } else {+ JS_FreeValue(ctx, r_val);+ return q_val;+ }+ fail:+ JS_FreeValue(ctx, q_val);+ JS_FreeValue(ctx, r_val);+ return JS_EXCEPTION;+}++static JSValue js_bigint_sqrt(JSContext *ctx,+ JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ bf_t a_s, *a, *r, *rem;+ int status;+ JSValue r_val, rem_val;+ + r_val = JS_NewBigInt(ctx);+ if (JS_IsException(r_val))+ return JS_EXCEPTION;+ rem_val = JS_NewBigInt(ctx);+ if (JS_IsException(rem_val))+ return JS_EXCEPTION;+ r = JS_GetBigInt(r_val);+ rem = JS_GetBigInt(rem_val);++ a = JS_ToBigInt(ctx, &a_s, argv[0]);+ if (!a)+ goto fail;+ status = bf_sqrtrem(r, rem, a);+ JS_FreeBigInt(ctx, a, &a_s);+ if (unlikely(status & ~BF_ST_INEXACT)) {+ throw_bf_exception(ctx, status);+ goto fail;+ }+ r_val = JS_CompactBigInt(ctx, r_val);+ if (magic) {+ JSValue ret;+ ret = JS_NewArray(ctx);+ if (JS_IsException(ret))+ goto fail;+ JS_SetPropertyUint32(ctx, ret, 0, r_val);+ JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, rem_val));+ return ret;+ } else {+ JS_FreeValue(ctx, rem_val);+ return r_val;+ }+ fail:+ JS_FreeValue(ctx, r_val);+ JS_FreeValue(ctx, rem_val);+ return JS_EXCEPTION;+}++static JSValue js_bigint_op1(JSContext *ctx,+ JSValueConst this_val,+ int argc, JSValueConst *argv,+ int magic)+{+ bf_t a_s, *a;+ int64_t res;++ a = JS_ToBigInt(ctx, &a_s, argv[0]);+ if (!a)+ return JS_EXCEPTION;+ switch(magic) {+ case 0: /* floorLog2 */+ if (a->sign || a->expn <= 0) {+ res = -1;+ } else {+ res = a->expn - 1;+ }+ break;+ case 1: /* ctz */+ if (bf_is_zero(a)) {+ res = -1;+ } else {+ res = bf_get_exp_min(a);+ }+ break;+ default:+ abort();+ }+ JS_FreeBigInt(ctx, a, &a_s);+ return JS_NewBigInt64(ctx, res);+}++static JSValue js_bigint_asUintN(JSContext *ctx,+ JSValueConst this_val,+ int argc, JSValueConst *argv, int asIntN)+{+ uint64_t bits;+ bf_t a_s, *a = &a_s, *r, mask_s, *mask = &mask_s;+ JSValue res;+ + if (JS_ToIndex(ctx, &bits, argv[0]))+ return JS_EXCEPTION;+ res = JS_NewBigInt(ctx);+ if (JS_IsException(res))+ return JS_EXCEPTION;+ r = JS_GetBigInt(res);+ a = JS_ToBigInt(ctx, &a_s, argv[1]);+ if (!a) {+ JS_FreeValue(ctx, res);+ return JS_EXCEPTION;+ }+ /* XXX: optimize */+ r = JS_GetBigInt(res);+ bf_init(ctx->bf_ctx, mask);+ bf_set_ui(mask, 1);+ bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ);+ bf_add_si(mask, mask, -1, BF_PREC_INF, BF_RNDZ);+ bf_logic_and(r, a, mask);+ if (asIntN && bits != 0) {+ bf_set_ui(mask, 1);+ bf_mul_2exp(mask, bits - 1, BF_PREC_INF, BF_RNDZ);+ if (bf_cmpu(r, mask) >= 0) {+ bf_set_ui(mask, 1);+ bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ);+ bf_sub(r, r, mask, BF_PREC_INF, BF_RNDZ);+ }+ }+ bf_delete(mask);+ JS_FreeBigInt(ctx, a, &a_s);+ return JS_CompactBigInt(ctx, res);+}++static const JSCFunctionListEntry js_bigint_funcs[] = {+ JS_CFUNC_MAGIC_DEF("asUintN", 2, js_bigint_asUintN, 0 ),+ JS_CFUNC_MAGIC_DEF("asIntN", 2, js_bigint_asUintN, 1 ),+ /* QuickJS extensions */+ JS_CFUNC_MAGIC_DEF("tdiv", 2, js_bigint_div, BF_RNDZ ),+ JS_CFUNC_MAGIC_DEF("fdiv", 2, js_bigint_div, BF_RNDD ),+ JS_CFUNC_MAGIC_DEF("cdiv", 2, js_bigint_div, BF_RNDU ),+ JS_CFUNC_MAGIC_DEF("ediv", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN ),+ JS_CFUNC_MAGIC_DEF("tdivrem", 2, js_bigint_div, BF_RNDZ | 0x10 ),+ JS_CFUNC_MAGIC_DEF("fdivrem", 2, js_bigint_div, BF_RNDD | 0x10 ),+ JS_CFUNC_MAGIC_DEF("cdivrem", 2, js_bigint_div, BF_RNDU | 0x10 ),+ JS_CFUNC_MAGIC_DEF("edivrem", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN | 0x10 ),+ JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigint_sqrt, 0 ),+ JS_CFUNC_MAGIC_DEF("sqrtrem", 1, js_bigint_sqrt, 1 ),+ JS_CFUNC_MAGIC_DEF("floorLog2", 1, js_bigint_op1, 0 ),+ JS_CFUNC_MAGIC_DEF("ctz", 1, js_bigint_op1, 1 ),+};++static const JSCFunctionListEntry js_bigint_proto_funcs[] = {+ JS_CFUNC_DEF("toString", 0, js_bigint_toString ),+ JS_CFUNC_DEF("valueOf", 0, js_bigint_valueOf ),+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "BigInt", JS_PROP_CONFIGURABLE ),+};++void JS_AddIntrinsicBigInt(JSContext *ctx)+{+ JSRuntime *rt = ctx->rt;+ JSValueConst obj1;++ rt->bigint_ops.to_string = js_bigint_to_string;+ rt->bigint_ops.from_string = js_string_to_bigint;+ rt->bigint_ops.unary_arith = js_unary_arith_bigint;+ rt->bigint_ops.binary_arith = js_binary_arith_bigint;+ rt->bigint_ops.compare = js_compare_bigfloat;+ + ctx->class_proto[JS_CLASS_BIG_INT] = JS_NewObject(ctx);+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_INT],+ js_bigint_proto_funcs,+ countof(js_bigint_proto_funcs));+ obj1 = JS_NewGlobalCConstructor(ctx, "BigInt", js_bigint_constructor, 1,+ ctx->class_proto[JS_CLASS_BIG_INT]);+ JS_SetPropertyFunctionList(ctx, obj1, js_bigint_funcs,+ countof(js_bigint_funcs));+}++/* BigFloat */++static JSValue js_thisBigFloatValue(JSContext *ctx, JSValueConst this_val)+{+ if (JS_IsBigFloat(this_val))+ return JS_DupValue(ctx, this_val);++ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {+ JSObject *p = JS_VALUE_GET_OBJ(this_val);+ if (p->class_id == JS_CLASS_BIG_FLOAT) {+ if (JS_IsBigFloat(p->u.object_data))+ return JS_DupValue(ctx, p->u.object_data);+ }+ }+ return JS_ThrowTypeError(ctx, "not a bigfloat");+}++static JSValue js_bigfloat_toString(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue val;+ int base;+ JSValue ret;++ val = js_thisBigFloatValue(ctx, this_val);+ if (JS_IsException(val))+ return val;+ if (argc == 0 || JS_IsUndefined(argv[0])) {+ base = 10;+ } else {+ base = js_get_radix(ctx, argv[0]);+ if (base < 0)+ goto fail;+ }+ ret = js_ftoa(ctx, val, base, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN);+ JS_FreeValue(ctx, val);+ return ret;+ fail:+ JS_FreeValue(ctx, val);+ return JS_EXCEPTION;+}++static JSValue js_bigfloat_valueOf(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return js_thisBigFloatValue(ctx, this_val);+}++static int bigfloat_get_rnd_mode(JSContext *ctx, JSValueConst val)+{+ int rnd_mode;+ if (JS_ToInt32Sat(ctx, &rnd_mode, val))+ return -1;+ if (rnd_mode < BF_RNDN || rnd_mode > BF_RNDF) {+ JS_ThrowRangeError(ctx, "invalid rounding mode");+ return -1;+ }+ return rnd_mode;+}++static JSValue js_bigfloat_toFixed(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue val, ret;+ int64_t f;+ int rnd_mode, radix;++ val = js_thisBigFloatValue(ctx, this_val);+ if (JS_IsException(val))+ return val;+ if (JS_ToInt64Sat(ctx, &f, argv[0]))+ goto fail;+ if (f < 0 || f > BF_PREC_MAX) {+ JS_ThrowRangeError(ctx, "invalid number of digits");+ goto fail;+ }+ rnd_mode = BF_RNDNA;+ radix = 10;+ /* XXX: swap parameter order for rounding mode and radix */+ if (argc > 1) {+ rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);+ if (rnd_mode < 0)+ goto fail;+ }+ if (argc > 2) {+ radix = js_get_radix(ctx, argv[2]);+ if (radix < 0)+ goto fail;+ }+ ret = js_ftoa(ctx, val, radix, f, rnd_mode | BF_FTOA_FORMAT_FRAC);+ JS_FreeValue(ctx, val);+ return ret;+ fail:+ JS_FreeValue(ctx, val);+ return JS_EXCEPTION;+}++static BOOL js_bigfloat_is_finite(JSContext *ctx, JSValueConst val)+{+ BOOL res;+ uint32_t tag;++ tag = JS_VALUE_GET_NORM_TAG(val);+ switch(tag) {+ case JS_TAG_BIG_FLOAT:+ {+ JSBigFloat *p = JS_VALUE_GET_PTR(val);+ res = bf_is_finite(&p->num);+ }+ break;+ default:+ res = FALSE;+ break;+ }+ return res;+}++static JSValue js_bigfloat_toExponential(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue val, ret;+ int64_t f;+ int rnd_mode, radix;++ val = js_thisBigFloatValue(ctx, this_val);+ if (JS_IsException(val))+ return val;+ if (JS_ToInt64Sat(ctx, &f, argv[0]))+ goto fail;+ if (!js_bigfloat_is_finite(ctx, val)) {+ ret = JS_ToString(ctx, val);+ } else if (JS_IsUndefined(argv[0])) {+ ret = js_ftoa(ctx, val, 10, 0,+ BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP);+ } else {+ if (f < 0 || f > BF_PREC_MAX) {+ JS_ThrowRangeError(ctx, "invalid number of digits");+ goto fail;+ }+ rnd_mode = BF_RNDNA;+ radix = 10;+ if (argc > 1) {+ rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);+ if (rnd_mode < 0)+ goto fail;+ }+ if (argc > 2) {+ radix = js_get_radix(ctx, argv[2]);+ if (radix < 0)+ goto fail;+ }+ ret = js_ftoa(ctx, val, radix, f + 1,+ rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP);+ }+ JS_FreeValue(ctx, val);+ return ret;+ fail:+ JS_FreeValue(ctx, val);+ return JS_EXCEPTION;+}++static JSValue js_bigfloat_toPrecision(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue val, ret;+ int64_t p;+ int rnd_mode, radix;++ val = js_thisBigFloatValue(ctx, this_val);+ if (JS_IsException(val))+ return val;+ if (JS_IsUndefined(argv[0]))+ goto to_string;+ if (JS_ToInt64Sat(ctx, &p, argv[0]))+ goto fail;+ if (!js_bigfloat_is_finite(ctx, val)) {+ to_string:+ ret = JS_ToString(ctx, this_val);+ } else {+ if (p < 1 || p > BF_PREC_MAX) {+ JS_ThrowRangeError(ctx, "invalid number of digits");+ goto fail;+ }+ rnd_mode = BF_RNDNA;+ radix = 10;+ if (argc > 1) {+ rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);+ if (rnd_mode < 0)+ goto fail;+ }+ if (argc > 2) {+ radix = js_get_radix(ctx, argv[2]);+ if (radix < 0)+ goto fail;+ }+ ret = js_ftoa(ctx, val, radix, p, rnd_mode | BF_FTOA_FORMAT_FIXED);+ }+ JS_FreeValue(ctx, val);+ return ret;+ fail:+ JS_FreeValue(ctx, val);+ return JS_EXCEPTION;+}++static const JSCFunctionListEntry js_bigfloat_proto_funcs[] = {+ JS_CFUNC_DEF("toString", 0, js_bigfloat_toString ),+ JS_CFUNC_DEF("valueOf", 0, js_bigfloat_valueOf ),+ JS_CFUNC_DEF("toPrecision", 1, js_bigfloat_toPrecision ),+ JS_CFUNC_DEF("toFixed", 1, js_bigfloat_toFixed ),+ JS_CFUNC_DEF("toExponential", 1, js_bigfloat_toExponential ),+};++static JSValue js_bigfloat_constructor(JSContext *ctx,+ JSValueConst new_target,+ int argc, JSValueConst *argv)+{+ JSValue val;+ if (!JS_IsUndefined(new_target))+ return JS_ThrowTypeError(ctx, "not a constructor");+ if (argc == 0) {+ bf_t *r;+ val = JS_NewBigFloat(ctx);+ if (JS_IsException(val))+ return val;+ r = JS_GetBigFloat(val);+ bf_set_zero(r, 0);+ } else {+ val = JS_DupValue(ctx, argv[0]);+ redo:+ switch(JS_VALUE_GET_NORM_TAG(val)) {+ case JS_TAG_BIG_FLOAT:+ break;+ case JS_TAG_FLOAT64:+ {+ bf_t *r;+ double d = JS_VALUE_GET_FLOAT64(val);+ val = JS_NewBigFloat(ctx);+ if (JS_IsException(val))+ break;+ r = JS_GetBigFloat(val);+ if (bf_set_float64(r, d))+ goto fail;+ }+ break;+ case JS_TAG_INT:+ {+ bf_t *r;+ int32_t v = JS_VALUE_GET_INT(val);+ val = JS_NewBigFloat(ctx);+ if (JS_IsException(val))+ break;+ r = JS_GetBigFloat(val);+ if (bf_set_si(r, v))+ goto fail;+ }+ break;+ case JS_TAG_BIG_INT:+ /* We keep the full precision of the integer */+ {+ JSBigFloat *p = JS_VALUE_GET_PTR(val);+ val = JS_MKPTR(JS_TAG_BIG_FLOAT, p);+ }+ break;+ case JS_TAG_BIG_DECIMAL:+ val = JS_ToStringFree(ctx, val);+ if (JS_IsException(val))+ break;+ goto redo;+ case JS_TAG_STRING:+ {+ const char *str, *p;+ size_t len;+ int err;++ str = JS_ToCStringLen(ctx, &len, val);+ JS_FreeValue(ctx, val);+ if (!str)+ return JS_EXCEPTION;+ p = str;+ p += skip_spaces(p);+ if ((p - str) == len) {+ bf_t *r;+ val = JS_NewBigFloat(ctx);+ if (JS_IsException(val))+ break;+ r = JS_GetBigFloat(val);+ bf_set_zero(r, 0);+ err = 0;+ } else {+ val = js_atof(ctx, p, &p, 0, ATOD_ACCEPT_BIN_OCT |+ ATOD_TYPE_BIG_FLOAT |+ ATOD_ACCEPT_PREFIX_AFTER_SIGN);+ if (JS_IsException(val)) {+ JS_FreeCString(ctx, str);+ return JS_EXCEPTION;+ }+ p += skip_spaces(p);+ err = ((p - str) != len);+ }+ JS_FreeCString(ctx, str);+ if (err) {+ JS_FreeValue(ctx, val);+ return JS_ThrowSyntaxError(ctx, "invalid bigfloat literal");+ }+ }+ break;+ case JS_TAG_OBJECT:+ val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);+ if (JS_IsException(val))+ break;+ goto redo;+ case JS_TAG_NULL:+ case JS_TAG_UNDEFINED:+ default:+ JS_FreeValue(ctx, val);+ return JS_ThrowTypeError(ctx, "cannot convert to bigfloat");+ }+ }+ return val;+ fail:+ JS_FreeValue(ctx, val);+ return JS_EXCEPTION;+}++static JSValue js_bigfloat_get_const(JSContext *ctx,+ JSValueConst this_val, int magic)+{+ bf_t *r;+ JSValue val;+ val = JS_NewBigFloat(ctx);+ if (JS_IsException(val))+ return val;+ r = JS_GetBigFloat(val);+ switch(magic) {+ case 0: /* PI */+ bf_const_pi(r, ctx->fp_env.prec, ctx->fp_env.flags);+ break;+ case 1: /* LN2 */+ bf_const_log2(r, ctx->fp_env.prec, ctx->fp_env.flags);+ break;+ case 2: /* MIN_VALUE */+ case 3: /* MAX_VALUE */+ {+ slimb_t e_range, e;+ e_range = (limb_t)1 << (bf_get_exp_bits(ctx->fp_env.flags) - 1);+ bf_set_ui(r, 1);+ if (magic == 2) {+ e = -e_range + 2;+ if (ctx->fp_env.flags & BF_FLAG_SUBNORMAL)+ e -= ctx->fp_env.prec - 1;+ bf_mul_2exp(r, e, ctx->fp_env.prec, ctx->fp_env.flags);+ } else {+ bf_mul_2exp(r, ctx->fp_env.prec, ctx->fp_env.prec,+ ctx->fp_env.flags);+ bf_add_si(r, r, -1, ctx->fp_env.prec, ctx->fp_env.flags);+ bf_mul_2exp(r, e_range - ctx->fp_env.prec, ctx->fp_env.prec,+ ctx->fp_env.flags);+ }+ }+ break;+ case 4: /* EPSILON */+ bf_set_ui(r, 1);+ bf_mul_2exp(r, 1 - ctx->fp_env.prec,+ ctx->fp_env.prec, ctx->fp_env.flags);+ break;+ default:+ abort();+ }+ return val;+}++static JSValue js_bigfloat_parseFloat(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ bf_t *a;+ const char *str;+ JSValue ret;+ int radix;+ JSFloatEnv *fe;++ str = JS_ToCString(ctx, argv[0]);+ if (!str)+ return JS_EXCEPTION;+ if (JS_ToInt32(ctx, &radix, argv[1])) {+ fail:+ JS_FreeCString(ctx, str);+ return JS_EXCEPTION;+ }+ if (radix != 0 && (radix < 2 || radix > 36)) {+ JS_ThrowRangeError(ctx, "radix must be between 2 and 36");+ goto fail;+ }+ fe = &ctx->fp_env;+ if (argc > 2) {+ fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV);+ if (!fe)+ goto fail;+ }+ ret = JS_NewBigFloat(ctx);+ if (JS_IsException(ret))+ goto done;+ a = JS_GetBigFloat(ret);+ /* XXX: use js_atof() */+ bf_atof(a, str, NULL, radix, fe->prec, fe->flags);+ done:+ JS_FreeCString(ctx, str);+ return ret;+}++static JSValue js_bigfloat_isFinite(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValueConst val = argv[0];+ JSBigFloat *p;+ + if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT)+ return JS_FALSE;+ p = JS_VALUE_GET_PTR(val);+ return JS_NewBool(ctx, bf_is_finite(&p->num));+}++static JSValue js_bigfloat_isNaN(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValueConst val = argv[0];+ JSBigFloat *p;+ + if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT)+ return JS_FALSE;+ p = JS_VALUE_GET_PTR(val);+ return JS_NewBool(ctx, bf_is_nan(&p->num));+}++enum {+ MATH_OP_ABS,+ MATH_OP_FLOOR,+ MATH_OP_CEIL,+ MATH_OP_ROUND,+ MATH_OP_TRUNC,+ MATH_OP_SQRT,+ MATH_OP_FPROUND,+ MATH_OP_ACOS,+ MATH_OP_ASIN,+ MATH_OP_ATAN,+ MATH_OP_ATAN2,+ MATH_OP_COS,+ MATH_OP_EXP,+ MATH_OP_LOG,+ MATH_OP_POW,+ MATH_OP_SIN,+ MATH_OP_TAN,+ MATH_OP_FMOD,+ MATH_OP_REM,+ MATH_OP_SIGN,++ MATH_OP_ADD,+ MATH_OP_SUB,+ MATH_OP_MUL,+ MATH_OP_DIV,+};++static JSValue js_bigfloat_fop(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ bf_t a_s, *a, *r;+ JSFloatEnv *fe;+ int rnd_mode;+ JSValue op1, res;++ op1 = JS_ToNumeric(ctx, argv[0]);+ if (JS_IsException(op1))+ return op1;+ a = JS_ToBigFloat(ctx, &a_s, op1);+ fe = &ctx->fp_env;+ if (argc > 1) {+ fe = JS_GetOpaque2(ctx, argv[1], JS_CLASS_FLOAT_ENV);+ if (!fe)+ goto fail;+ }+ res = JS_NewBigFloat(ctx);+ if (JS_IsException(res)) {+ fail:+ if (a == &a_s)+ bf_delete(a);+ JS_FreeValue(ctx, op1);+ return JS_EXCEPTION;+ }+ r = JS_GetBigFloat(res);+ switch (magic) {+ case MATH_OP_ABS:+ bf_set(r, a);+ r->sign = 0;+ break;+ case MATH_OP_FLOOR:+ rnd_mode = BF_RNDD;+ goto rint;+ case MATH_OP_CEIL:+ rnd_mode = BF_RNDU;+ goto rint;+ case MATH_OP_ROUND:+ rnd_mode = BF_RNDNA;+ goto rint;+ case MATH_OP_TRUNC:+ rnd_mode = BF_RNDZ;+ rint:+ bf_set(r, a);+ fe->status |= bf_rint(r, rnd_mode);+ break;+ case MATH_OP_SQRT:+ fe->status |= bf_sqrt(r, a, fe->prec, fe->flags);+ break;+ case MATH_OP_FPROUND:+ bf_set(r, a);+ fe->status |= bf_round(r, fe->prec, fe->flags);+ break;+ case MATH_OP_ACOS:+ fe->status |= bf_acos(r, a, fe->prec, fe->flags);+ break;+ case MATH_OP_ASIN:+ fe->status |= bf_asin(r, a, fe->prec, fe->flags);+ break;+ case MATH_OP_ATAN:+ fe->status |= bf_atan(r, a, fe->prec, fe->flags);+ break;+ case MATH_OP_COS:+ fe->status |= bf_cos(r, a, fe->prec, fe->flags);+ break;+ case MATH_OP_EXP:+ fe->status |= bf_exp(r, a, fe->prec, fe->flags);+ break;+ case MATH_OP_LOG:+ fe->status |= bf_log(r, a, fe->prec, fe->flags);+ break;+ case MATH_OP_SIN:+ fe->status |= bf_sin(r, a, fe->prec, fe->flags);+ break;+ case MATH_OP_TAN:+ fe->status |= bf_tan(r, a, fe->prec, fe->flags);+ break;+ case MATH_OP_SIGN:+ if (bf_is_nan(a) || bf_is_zero(a)) {+ bf_set(r, a);+ } else {+ bf_set_si(r, 1 - 2 * a->sign);+ }+ break;+ default:+ abort();+ }+ if (a == &a_s)+ bf_delete(a);+ JS_FreeValue(ctx, op1);+ return res;+}++static JSValue js_bigfloat_fop2(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ bf_t a_s, *a, b_s, *b, r_s, *r = &r_s;+ JSFloatEnv *fe;+ JSValue op1, op2, res;++ op1 = JS_ToNumeric(ctx, argv[0]);+ if (JS_IsException(op1))+ return op1;+ op2 = JS_ToNumeric(ctx, argv[1]);+ if (JS_IsException(op2)) {+ JS_FreeValue(ctx, op1);+ return op2;+ }+ a = JS_ToBigFloat(ctx, &a_s, op1);+ b = JS_ToBigFloat(ctx, &b_s, op2);+ fe = &ctx->fp_env;+ if (argc > 2) {+ fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV);+ if (!fe)+ goto fail;+ }+ res = JS_NewBigFloat(ctx);+ if (JS_IsException(res)) {+ fail:+ if (a == &a_s)+ bf_delete(a);+ if (b == &b_s)+ bf_delete(b);+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ return JS_EXCEPTION;+ }+ r = JS_GetBigFloat(res);+ switch (magic) {+ case MATH_OP_ATAN2:+ fe->status |= bf_atan2(r, a, b, fe->prec, fe->flags);+ break;+ case MATH_OP_POW:+ fe->status |= bf_pow(r, a, b, fe->prec, fe->flags | BF_POW_JS_QUIRKS);+ break;+ case MATH_OP_FMOD:+ fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ);+ break;+ case MATH_OP_REM:+ fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDN);+ break;+ case MATH_OP_ADD:+ fe->status |= bf_add(r, a, b, fe->prec, fe->flags);+ break;+ case MATH_OP_SUB:+ fe->status |= bf_sub(r, a, b, fe->prec, fe->flags);+ break;+ case MATH_OP_MUL:+ fe->status |= bf_mul(r, a, b, fe->prec, fe->flags);+ break;+ case MATH_OP_DIV:+ fe->status |= bf_div(r, a, b, fe->prec, fe->flags);+ break;+ default:+ abort();+ }+ if (a == &a_s)+ bf_delete(a);+ if (b == &b_s)+ bf_delete(b);+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ return res;+}++static const JSCFunctionListEntry js_bigfloat_funcs[] = {+ JS_CGETSET_MAGIC_DEF("PI", js_bigfloat_get_const, NULL, 0 ),+ JS_CGETSET_MAGIC_DEF("LN2", js_bigfloat_get_const, NULL, 1 ),+ JS_CGETSET_MAGIC_DEF("MIN_VALUE", js_bigfloat_get_const, NULL, 2 ),+ JS_CGETSET_MAGIC_DEF("MAX_VALUE", js_bigfloat_get_const, NULL, 3 ),+ JS_CGETSET_MAGIC_DEF("EPSILON", js_bigfloat_get_const, NULL, 4 ),+ JS_CFUNC_DEF("parseFloat", 1, js_bigfloat_parseFloat ),+ JS_CFUNC_DEF("isFinite", 1, js_bigfloat_isFinite ),+ JS_CFUNC_DEF("isNaN", 1, js_bigfloat_isNaN ),+ JS_CFUNC_MAGIC_DEF("abs", 1, js_bigfloat_fop, MATH_OP_ABS ),+ JS_CFUNC_MAGIC_DEF("fpRound", 1, js_bigfloat_fop, MATH_OP_FPROUND ),+ JS_CFUNC_MAGIC_DEF("floor", 1, js_bigfloat_fop, MATH_OP_FLOOR ),+ JS_CFUNC_MAGIC_DEF("ceil", 1, js_bigfloat_fop, MATH_OP_CEIL ),+ JS_CFUNC_MAGIC_DEF("round", 1, js_bigfloat_fop, MATH_OP_ROUND ),+ JS_CFUNC_MAGIC_DEF("trunc", 1, js_bigfloat_fop, MATH_OP_TRUNC ),+ JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigfloat_fop, MATH_OP_SQRT ),+ JS_CFUNC_MAGIC_DEF("acos", 1, js_bigfloat_fop, MATH_OP_ACOS ),+ JS_CFUNC_MAGIC_DEF("asin", 1, js_bigfloat_fop, MATH_OP_ASIN ),+ JS_CFUNC_MAGIC_DEF("atan", 1, js_bigfloat_fop, MATH_OP_ATAN ),+ JS_CFUNC_MAGIC_DEF("atan2", 2, js_bigfloat_fop2, MATH_OP_ATAN2 ),+ JS_CFUNC_MAGIC_DEF("cos", 1, js_bigfloat_fop, MATH_OP_COS ),+ JS_CFUNC_MAGIC_DEF("exp", 1, js_bigfloat_fop, MATH_OP_EXP ),+ JS_CFUNC_MAGIC_DEF("log", 1, js_bigfloat_fop, MATH_OP_LOG ),+ JS_CFUNC_MAGIC_DEF("pow", 2, js_bigfloat_fop2, MATH_OP_POW ),+ JS_CFUNC_MAGIC_DEF("sin", 1, js_bigfloat_fop, MATH_OP_SIN ),+ JS_CFUNC_MAGIC_DEF("tan", 1, js_bigfloat_fop, MATH_OP_TAN ),+ JS_CFUNC_MAGIC_DEF("sign", 1, js_bigfloat_fop, MATH_OP_SIGN ),+ JS_CFUNC_MAGIC_DEF("add", 2, js_bigfloat_fop2, MATH_OP_ADD ),+ JS_CFUNC_MAGIC_DEF("sub", 2, js_bigfloat_fop2, MATH_OP_SUB ),+ JS_CFUNC_MAGIC_DEF("mul", 2, js_bigfloat_fop2, MATH_OP_MUL ),+ JS_CFUNC_MAGIC_DEF("div", 2, js_bigfloat_fop2, MATH_OP_DIV ),+ JS_CFUNC_MAGIC_DEF("fmod", 2, js_bigfloat_fop2, MATH_OP_FMOD ),+ JS_CFUNC_MAGIC_DEF("remainder", 2, js_bigfloat_fop2, MATH_OP_REM ),+};++/* FloatEnv */++static JSValue js_float_env_constructor(JSContext *ctx,+ JSValueConst new_target,+ int argc, JSValueConst *argv)+{+ JSValue obj;+ JSFloatEnv *fe;+ int64_t prec;+ int flags, rndmode;++ prec = ctx->fp_env.prec;+ flags = ctx->fp_env.flags;+ if (!JS_IsUndefined(argv[0])) {+ if (JS_ToInt64Sat(ctx, &prec, argv[0]))+ return JS_EXCEPTION;+ if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)+ return JS_ThrowRangeError(ctx, "invalid precision");+ flags = BF_RNDN; /* RNDN, max exponent size, no subnormal */+ if (argc > 1 && !JS_IsUndefined(argv[1])) {+ if (JS_ToInt32Sat(ctx, &rndmode, argv[1]))+ return JS_EXCEPTION;+ if (rndmode < BF_RNDN || rndmode > BF_RNDF)+ return JS_ThrowRangeError(ctx, "invalid rounding mode");+ flags = rndmode;+ }+ }++ obj = JS_NewObjectClass(ctx, JS_CLASS_FLOAT_ENV);+ if (JS_IsException(obj))+ return JS_EXCEPTION;+ fe = js_malloc(ctx, sizeof(*fe));+ if (!fe)+ return JS_EXCEPTION;+ fe->prec = prec;+ fe->flags = flags;+ fe->status = 0;+ JS_SetOpaque(obj, fe);+ return obj;+}++static void js_float_env_finalizer(JSRuntime *rt, JSValue val)+{+ JSFloatEnv *fe = JS_GetOpaque(val, JS_CLASS_FLOAT_ENV);+ js_free_rt(rt, fe);+}++static JSValue js_float_env_get_prec(JSContext *ctx, JSValueConst this_val)+{+ return JS_NewInt64(ctx, ctx->fp_env.prec);+}++static JSValue js_float_env_get_expBits(JSContext *ctx, JSValueConst this_val)+{+ return JS_NewInt32(ctx, bf_get_exp_bits(ctx->fp_env.flags));+}++static JSValue js_float_env_setPrec(JSContext *ctx,+ JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValueConst func;+ int exp_bits, flags, saved_flags;+ JSValue ret;+ limb_t saved_prec;+ int64_t prec;++ func = argv[0];+ if (JS_ToInt64Sat(ctx, &prec, argv[1]))+ return JS_EXCEPTION;+ if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)+ return JS_ThrowRangeError(ctx, "invalid precision");+ exp_bits = BF_EXP_BITS_MAX;++ if (argc > 2 && !JS_IsUndefined(argv[2])) {+ if (JS_ToInt32Sat(ctx, &exp_bits, argv[2]))+ return JS_EXCEPTION;+ if (exp_bits < BF_EXP_BITS_MIN || exp_bits > BF_EXP_BITS_MAX)+ return JS_ThrowRangeError(ctx, "invalid number of exponent bits");+ }++ flags = BF_RNDN | BF_FLAG_SUBNORMAL | bf_set_exp_bits(exp_bits);++ saved_prec = ctx->fp_env.prec;+ saved_flags = ctx->fp_env.flags;++ ctx->fp_env.prec = prec;+ ctx->fp_env.flags = flags;++ ret = JS_Call(ctx, func, JS_UNDEFINED, 0, NULL);+ /* always restore the floating point precision */+ ctx->fp_env.prec = saved_prec;+ ctx->fp_env.flags = saved_flags;+ return ret;+}++#define FE_PREC (-1)+#define FE_EXP (-2)+#define FE_RNDMODE (-3)+#define FE_SUBNORMAL (-4)++static JSValue js_float_env_proto_get_status(JSContext *ctx, JSValueConst this_val, int magic)+{+ JSFloatEnv *fe;+ fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);+ if (!fe)+ return JS_EXCEPTION;+ switch(magic) {+ case FE_PREC:+ return JS_NewInt64(ctx, fe->prec);+ case FE_EXP:+ return JS_NewInt32(ctx, bf_get_exp_bits(fe->flags));+ case FE_RNDMODE:+ return JS_NewInt32(ctx, fe->flags & BF_RND_MASK);+ case FE_SUBNORMAL:+ return JS_NewBool(ctx, (fe->flags & BF_FLAG_SUBNORMAL) != 0);+ default:+ return JS_NewBool(ctx, (fe->status & magic) != 0);+ }+}++static JSValue js_float_env_proto_set_status(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic)+{+ JSFloatEnv *fe;+ int b;+ int64_t prec;++ fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);+ if (!fe)+ return JS_EXCEPTION;+ switch(magic) {+ case FE_PREC:+ if (JS_ToInt64Sat(ctx, &prec, val))+ return JS_EXCEPTION;+ if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)+ return JS_ThrowRangeError(ctx, "invalid precision");+ fe->prec = prec;+ break;+ case FE_EXP:+ if (JS_ToInt32Sat(ctx, &b, val))+ return JS_EXCEPTION;+ if (b < BF_EXP_BITS_MIN || b > BF_EXP_BITS_MAX)+ return JS_ThrowRangeError(ctx, "invalid number of exponent bits");+ fe->flags = (fe->flags & ~(BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)) |+ bf_set_exp_bits(b);+ break;+ case FE_RNDMODE:+ b = bigfloat_get_rnd_mode(ctx, val);+ if (b < 0)+ return JS_EXCEPTION;+ fe->flags = (fe->flags & ~BF_RND_MASK) | b;+ break;+ case FE_SUBNORMAL:+ b = JS_ToBool(ctx, val);+ fe->flags = (fe->flags & ~BF_FLAG_SUBNORMAL) | (b ? BF_FLAG_SUBNORMAL: 0);+ break;+ default:+ b = JS_ToBool(ctx, val);+ fe->status = (fe->status & ~magic) & ((-b) & magic);+ break;+ }+ return JS_UNDEFINED;+}++static JSValue js_float_env_clearStatus(JSContext *ctx,+ JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSFloatEnv *fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);+ if (!fe)+ return JS_EXCEPTION;+ fe->status = 0;+ return JS_UNDEFINED;+}++static const JSCFunctionListEntry js_float_env_funcs[] = {+ JS_CGETSET_DEF("prec", js_float_env_get_prec, NULL ),+ JS_CGETSET_DEF("expBits", js_float_env_get_expBits, NULL ),+ JS_CFUNC_DEF("setPrec", 2, js_float_env_setPrec ),+ JS_PROP_INT32_DEF("RNDN", BF_RNDN, 0 ),+ JS_PROP_INT32_DEF("RNDZ", BF_RNDZ, 0 ),+ JS_PROP_INT32_DEF("RNDU", BF_RNDU, 0 ),+ JS_PROP_INT32_DEF("RNDD", BF_RNDD, 0 ),+ JS_PROP_INT32_DEF("RNDNA", BF_RNDNA, 0 ),+ JS_PROP_INT32_DEF("RNDA", BF_RNDA, 0 ),+ JS_PROP_INT32_DEF("RNDF", BF_RNDF, 0 ),+ JS_PROP_INT32_DEF("precMin", BF_PREC_MIN, 0 ),+ JS_PROP_INT64_DEF("precMax", BF_PREC_MAX, 0 ),+ JS_PROP_INT32_DEF("expBitsMin", BF_EXP_BITS_MIN, 0 ),+ JS_PROP_INT32_DEF("expBitsMax", BF_EXP_BITS_MAX, 0 ),+};++static const JSCFunctionListEntry js_float_env_proto_funcs[] = {+ JS_CGETSET_MAGIC_DEF("prec", js_float_env_proto_get_status,+ js_float_env_proto_set_status, FE_PREC ),+ JS_CGETSET_MAGIC_DEF("expBits", js_float_env_proto_get_status,+ js_float_env_proto_set_status, FE_EXP ),+ JS_CGETSET_MAGIC_DEF("rndMode", js_float_env_proto_get_status,+ js_float_env_proto_set_status, FE_RNDMODE ),+ JS_CGETSET_MAGIC_DEF("subnormal", js_float_env_proto_get_status,+ js_float_env_proto_set_status, FE_SUBNORMAL ),+ JS_CGETSET_MAGIC_DEF("invalidOperation", js_float_env_proto_get_status,+ js_float_env_proto_set_status, BF_ST_INVALID_OP ),+ JS_CGETSET_MAGIC_DEF("divideByZero", js_float_env_proto_get_status,+ js_float_env_proto_set_status, BF_ST_DIVIDE_ZERO ),+ JS_CGETSET_MAGIC_DEF("overflow", js_float_env_proto_get_status,+ js_float_env_proto_set_status, BF_ST_OVERFLOW ),+ JS_CGETSET_MAGIC_DEF("underflow", js_float_env_proto_get_status,+ js_float_env_proto_set_status, BF_ST_UNDERFLOW ),+ JS_CGETSET_MAGIC_DEF("inexact", js_float_env_proto_get_status,+ js_float_env_proto_set_status, BF_ST_INEXACT ),+ JS_CFUNC_DEF("clearStatus", 0, js_float_env_clearStatus ),+};++void JS_AddIntrinsicBigFloat(JSContext *ctx)+{+ JSRuntime *rt = ctx->rt;+ JSValueConst obj1;+ + rt->bigfloat_ops.to_string = js_bigfloat_to_string;+ rt->bigfloat_ops.from_string = js_string_to_bigfloat;+ rt->bigfloat_ops.unary_arith = js_unary_arith_bigfloat;+ rt->bigfloat_ops.binary_arith = js_binary_arith_bigfloat;+ rt->bigfloat_ops.compare = js_compare_bigfloat;+ rt->bigfloat_ops.mul_pow10_to_float64 = js_mul_pow10_to_float64;+ rt->bigfloat_ops.mul_pow10 = js_mul_pow10;+ + ctx->class_proto[JS_CLASS_BIG_FLOAT] = JS_NewObject(ctx);+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT],+ js_bigfloat_proto_funcs,+ countof(js_bigfloat_proto_funcs));+ obj1 = JS_NewGlobalCConstructor(ctx, "BigFloat", js_bigfloat_constructor, 1,+ ctx->class_proto[JS_CLASS_BIG_FLOAT]);+ JS_SetPropertyFunctionList(ctx, obj1, js_bigfloat_funcs,+ countof(js_bigfloat_funcs));++ ctx->class_proto[JS_CLASS_FLOAT_ENV] = JS_NewObject(ctx);+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_FLOAT_ENV],+ js_float_env_proto_funcs,+ countof(js_float_env_proto_funcs));+ obj1 = JS_NewGlobalCConstructorOnly(ctx, "BigFloatEnv",+ js_float_env_constructor, 1,+ ctx->class_proto[JS_CLASS_FLOAT_ENV]);+ JS_SetPropertyFunctionList(ctx, obj1, js_float_env_funcs,+ countof(js_float_env_funcs));+}++/* BigDecimal */++static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,+ BOOL allow_null_or_undefined)+{+ redo:+ switch(JS_VALUE_GET_NORM_TAG(val)) {+ case JS_TAG_BIG_DECIMAL:+ break;+ case JS_TAG_NULL:+ if (!allow_null_or_undefined)+ goto fail;+ /* fall thru */+ case JS_TAG_BOOL:+ case JS_TAG_INT:+ {+ bfdec_t *r;+ int32_t v = JS_VALUE_GET_INT(val);++ val = JS_NewBigDecimal(ctx);+ if (JS_IsException(val))+ break;+ r = JS_GetBigDecimal(val);+ if (bfdec_set_si(r, v)) {+ JS_FreeValue(ctx, val);+ val = JS_EXCEPTION;+ break;+ }+ }+ break;+ case JS_TAG_FLOAT64:+ case JS_TAG_BIG_INT:+ case JS_TAG_BIG_FLOAT:+ val = JS_ToStringFree(ctx, val);+ if (JS_IsException(val))+ break;+ goto redo;+ case JS_TAG_STRING:+ {+ const char *str, *p;+ size_t len;+ int err;++ str = JS_ToCStringLen(ctx, &len, val);+ JS_FreeValue(ctx, val);+ if (!str)+ return JS_EXCEPTION;+ p = str;+ p += skip_spaces(p);+ if ((p - str) == len) {+ bfdec_t *r;+ val = JS_NewBigDecimal(ctx);+ if (JS_IsException(val))+ break;+ r = JS_GetBigDecimal(val);+ bfdec_set_zero(r, 0);+ err = 0;+ } else {+ val = js_atof(ctx, p, &p, 0, ATOD_TYPE_BIG_DECIMAL);+ if (JS_IsException(val)) {+ JS_FreeCString(ctx, str);+ return JS_EXCEPTION;+ }+ p += skip_spaces(p);+ err = ((p - str) != len);+ }+ JS_FreeCString(ctx, str);+ if (err) {+ JS_FreeValue(ctx, val);+ return JS_ThrowSyntaxError(ctx, "invalid bigdecimal literal");+ }+ }+ break;+ case JS_TAG_OBJECT:+ val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);+ if (JS_IsException(val))+ break;+ goto redo;+ case JS_TAG_UNDEFINED:+ {+ bfdec_t *r;+ if (!allow_null_or_undefined)+ goto fail;+ val = JS_NewBigDecimal(ctx);+ if (JS_IsException(val))+ break;+ r = JS_GetBigDecimal(val);+ bfdec_set_nan(r);+ }+ break;+ default:+ fail:+ JS_FreeValue(ctx, val);+ return JS_ThrowTypeError(ctx, "cannot convert to bigdecimal");+ }+ return val;+}++static JSValue js_bigdecimal_constructor(JSContext *ctx,+ JSValueConst new_target,+ int argc, JSValueConst *argv)+{+ JSValue val;+ if (!JS_IsUndefined(new_target))+ return JS_ThrowTypeError(ctx, "not a constructor");+ if (argc == 0) {+ bfdec_t *r;+ val = JS_NewBigDecimal(ctx);+ if (JS_IsException(val))+ return val;+ r = JS_GetBigDecimal(val);+ bfdec_set_zero(r, 0);+ } else {+ val = JS_ToBigDecimalFree(ctx, JS_DupValue(ctx, argv[0]), FALSE);+ }+ return val;+}++static JSValue js_thisBigDecimalValue(JSContext *ctx, JSValueConst this_val)+{+ if (JS_IsBigDecimal(this_val))+ return JS_DupValue(ctx, this_val);++ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {+ JSObject *p = JS_VALUE_GET_OBJ(this_val);+ if (p->class_id == JS_CLASS_BIG_DECIMAL) {+ if (JS_IsBigDecimal(p->u.object_data))+ return JS_DupValue(ctx, p->u.object_data);+ }+ }+ return JS_ThrowTypeError(ctx, "not a bigdecimal");+}++static JSValue js_bigdecimal_toString(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue val;++ val = js_thisBigDecimalValue(ctx, this_val);+ if (JS_IsException(val))+ return val;+ return JS_ToStringFree(ctx, val);+}++static JSValue js_bigdecimal_valueOf(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ return js_thisBigDecimalValue(ctx, this_val);+}++static int js_bigdecimal_get_rnd_mode(JSContext *ctx, JSValueConst obj)+{+ const char *str;+ size_t size;+ int rnd_mode;+ + str = JS_ToCStringLen(ctx, &size, obj);+ if (!str)+ return -1;+ if (strlen(str) != size)+ goto invalid_rounding_mode;+ if (!strcmp(str, "floor")) {+ rnd_mode = BF_RNDD;+ } else if (!strcmp(str, "ceiling")) {+ rnd_mode = BF_RNDU;+ } else if (!strcmp(str, "down")) {+ rnd_mode = BF_RNDZ;+ } else if (!strcmp(str, "up")) {+ rnd_mode = BF_RNDA;+ } else if (!strcmp(str, "half-even")) {+ rnd_mode = BF_RNDN;+ } else if (!strcmp(str, "half-up")) {+ rnd_mode = BF_RNDNA;+ } else {+ invalid_rounding_mode:+ JS_FreeCString(ctx, str);+ JS_ThrowTypeError(ctx, "invalid rounding mode");+ return -1;+ }+ JS_FreeCString(ctx, str);+ return rnd_mode;+}++typedef struct {+ int64_t prec;+ bf_flags_t flags;+} BigDecimalEnv;++static int js_bigdecimal_get_env(JSContext *ctx, BigDecimalEnv *fe,+ JSValueConst obj)+{+ JSValue prop;+ int64_t val;+ BOOL has_prec;+ int rnd_mode;+ + if (!JS_IsObject(obj)) {+ JS_ThrowTypeErrorNotAnObject(ctx);+ return -1;+ }+ prop = JS_GetProperty(ctx, obj, JS_ATOM_roundingMode);+ if (JS_IsException(prop))+ return -1;+ rnd_mode = js_bigdecimal_get_rnd_mode(ctx, prop);+ JS_FreeValue(ctx, prop);+ if (rnd_mode < 0)+ return -1;+ fe->flags = rnd_mode;+ + prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumSignificantDigits);+ if (JS_IsException(prop))+ return -1;+ has_prec = FALSE;+ if (!JS_IsUndefined(prop)) {+ if (JS_ToInt64SatFree(ctx, &val, prop))+ return -1;+ if (val < 1 || val > BF_PREC_MAX)+ goto invalid_precision;+ fe->prec = val;+ has_prec = TRUE;+ }++ prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumFractionDigits);+ if (JS_IsException(prop))+ return -1;+ if (!JS_IsUndefined(prop)) {+ if (has_prec) {+ JS_FreeValue(ctx, prop);+ JS_ThrowTypeError(ctx, "cannot provide both maximumSignificantDigits and maximumFractionDigits");+ return -1;+ }+ if (JS_ToInt64SatFree(ctx, &val, prop))+ return -1;+ if (val < 0 || val > BF_PREC_MAX) {+ invalid_precision:+ JS_ThrowTypeError(ctx, "invalid precision");+ return -1;+ }+ fe->prec = val;+ fe->flags |= BF_FLAG_RADPNT_PREC;+ has_prec = TRUE;+ }+ if (!has_prec) {+ JS_ThrowTypeError(ctx, "precision must be present");+ return -1;+ }+ return 0;+}+++static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv, int magic)+{+ bfdec_t *a, *b, r_s, *r = &r_s;+ JSValue op1, op2, res;+ BigDecimalEnv fe_s, *fe = &fe_s;+ int op_count, ret;++ if (magic == MATH_OP_SQRT ||+ magic == MATH_OP_ROUND)+ op_count = 1;+ else+ op_count = 2;+ + op1 = JS_ToNumeric(ctx, argv[0]);+ if (JS_IsException(op1))+ return op1;+ a = JS_ToBigDecimal(ctx, op1);+ if (!a) {+ JS_FreeValue(ctx, op1);+ return JS_EXCEPTION;+ }+ if (op_count >= 2) {+ op2 = JS_ToNumeric(ctx, argv[1]);+ if (JS_IsException(op2)) {+ JS_FreeValue(ctx, op1);+ return op2;+ }+ b = JS_ToBigDecimal(ctx, op2);+ if (!b)+ goto fail;+ } else {+ op2 = JS_UNDEFINED;+ b = NULL;+ }+ fe->flags = BF_RNDZ;+ fe->prec = BF_PREC_INF;+ if (op_count < argc) {+ if (js_bigdecimal_get_env(ctx, fe, argv[op_count]))+ goto fail;+ }++ res = JS_NewBigDecimal(ctx);+ if (JS_IsException(res)) {+ fail:+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ return JS_EXCEPTION;+ }+ r = JS_GetBigDecimal(res);+ switch (magic) {+ case MATH_OP_ADD:+ ret = bfdec_add(r, a, b, fe->prec, fe->flags);+ break;+ case MATH_OP_SUB:+ ret = bfdec_sub(r, a, b, fe->prec, fe->flags);+ break;+ case MATH_OP_MUL:+ ret = bfdec_mul(r, a, b, fe->prec, fe->flags);+ break;+ case MATH_OP_DIV:+ ret = bfdec_div(r, a, b, fe->prec, fe->flags);+ break;+ case MATH_OP_FMOD:+ ret = bfdec_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ);+ break;+ case MATH_OP_SQRT:+ ret = bfdec_sqrt(r, a, fe->prec, fe->flags);+ break;+ case MATH_OP_ROUND:+ ret = bfdec_set(r, a);+ if (!(ret & BF_ST_MEM_ERROR))+ ret = bfdec_round(r, fe->prec, fe->flags);+ break;+ default:+ abort();+ }+ JS_FreeValue(ctx, op1);+ JS_FreeValue(ctx, op2);+ ret &= BF_ST_MEM_ERROR | BF_ST_DIVIDE_ZERO | BF_ST_INVALID_OP |+ BF_ST_OVERFLOW;+ if (ret != 0) {+ JS_FreeValue(ctx, res);+ return throw_bf_exception(ctx, ret);+ } else {+ return res;+ }+}++static JSValue js_bigdecimal_toFixed(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue val, ret;+ int64_t f;+ int rnd_mode;++ val = js_thisBigDecimalValue(ctx, this_val);+ if (JS_IsException(val))+ return val;+ if (JS_ToInt64Sat(ctx, &f, argv[0]))+ goto fail;+ if (f < 0 || f > BF_PREC_MAX) {+ JS_ThrowRangeError(ctx, "invalid number of digits");+ goto fail;+ }+ rnd_mode = BF_RNDNA;+ if (argc > 1) {+ rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);+ if (rnd_mode < 0)+ goto fail;+ }+ ret = js_bigdecimal_to_string1(ctx, val, f, rnd_mode | BF_FTOA_FORMAT_FRAC);+ JS_FreeValue(ctx, val);+ return ret;+ fail:+ JS_FreeValue(ctx, val);+ return JS_EXCEPTION;+}++static JSValue js_bigdecimal_toExponential(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue val, ret;+ int64_t f;+ int rnd_mode;++ val = js_thisBigDecimalValue(ctx, this_val);+ if (JS_IsException(val))+ return val;+ if (JS_ToInt64Sat(ctx, &f, argv[0]))+ goto fail;+ if (JS_IsUndefined(argv[0])) {+ ret = js_bigdecimal_to_string1(ctx, val, 0,+ BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP);+ } else {+ if (f < 0 || f > BF_PREC_MAX) {+ JS_ThrowRangeError(ctx, "invalid number of digits");+ goto fail;+ }+ rnd_mode = BF_RNDNA;+ if (argc > 1) {+ rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);+ if (rnd_mode < 0)+ goto fail;+ }+ ret = js_bigdecimal_to_string1(ctx, val, f + 1,+ rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP);+ }+ JS_FreeValue(ctx, val);+ return ret;+ fail:+ JS_FreeValue(ctx, val);+ return JS_EXCEPTION;+}++static JSValue js_bigdecimal_toPrecision(JSContext *ctx, JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSValue val, ret;+ int64_t p;+ int rnd_mode;++ val = js_thisBigDecimalValue(ctx, this_val);+ if (JS_IsException(val))+ return val;+ if (JS_IsUndefined(argv[0])) {+ return JS_ToStringFree(ctx, val);+ }+ if (JS_ToInt64Sat(ctx, &p, argv[0]))+ goto fail;+ if (p < 1 || p > BF_PREC_MAX) {+ JS_ThrowRangeError(ctx, "invalid number of digits");+ goto fail;+ }+ rnd_mode = BF_RNDNA;+ if (argc > 1) {+ rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);+ if (rnd_mode < 0)+ goto fail;+ }+ ret = js_bigdecimal_to_string1(ctx, val, p,+ rnd_mode | BF_FTOA_FORMAT_FIXED);+ JS_FreeValue(ctx, val);+ return ret;+ fail:+ JS_FreeValue(ctx, val);+ return JS_EXCEPTION;+}++static const JSCFunctionListEntry js_bigdecimal_proto_funcs[] = {+ JS_CFUNC_DEF("toString", 0, js_bigdecimal_toString ),+ JS_CFUNC_DEF("valueOf", 0, js_bigdecimal_valueOf ),+ JS_CFUNC_DEF("toPrecision", 1, js_bigdecimal_toPrecision ),+ JS_CFUNC_DEF("toFixed", 1, js_bigdecimal_toFixed ),+ JS_CFUNC_DEF("toExponential", 1, js_bigdecimal_toExponential ),+};++static const JSCFunctionListEntry js_bigdecimal_funcs[] = {+ JS_CFUNC_MAGIC_DEF("add", 2, js_bigdecimal_fop, MATH_OP_ADD ),+ JS_CFUNC_MAGIC_DEF("sub", 2, js_bigdecimal_fop, MATH_OP_SUB ),+ JS_CFUNC_MAGIC_DEF("mul", 2, js_bigdecimal_fop, MATH_OP_MUL ),+ JS_CFUNC_MAGIC_DEF("div", 2, js_bigdecimal_fop, MATH_OP_DIV ),+ JS_CFUNC_MAGIC_DEF("mod", 2, js_bigdecimal_fop, MATH_OP_FMOD ),+ JS_CFUNC_MAGIC_DEF("round", 1, js_bigdecimal_fop, MATH_OP_ROUND ),+ JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigdecimal_fop, MATH_OP_SQRT ),+};++void JS_AddIntrinsicBigDecimal(JSContext *ctx)+{+ JSRuntime *rt = ctx->rt;+ JSValueConst obj1;++ rt->bigdecimal_ops.to_string = js_bigdecimal_to_string;+ rt->bigdecimal_ops.from_string = js_string_to_bigdecimal;+ rt->bigdecimal_ops.unary_arith = js_unary_arith_bigdecimal;+ rt->bigdecimal_ops.binary_arith = js_binary_arith_bigdecimal;+ rt->bigdecimal_ops.compare = js_compare_bigdecimal;++ ctx->class_proto[JS_CLASS_BIG_DECIMAL] = JS_NewObject(ctx);+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL],+ js_bigdecimal_proto_funcs,+ countof(js_bigdecimal_proto_funcs));+ obj1 = JS_NewGlobalCConstructor(ctx, "BigDecimal",+ js_bigdecimal_constructor, 1,+ ctx->class_proto[JS_CLASS_BIG_DECIMAL]);+ JS_SetPropertyFunctionList(ctx, obj1, js_bigdecimal_funcs,+ countof(js_bigdecimal_funcs));+}++void JS_EnableBignumExt(JSContext *ctx, BOOL enable)+{+ ctx->bignum_ext = enable;+}++#endif /* CONFIG_BIGNUM */++static const char * const native_error_name[JS_NATIVE_ERROR_COUNT] = {+ "EvalError", "RangeError", "ReferenceError",+ "SyntaxError", "TypeError", "URIError",+ "InternalError", "AggregateError",+};++/* Minimum amount of objects to be able to compile code and display+ error messages. No JSAtom should be allocated by this function. */+static void JS_AddIntrinsicBasicObjects(JSContext *ctx)+{+ JSValue proto;+ int i;++ ctx->class_proto[JS_CLASS_OBJECT] = JS_NewObjectProto(ctx, JS_NULL);+ ctx->function_proto = JS_NewCFunction3(ctx, js_function_proto, "", 0,+ JS_CFUNC_generic, 0,+ ctx->class_proto[JS_CLASS_OBJECT]);+ ctx->class_proto[JS_CLASS_BYTECODE_FUNCTION] = JS_DupValue(ctx, ctx->function_proto);+ ctx->class_proto[JS_CLASS_ERROR] = JS_NewObject(ctx);+#if 0+ /* these are auto-initialized from js_error_proto_funcs,+ but delaying might be a problem */+ JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ERROR], JS_ATOM_name,+ JS_AtomToString(ctx, JS_ATOM_Error),+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+ JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ERROR], JS_ATOM_message,+ JS_AtomToString(ctx, JS_ATOM_empty_string),+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+#endif+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ERROR],+ js_error_proto_funcs,+ countof(js_error_proto_funcs));++ for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {+ proto = JS_NewObjectProto(ctx, ctx->class_proto[JS_CLASS_ERROR]);+ JS_DefinePropertyValue(ctx, proto, JS_ATOM_name,+ JS_NewAtomString(ctx, native_error_name[i]),+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+ JS_DefinePropertyValue(ctx, proto, JS_ATOM_message,+ JS_AtomToString(ctx, JS_ATOM_empty_string),+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);+ ctx->native_error_proto[i] = proto;+ }++ /* the array prototype is an array */+ ctx->class_proto[JS_CLASS_ARRAY] =+ JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],+ JS_CLASS_ARRAY);++ ctx->array_shape = js_new_shape2(ctx, get_proto_obj(ctx->class_proto[JS_CLASS_ARRAY]),+ JS_PROP_INITIAL_HASH_SIZE, 1);+ add_shape_property(ctx, &ctx->array_shape, NULL,+ JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_LENGTH);++ /* XXX: could test it on first context creation to ensure that no+ new atoms are created in JS_AddIntrinsicBasicObjects(). It is+ necessary to avoid useless renumbering of atoms after+ JS_EvalBinary() if it is done just after+ JS_AddIntrinsicBasicObjects(). */+ // assert(ctx->rt->atom_count == JS_ATOM_END);+}++void JS_AddIntrinsicBaseObjects(JSContext *ctx)+{+ int i;+ JSValueConst obj, number_obj;+ JSValue obj1;++ ctx->throw_type_error = JS_NewCFunction(ctx, js_throw_type_error, NULL, 0);++ /* add caller and arguments properties to throw a TypeError */+ obj1 = JS_NewCFunction(ctx, js_function_proto_caller, NULL, 0);+ JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_caller, JS_UNDEFINED,+ obj1, ctx->throw_type_error,+ JS_PROP_HAS_GET | JS_PROP_HAS_SET |+ JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE);+ JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_arguments, JS_UNDEFINED,+ obj1, ctx->throw_type_error,+ JS_PROP_HAS_GET | JS_PROP_HAS_SET |+ JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE);+ JS_FreeValue(ctx, obj1);+ JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, (JSValueConst *)&ctx->throw_type_error, 1));++ ctx->global_obj = JS_NewObject(ctx);+ ctx->global_var_obj = JS_NewObjectProto(ctx, JS_NULL);++ /* Object */+ obj = JS_NewGlobalCConstructor(ctx, "Object", js_object_constructor, 1,+ ctx->class_proto[JS_CLASS_OBJECT]);+ JS_SetPropertyFunctionList(ctx, obj, js_object_funcs, countof(js_object_funcs));+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_OBJECT],+ js_object_proto_funcs, countof(js_object_proto_funcs));++ /* Function */+ JS_SetPropertyFunctionList(ctx, ctx->function_proto, js_function_proto_funcs, countof(js_function_proto_funcs));+ ctx->function_ctor = JS_NewCFunctionMagic(ctx, js_function_constructor,+ "Function", 1, JS_CFUNC_constructor_or_func_magic,+ JS_FUNC_NORMAL);+ JS_NewGlobalCConstructor2(ctx, JS_DupValue(ctx, ctx->function_ctor), "Function",+ ctx->function_proto);++ /* Error */+ obj1 = JS_NewCFunctionMagic(ctx, js_error_constructor,+ "Error", 1, JS_CFUNC_constructor_or_func_magic, -1);+ JS_NewGlobalCConstructor2(ctx, obj1,+ "Error", ctx->class_proto[JS_CLASS_ERROR]);++ for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {+ JSValue func_obj;+ int n_args;+ n_args = 1 + (i == JS_AGGREGATE_ERROR);+ func_obj = JS_NewCFunction3(ctx, (JSCFunction *)js_error_constructor,+ native_error_name[i], n_args,+ JS_CFUNC_constructor_or_func_magic, i, obj1);+ JS_NewGlobalCConstructor2(ctx, func_obj, native_error_name[i],+ ctx->native_error_proto[i]);+ }++ /* Iterator prototype */+ ctx->iterator_proto = JS_NewObject(ctx);+ JS_SetPropertyFunctionList(ctx, ctx->iterator_proto,+ js_iterator_proto_funcs,+ countof(js_iterator_proto_funcs));++ /* Array */+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY],+ js_array_proto_funcs,+ countof(js_array_proto_funcs));++ obj = JS_NewGlobalCConstructor(ctx, "Array", js_array_constructor, 1,+ ctx->class_proto[JS_CLASS_ARRAY]);+ ctx->array_ctor = JS_DupValue(ctx, obj);+ JS_SetPropertyFunctionList(ctx, obj, js_array_funcs,+ countof(js_array_funcs));++ /* XXX: create auto_initializer */+ {+ /* initialize Array.prototype[Symbol.unscopables] */+ char const unscopables[] = "copyWithin" "\0" "entries" "\0" "fill" "\0" "find" "\0"+ "findIndex" "\0" "flat" "\0" "flatMap" "\0" "includes" "\0" "keys" "\0" "values" "\0";+ const char *p = unscopables;+ obj1 = JS_NewObjectProto(ctx, JS_NULL);+ for(p = unscopables; *p; p += strlen(p) + 1) {+ JS_DefinePropertyValueStr(ctx, obj1, p, JS_TRUE, JS_PROP_C_W_E);+ }+ JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ARRAY],+ JS_ATOM_Symbol_unscopables, obj1,+ JS_PROP_CONFIGURABLE);+ }++ /* needed to initialize arguments[Symbol.iterator] */+ ctx->array_proto_values =+ JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_values);++ ctx->class_proto[JS_CLASS_ARRAY_ITERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_ITERATOR],+ js_array_iterator_proto_funcs,+ countof(js_array_iterator_proto_funcs));++ /* parseFloat and parseInteger must be defined before Number+ because of the Number.parseFloat and Number.parseInteger+ aliases */+ JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_global_funcs,+ countof(js_global_funcs));++ /* Number */+ ctx->class_proto[JS_CLASS_NUMBER] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],+ JS_CLASS_NUMBER);+ JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_NUMBER], JS_NewInt32(ctx, 0));+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_NUMBER],+ js_number_proto_funcs,+ countof(js_number_proto_funcs));+ number_obj = JS_NewGlobalCConstructor(ctx, "Number", js_number_constructor, 1,+ ctx->class_proto[JS_CLASS_NUMBER]);+ JS_SetPropertyFunctionList(ctx, number_obj, js_number_funcs, countof(js_number_funcs));++ /* Boolean */+ ctx->class_proto[JS_CLASS_BOOLEAN] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],+ JS_CLASS_BOOLEAN);+ JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_BOOLEAN], JS_NewBool(ctx, FALSE));+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BOOLEAN], js_boolean_proto_funcs,+ countof(js_boolean_proto_funcs));+ JS_NewGlobalCConstructor(ctx, "Boolean", js_boolean_constructor, 1,+ ctx->class_proto[JS_CLASS_BOOLEAN]);++ /* String */+ ctx->class_proto[JS_CLASS_STRING] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],+ JS_CLASS_STRING);+ JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_STRING], JS_AtomToString(ctx, JS_ATOM_empty_string));+ obj = JS_NewGlobalCConstructor(ctx, "String", js_string_constructor, 1,+ ctx->class_proto[JS_CLASS_STRING]);+ JS_SetPropertyFunctionList(ctx, obj, js_string_funcs,+ countof(js_string_funcs));+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_funcs,+ countof(js_string_proto_funcs));++ ctx->class_proto[JS_CLASS_STRING_ITERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING_ITERATOR],+ js_string_iterator_proto_funcs,+ countof(js_string_iterator_proto_funcs));++ /* Math: create as autoinit object */+ js_random_init(ctx);+ JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_math_obj, countof(js_math_obj));++ /* ES6 Reflect: create as autoinit object */+ JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_reflect_obj, countof(js_reflect_obj));++ /* ES6 Symbol */+ ctx->class_proto[JS_CLASS_SYMBOL] = JS_NewObject(ctx);+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SYMBOL], js_symbol_proto_funcs,+ countof(js_symbol_proto_funcs));+ obj = JS_NewGlobalCConstructor(ctx, "Symbol", js_symbol_constructor, 0,+ ctx->class_proto[JS_CLASS_SYMBOL]);+ JS_SetPropertyFunctionList(ctx, obj, js_symbol_funcs,+ countof(js_symbol_funcs));+ for(i = JS_ATOM_Symbol_toPrimitive; i < JS_ATOM_END; i++) {+ char buf[ATOM_GET_STR_BUF_SIZE];+ const char *str, *p;+ str = JS_AtomGetStr(ctx, buf, sizeof(buf), i);+ /* skip "Symbol." */+ p = strchr(str, '.');+ if (p)+ str = p + 1;+ JS_DefinePropertyValueStr(ctx, obj, str, JS_AtomToValue(ctx, i), 0);+ }++ /* ES6 Generator */+ ctx->class_proto[JS_CLASS_GENERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_GENERATOR],+ js_generator_proto_funcs,+ countof(js_generator_proto_funcs));++ ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto);+ obj1 = JS_NewCFunctionMagic(ctx, js_function_constructor,+ "GeneratorFunction", 1,+ JS_CFUNC_constructor_or_func_magic, JS_FUNC_GENERATOR);+ JS_SetPropertyFunctionList(ctx,+ ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],+ js_generator_function_proto_funcs,+ countof(js_generator_function_proto_funcs));+ JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],+ ctx->class_proto[JS_CLASS_GENERATOR],+ JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE);+ JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],+ 0, JS_PROP_CONFIGURABLE);+ JS_FreeValue(ctx, obj1);++ /* global properties */+ ctx->eval_obj = JS_NewCFunction(ctx, js_global_eval, "eval", 1);+ JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_eval,+ JS_DupValue(ctx, ctx->eval_obj),+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);++ JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_globalThis,+ JS_DupValue(ctx, ctx->global_obj),+ JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);+}++/* Typed Arrays */++static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT] = {+ 0, 0, 0, 1, 1, 2, 2,+#ifdef CONFIG_BIGNUM+ 3, 3, /* BigInt64Array, BigUint64Array */+#endif+ 2, 3+};++static JSValue js_array_buffer_constructor3(JSContext *ctx,+ JSValueConst new_target,+ uint64_t len, JSClassID class_id,+ uint8_t *buf,+ JSFreeArrayBufferDataFunc *free_func,+ void *opaque, BOOL alloc_flag)+{+ JSRuntime *rt = ctx->rt;+ JSValue obj;+ JSArrayBuffer *abuf = NULL;++ obj = js_create_from_ctor(ctx, new_target, class_id);+ if (JS_IsException(obj))+ return obj;+ /* XXX: we are currently limited to 2 GB */+ if (len > INT32_MAX) {+ JS_ThrowRangeError(ctx, "invalid array buffer length");+ goto fail;+ }+ abuf = js_malloc(ctx, sizeof(*abuf));+ if (!abuf)+ goto fail;+ abuf->byte_length = len;+ if (alloc_flag) {+ if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER &&+ rt->sab_funcs.sab_alloc) {+ abuf->data = rt->sab_funcs.sab_alloc(rt->sab_funcs.sab_opaque,+ max_int(len, 1));+ if (!abuf->data)+ goto fail;+ memset(abuf->data, 0, len);+ } else {+ /* the allocation must be done after the object creation */+ abuf->data = js_mallocz(ctx, max_int(len, 1));+ if (!abuf->data)+ goto fail;+ }+ } else {+ if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER &&+ rt->sab_funcs.sab_dup) {+ rt->sab_funcs.sab_dup(rt->sab_funcs.sab_opaque, buf);+ }+ abuf->data = buf;+ }+ init_list_head(&abuf->array_list);+ abuf->detached = FALSE;+ abuf->shared = (class_id == JS_CLASS_SHARED_ARRAY_BUFFER);+ abuf->opaque = opaque;+ abuf->free_func = free_func;+ if (alloc_flag && buf)+ memcpy(abuf->data, buf, len);+ JS_SetOpaque(obj, abuf);+ return obj;+ fail:+ JS_FreeValue(ctx, obj);+ js_free(ctx, abuf);+ return JS_EXCEPTION;+}++static void js_array_buffer_free(JSRuntime *rt, void *opaque, void *ptr)+{+ js_free_rt(rt, ptr);+}++static JSValue js_array_buffer_constructor2(JSContext *ctx,+ JSValueConst new_target,+ uint64_t len, JSClassID class_id)+{+ return js_array_buffer_constructor3(ctx, new_target, len, class_id,+ NULL, js_array_buffer_free, NULL,+ TRUE);+}++static JSValue js_array_buffer_constructor1(JSContext *ctx,+ JSValueConst new_target,+ uint64_t len)+{+ return js_array_buffer_constructor2(ctx, new_target, len,+ JS_CLASS_ARRAY_BUFFER);+}++JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len,+ JSFreeArrayBufferDataFunc *free_func, void *opaque,+ BOOL is_shared)+{+ return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len,+ is_shared ? JS_CLASS_SHARED_ARRAY_BUFFER : JS_CLASS_ARRAY_BUFFER,+ buf, free_func, opaque, FALSE);+}++/* create a new ArrayBuffer of length 'len' and copy 'buf' to it */+JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len)+{+ return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len,+ JS_CLASS_ARRAY_BUFFER,+ (uint8_t *)buf,+ js_array_buffer_free, NULL,+ TRUE);+}++static JSValue js_array_buffer_constructor(JSContext *ctx,+ JSValueConst new_target,+ int argc, JSValueConst *argv)+{+ uint64_t len;+ if (JS_ToIndex(ctx, &len, argv[0]))+ return JS_EXCEPTION;+ return js_array_buffer_constructor1(ctx, new_target, len);+}++static JSValue js_shared_array_buffer_constructor(JSContext *ctx,+ JSValueConst new_target,+ int argc, JSValueConst *argv)+{+ uint64_t len;+ if (JS_ToIndex(ctx, &len, argv[0]))+ return JS_EXCEPTION;+ return js_array_buffer_constructor2(ctx, new_target, len,+ JS_CLASS_SHARED_ARRAY_BUFFER);+}++/* also used for SharedArrayBuffer */+static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val)+{+ JSObject *p = JS_VALUE_GET_OBJ(val);+ JSArrayBuffer *abuf = p->u.array_buffer;+ if (abuf) {+ /* The ArrayBuffer finalizer may be called before the typed+ array finalizers using it, so abuf->array_list is not+ necessarily empty. */+ // assert(list_empty(&abuf->array_list));+ if (abuf->shared && rt->sab_funcs.sab_free) {+ rt->sab_funcs.sab_free(rt->sab_funcs.sab_opaque, abuf->data);+ } else {+ if (abuf->free_func)+ abuf->free_func(rt, abuf->opaque, abuf->data);+ }+ js_free_rt(rt, abuf);+ }+}++static JSValue js_array_buffer_isView(JSContext *ctx,+ JSValueConst this_val,+ int argc, JSValueConst *argv)+{+ JSObject *p;+ BOOL res;+ res = FALSE;+ if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) {+ p = JS_VALUE_GET_OBJ(argv[0]);+ if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&+ p->class_id <= JS_CLASS_DATAVIEW) {+ res = TRUE;+ }+ }+ return JS_NewBool(ctx, res);+}++static const JSCFunctionListEntry js_array_buffer_funcs[] = {+ JS_CFUNC_DEF("isView", 1, js_array_buffer_isView ),+ JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),+};++static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx)+{+ return JS_ThrowTypeError(ctx, "ArrayBuffer is detached");+}++static JSValue js_array_buffer_get_byteLength(JSContext *ctx,+ JSValueConst this_val,+ int class_id)+{+ JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id);+ if (!abuf)+ return JS_EXCEPTION;+ /* return 0 if detached */ return JS_NewUint32(ctx, abuf->byte_length); }
quickjs/quickjs.h view
@@ -776,7 +776,10 @@ /* 'input' must be zero terminated i.e. input[input_len] = '\0'. */ JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len, const char *filename, int eval_flags);-JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj);+/* same as JS_Eval() but with an explicit 'this_obj' parameter */+JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj,+ const char *input, size_t input_len,+ const char *filename, int eval_flags); JSValue JS_GetGlobalObject(JSContext *ctx); int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj); int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,@@ -885,7 +888,9 @@ #define JS_READ_OBJ_REFERENCE (1 << 3) /* allow object references */ JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len, int flags);-+/* instantiate and evaluate a bytecode function. Only used when+ reading a script or module with JS_ReadObject() */+JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj); /* load the dependencies of the module 'obj'. Useful when JS_ReadObject() returns a module. */ int JS_ResolveModule(JSContext *ctx, JSValueConst obj);