diff options
author | bellard <6490144+bellard@users.noreply.github.com> | 2020-11-08 14:30:56 +0100 |
---|---|---|
committer | bellard <6490144+bellard@users.noreply.github.com> | 2020-11-08 14:30:56 +0100 |
commit | b1f67dfc1a7372dd665246cf1c203528e5057e42 (patch) | |
tree | b68346b1d20aa2e709c44a4f7ea902dd3ba6336f /quickjs.c | |
parent | 7c312df422572cf867f29a1d80693e8a77f7fb2a (diff) | |
download | quickjs-b1f67dfc1a7372dd665246cf1c203528e5057e42.tar.gz quickjs-b1f67dfc1a7372dd665246cf1c203528e5057e42.zip |
2020-11-08 release
Diffstat (limited to 'quickjs.c')
-rw-r--r-- | quickjs.c | 2541 |
1 files changed, 1383 insertions, 1158 deletions
@@ -199,7 +199,7 @@ typedef enum JSErrorEnum { } JSErrorEnum; #define JS_MAX_LOCAL_VARS 65536 -#define JS_STACK_SIZE_MAX 65536 +#define JS_STACK_SIZE_MAX 65534 #define JS_STRING_LEN_MAX ((1 << 30) - 1) #define __exception __attribute__((warn_unused_result)) @@ -506,14 +506,17 @@ typedef struct JSClosureVar { 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 */ + 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 */ @@ -526,6 +529,7 @@ typedef enum { 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, @@ -533,12 +537,21 @@ typedef enum { 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; - 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 */ + /* 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; @@ -548,7 +561,9 @@ typedef struct JSVarDef { 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 */ + 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 */ @@ -855,7 +870,7 @@ struct JSObject { 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 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() */ @@ -1000,7 +1015,7 @@ static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_o 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); + 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, ...); @@ -1226,11 +1241,11 @@ 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, +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 int JS_InstantiateFunctionListItem(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; @@ -6710,6 +6725,21 @@ static JSValue JS_ThrowReferenceErrorUninitialized(JSContext *ctx, JSAtom name) 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; @@ -6986,25 +7016,37 @@ int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj) return JS_OrdinaryIsInstanceOf(ctx, val, obj); } -typedef int JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); +/* 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_InstantiateFunctionListItem, /* JS_AUTOINIT_ID_PROP */ + JS_InstantiateFunctionListItem2, /* JS_AUTOINIT_ID_PROP */ }; +/* warning: 'prs' is reallocated after it */ static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop, - JSProperty *pr) + JSProperty *pr, JSShapeProperty *prs) { - int ret; + 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)]; - ret = func(realm, p, prop, pr->u.init.opaque); - return ret; + /* '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, @@ -7075,7 +7117,7 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, 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)) + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) return JS_EXCEPTION; continue; } @@ -7093,7 +7135,7 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, 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; + return JS_UNDEFINED; } } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY) { @@ -7102,9 +7144,6 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, 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; } } @@ -7337,6 +7376,22 @@ static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func) 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; @@ -7379,7 +7434,7 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, 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; + uint32_t num_index, str_index, sym_index, exotic_count, exotic_keys_count; BOOL is_enumerable, num_sorted; uint32_t num_key; JSAtomKindEnum kind; @@ -7392,6 +7447,7 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, 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; @@ -7425,19 +7481,13 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, 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 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) { @@ -7466,13 +7516,7 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, 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++; - } + exotic_keys_count++; } } } @@ -7482,7 +7526,7 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, /* fill them */ - atom_count = num_keys_count + str_keys_count + sym_keys_count; + 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) { @@ -7518,12 +7562,19 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, } if (p->is_exotic) { + int len; if (p->fast_array) { if (flags & JS_GPN_STRING_MASK) { - for(i = 0; i < p->u.array.count; i++) { + 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_exotic, exotic_count); js_free_prop_enum(ctx, tab_atom, num_index); return -1; } @@ -7531,30 +7582,23 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, num_index++; } } - } - if (exotic_count > 0) { + } 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) { - 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; + 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); } - js_free(ctx, tab_exotic); } assert(num_index == num_keys_count); @@ -7614,7 +7658,7 @@ retry: 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)) + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) return -1; goto retry; } @@ -7642,10 +7686,8 @@ retry: 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->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); @@ -7653,19 +7695,6 @@ retry: 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) { @@ -7748,8 +7777,6 @@ int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop) if (ret != 0) { if (ret < 0) return -1; - /* the detached array test was done in - JS_GetOwnPropertyInternal() */ return FALSE; } } @@ -8074,7 +8101,7 @@ static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) return -1; goto redo; } else { - return FALSE; /* not configurable */ + return FALSE; } } } else { @@ -8114,15 +8141,19 @@ static int call_setter(JSContext *ctx, JSObject *setter, } /* set the array length and remove the array elements if necessary. */ -static int set_array_length(JSContext *ctx, JSObject *p, JSValue val, int flags) +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); + 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; @@ -8382,8 +8413,7 @@ retry: /* 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)) { + } 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); @@ -8399,7 +8429,7 @@ retry: 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)) { + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) { JS_FreeValue(ctx, val); return -1; } @@ -8437,10 +8467,6 @@ retry: 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"); } } @@ -8509,7 +8535,7 @@ retry: 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)) + if (JS_AutoInitProperty(ctx, p1, prop, pr, prs)) return -1; goto retry2; } else if (!(prs->flags & JS_PROP_WRITABLE)) { @@ -8677,12 +8703,7 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, 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"); - } + return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index"); } p->u.array.u.double_ptr[idx] = d; break; @@ -8967,13 +8988,32 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst 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"); } - retry: + 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)) { @@ -8996,14 +9036,6 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, /* 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); } @@ -9051,39 +9083,17 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, 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; + (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_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 ((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 @@ -9107,9 +9117,27 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, 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 (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); @@ -9194,9 +9222,9 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, 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); + 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)) { + 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) { @@ -9581,9 +9609,8 @@ static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val, set_value(ctx, &pr->u.value, val); return 0; } - flags = JS_PROP_THROW_STRICT; - if (flag != 2 && is_strict_mode(ctx)) + if (is_strict_mode(ctx)) flags |= JS_PROP_NO_ADD; return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, flags); } @@ -10918,11 +10945,10 @@ static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val) } static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, - JSValue val) + JSValue val, BOOL is_array_ctor) { uint32_t tag, len; - redo: tag = JS_VALUE_GET_TAG(val); switch(tag) { case JS_TAG_INT: @@ -10959,16 +10985,36 @@ static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, double d; d = JS_VALUE_GET_FLOAT64(val); len = (uint32_t)d; - if (len != d) { - fail: - JS_ThrowRangeError(ctx, "invalid array length"); - return -1; - } + if (len != d) + goto fail; } else { - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) - return -1; - goto redo; + 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; } @@ -15031,15 +15077,6 @@ static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj) 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; @@ -15349,16 +15386,6 @@ static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset) 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) { @@ -15663,7 +15690,7 @@ static JSValue js_closure2(JSContext *ctx, JSValue func_obj, return JS_EXCEPTION; } -static int js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque) +static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque) { JSValue obj, this_val; int ret; @@ -15671,15 +15698,17 @@ static int js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, vo this_val = JS_MKPTR(JS_TAG_OBJECT, p); obj = JS_NewObject(ctx); if (JS_IsException(obj)) - return -1; + 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 (JS_DefinePropertyValue(ctx, this_val, atom, obj, JS_PROP_WRITABLE) < 0 || ret < 0) - return -1; - return 0; + if (ret < 0) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + return obj; } static const uint16_t func_kind_to_class_id[] = { @@ -16645,11 +16674,12 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, JS_Throw(ctx, *--sp); goto exception; - CASE(OP_throw_var): + 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_VAR_DELETE_SUPER 3 +#define JS_THROW_ERROR_DELETE_SUPER 3 +#define JS_THROW_ERROR_ITERATOR_THROW 4 { JSAtom atom; int type; @@ -16665,9 +16695,12 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, if (type == JS_THROW_VAR_UNINITIALIZED) JS_ThrowReferenceErrorUninitialized(ctx, atom); else - if (type == JS_THROW_VAR_DELETE_SUPER) + 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; @@ -16995,7 +17028,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, pc += 2; val = *var_refs[idx]->pvalue; if (unlikely(JS_IsUninitialized(val))) { - JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL); + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); goto exception; } sp[0] = JS_DupValue(ctx, val); @@ -17008,7 +17041,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, idx = get_u16(pc); pc += 2; if (unlikely(JS_IsUninitialized(*var_refs[idx]->pvalue))) { - JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL); + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); goto exception; } set_value(ctx, var_refs[idx]->pvalue, sp[-1]); @@ -17021,7 +17054,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, idx = get_u16(pc); pc += 2; if (unlikely(!JS_IsUninitialized(*var_refs[idx]->pvalue))) { - JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL); + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); goto exception; } set_value(ctx, var_refs[idx]->pvalue, sp[-1]); @@ -17042,7 +17075,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, idx = get_u16(pc); pc += 2; if (unlikely(JS_IsUninitialized(var_buf[idx]))) { - JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL); + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE); goto exception; } sp[0] = JS_DupValue(ctx, var_buf[idx]); @@ -17055,7 +17088,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, idx = get_u16(pc); pc += 2; if (unlikely(JS_IsUninitialized(var_buf[idx]))) { - JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL); + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE); goto exception; } set_value(ctx, &var_buf[idx], sp[-1]); @@ -17294,16 +17327,17 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, 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_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 -> */ @@ -17340,37 +17374,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, } 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): + CASE(OP_iterator_next): /* stack: iter_obj next catch_offset val */ { JSValue ret; @@ -17383,26 +17387,28 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, } BREAK; - CASE(OP_async_iterator_get): + CASE(OP_iterator_call): /* 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); + 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 { - ret = JS_CallFree(ctx, method, sp[-4], - 1, (JSValueConst *)(sp - 1)); + 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]); @@ -17728,7 +17734,8 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, CASE(OP_put_ref_value): { - int ret; + 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]); @@ -17740,8 +17747,11 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, } 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], JS_PROP_THROW_STRICT); + ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], flags); JS_FreeValue(ctx, sp[-3]); sp -= 3; if (unlikely(ret < 0)) @@ -18084,8 +18094,12 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, var_buf[idx] = JS_NewInt32(ctx, val + 1); } else { inc_loc_slow: - if (js_unary_arith_slow(ctx, var_buf + idx + 1, OP_inc)) + /* 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; @@ -18105,8 +18119,12 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, var_buf[idx] = JS_NewInt32(ctx, val - 1); } else { dec_loc_slow: - if (js_unary_arith_slow(ctx, var_buf + idx + 1, OP_dec)) + /* 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; @@ -18432,6 +18450,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, 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]); @@ -18922,13 +18941,11 @@ static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, 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: @@ -18940,85 +18957,16 @@ static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, } 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) { + if (magic == GEN_MAGIC_THROW && + s->state == JS_GENERATOR_STATE_SUSPENDED_YIELD) { 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[0] = JS_NewInt32(ctx, magic); sf->cur_sp++; exec_no_arg: s->func_state.throw_flag = FALSE; @@ -19032,17 +18980,14 @@ static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, 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) { - /* 'yield *' */ s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR; - iter_args[0] = JS_UNDEFINED; - argc = 1; - argv = iter_args; - goto redo; + /* return (value, done) object */ + *pdone = 2; } 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 { @@ -19853,15 +19798,15 @@ typedef struct BlockEnv { int has_iterator; } BlockEnv; -typedef struct JSHoistedDef { - int cpool_idx; /* -1 means variable global definition */ - uint8_t force_init : 1; /* initialize to undefined */ +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 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; + JSAtom var_name; /* variable name */ +} JSGlobalVar; typedef struct RelocEntry { struct RelocEntry *next; @@ -19924,6 +19869,7 @@ typedef struct JSFunctionDef { 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 @@ -19951,7 +19897,10 @@ typedef struct JSFunctionDef { 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 */ @@ -19967,10 +19916,11 @@ typedef struct JSFunctionDef { 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 hoisted_def_count; - int hoisted_def_size; - JSHoistedDef *hoisted_def; + int global_var_count; + int global_var_size; + JSGlobalVar *global_vars; DynBuf byte_code; int last_opcode_pos; /* -1 if no last opcode */ @@ -21475,7 +21425,7 @@ static BOOL js_is_live_code(JSParseState *s) { case OP_return_undef: case OP_return_async: case OP_throw: - case OP_throw_var: + case OP_throw_error: case OP_goto: #if SHORT_OPCODES case OP_goto8: @@ -21647,6 +21597,21 @@ static int find_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name) 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, @@ -21668,7 +21633,7 @@ static int find_var_in_child_scope(JSContext *ctx, JSFunctionDef *fd, 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, + if (is_child_scope(ctx, fd, vd->scope_next, scope_level)) return i; } @@ -21677,11 +21642,11 @@ static int find_var_in_child_scope(JSContext *ctx, JSFunctionDef *fd, } -static JSHoistedDef *find_hoisted_def(JSFunctionDef *fd, JSAtom name) +static JSGlobalVar *find_global_var(JSFunctionDef *fd, JSAtom name) { int i; - for(i = 0; i < fd->hoisted_def_count; i++) { - JSHoistedDef *hf = &fd->hoisted_def[i]; + for(i = 0; i < fd->global_var_count; i++) { + JSGlobalVar *hf = &fd->global_vars[i]; if (hf->var_name == name) return hf; } @@ -21689,9 +21654,9 @@ static JSHoistedDef *find_hoisted_def(JSFunctionDef *fd, JSAtom name) } -static JSHoistedDef *find_lexical_hoisted_def(JSFunctionDef *fd, JSAtom name) +static JSGlobalVar *find_lexical_global_var(JSFunctionDef *fd, JSAtom name) { - JSHoistedDef *hf = find_hoisted_def(fd, name); + JSGlobalVar *hf = find_global_var(fd, name); if (hf && hf->is_lexical) return hf; else @@ -21711,7 +21676,7 @@ static int find_lexical_decl(JSContext *ctx, JSFunctionDef *fd, JSAtom name, } if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_GLOBAL) { - if (find_lexical_hoisted_def(fd, name)) + if (find_lexical_global_var(fd, name)) return GLOBAL_VAR_OFFSET; } return -1; @@ -21800,6 +21765,7 @@ static int add_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name) 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; } @@ -21823,22 +21789,47 @@ 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; + 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, JSAtom name) +static int add_arguments_var(JSContext *ctx, JSFunctionDef *fd) { int idx = fd->arguments_var_idx; - if (idx < 0 && (idx = add_var(ctx, fd, name)) >= 0) { + 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; @@ -21854,32 +21845,27 @@ static int add_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name) 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 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) +/* add a global variable definition */ +static JSGlobalVar *add_global_var(JSContext *ctx, JSFunctionDef *s, + JSAtom name) { - JSHoistedDef *hf; + JSGlobalVar *hf; - if (js_resize_array(ctx, (void **)&s->hoisted_def, - sizeof(s->hoisted_def[0]), - &s->hoisted_def_size, s->hoisted_def_count + 1)) + 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->hoisted_def[s->hoisted_def_count++]; - hf->cpool_idx = cpool_idx; - hf->force_init = 0; - hf->is_lexical = is_lexical; + 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->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); - } + hf->var_name = JS_DupAtom(ctx, name); return hf; } @@ -21924,7 +21910,7 @@ static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name, goto redef_lex_error; } } else { - if (fd->scope_level == 1) { + 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"); @@ -21933,7 +21919,7 @@ static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name, } if (var_def_type != JS_VAR_DEF_FUNCTION_DECL && var_def_type != JS_VAR_DEF_NEW_FUNCTION_DECL && - fd->scope_level == 1 && + 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"); @@ -21944,8 +21930,8 @@ static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name, } if (fd->is_global_var) { - JSHoistedDef *hf; - hf = find_hoisted_def(fd, name); + 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"); @@ -21955,11 +21941,12 @@ static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name, 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); + 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 { @@ -21991,13 +21978,13 @@ static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name, return js_parse_error(s, "invalid redefinition of lexical identifier"); } if (fd->is_global_var) { - JSHoistedDef *hf; - hf = find_hoisted_def(fd, name); + 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_hoisted_def(s->ctx, fd, -1, name, -1, FALSE); + hf = add_global_var(s->ctx, fd, name); if (!hf) return -1; idx = GLOBAL_VAR_OFFSET; @@ -22010,7 +21997,7 @@ static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom 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; + fd->vars[idx].scope_next = fd->scope_level; } } break; @@ -22052,8 +22039,9 @@ static __exception int js_parse_function_decl2(JSParseState *s, 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 __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, @@ -22385,8 +22373,9 @@ static BOOL is_regexp_allowed(int tok) } } -#define SKIP_HAS_SEMI (1 << 0) -#define SKIP_HAS_ELLIPSIS (1 << 1) +#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 @@ -22459,7 +22448,10 @@ static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_ bits |= SKIP_HAS_ELLIPSIS; } break; - + case '=': + bits |= SKIP_HAS_ASSIGNMENT; + break; + case TOK_DIV_ASSIGN: tok_len = 2; goto parse_regexp; @@ -22574,7 +22566,7 @@ static __exception int js_parse_object_literal(JSParseState *s) if (s->token.val == TOK_ELLIPSIS) { if (next_token(s)) return -1; - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; emit_op(s, OP_null); /* dummy excludeList */ emit_op(s, OP_copy_data_properties); @@ -22633,7 +22625,7 @@ static __exception int js_parse_object_literal(JSParseState *s) } else { if (js_parse_expect(s, ':')) goto fail; - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) goto fail; if (name == JS_ATOM_NULL) { set_object_name_computed(s); @@ -22668,8 +22660,23 @@ static __exception int js_parse_object_literal(JSParseState *s) return -1; } -static __exception int js_parse_postfix_expr(JSParseState *s, - BOOL accept_lparen); +/* 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, @@ -22873,8 +22880,7 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, 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)) + if (js_parse_left_hand_side_expr(s)) goto fail; } else { emit_op(s, OP_undefined); @@ -23081,7 +23087,7 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, if (s->token.val == '=') { if (next_token(s)) goto fail; - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) goto fail; } else { emit_op(s, OP_undefined); @@ -23285,7 +23291,7 @@ static __exception int js_parse_array_literal(JSParseState *s) while (s->token.val != ']' && idx < 32) { if (s->token.val == ',' || s->token.val == TOK_ELLIPSIS) break; - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; idx++; /* accept trailing comma */ @@ -23306,7 +23312,7 @@ static __exception int js_parse_array_literal(JSParseState *s) break; need_length = TRUE; if (s->token.val != ',') { - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; emit_op(s, OP_define_field); emit_u32(s, __JS_AtomFromUInt32(idx)); @@ -23341,7 +23347,7 @@ static __exception int js_parse_array_literal(JSParseState *s) if (s->token.val == TOK_ELLIPSIS) { if (next_token(s)) return -1; - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; #if 1 emit_op(s, OP_append); @@ -23373,7 +23379,7 @@ static __exception int js_parse_array_literal(JSParseState *s) } else { need_length = TRUE; if (s->token.val != ',') { - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; /* a idx val */ emit_op(s, OP_define_array_el); @@ -23781,6 +23787,8 @@ fail: 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) @@ -23789,7 +23797,8 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, 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); @@ -23842,7 +23851,7 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, label_lvalue = -1; depth_lvalue = 0; } else { - if (js_parse_postfix_expr(s, TRUE)) + if (js_parse_left_hand_side_expr(s)) return -1; if (get_lvalue(s, &opcode, &scope, &var_name, @@ -23898,7 +23907,7 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, emit_op(s, OP_get_field2); emit_u32(s, prop_name); } - if (js_parse_destructuring_element(s, tok, is_arg, TRUE, -1, TRUE)) + if (js_parse_destructuring_element(s, tok, is_arg, TRUE, -1, TRUE) < 0) return -1; if (s->token.val == '}') break; @@ -23935,7 +23944,7 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, if (var_name == JS_ATOM_NULL) goto prop_error; } else { - if (js_parse_postfix_expr(s, TRUE)) + if (js_parse_left_hand_side_expr(s)) goto prop_error; lvalue: if (get_lvalue(s, &opcode, &scope, &var_name, @@ -24033,7 +24042,7 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, if (next_token(s)) goto var_error; emit_op(s, OP_drop); - if (js_parse_assign_expr(s, TRUE)) + 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); @@ -24097,7 +24106,7 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, 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)) + 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; @@ -24111,7 +24120,7 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, opcode = OP_scope_get_var; scope = s->cur_func->scope_level; } else { - if (js_parse_postfix_expr(s, TRUE)) + if (js_parse_left_hand_side_expr(s)) return -1; if (get_lvalue(s, &opcode, &scope, &var_name, &label_lvalue, &enum_depth, FALSE, '[')) { @@ -24135,7 +24144,7 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, if (next_token(s)) goto var_error; emit_op(s, OP_drop); - if (js_parse_assign_expr(s, TRUE)) + 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); @@ -24170,10 +24179,11 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, emit_label(s, label_parse); if (hasval) emit_op(s, OP_drop); - if (js_parse_assign_expr(s, TRUE)) + 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 */ @@ -24186,8 +24196,9 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, 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 0; + return has_initializer; prop_error: JS_FreeAtom(s->ctx, prop_name); @@ -24220,10 +24231,12 @@ static void optional_chain_test(JSParseState *s, int *poptional_chaining_label, emit_label(s, label_next); } -static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen) +/* 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) { @@ -24321,7 +24334,8 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen } break; case '(': - if (js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) { + 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)) @@ -24369,7 +24383,8 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen if (s->token.u.ident.is_reserved) { return js_parse_error_reserved_identifier(s); } - if (peek_token(s, TRUE) == TOK_ARROW) { + 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)) @@ -24388,10 +24403,11 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen 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)) { + } 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)) @@ -24421,7 +24437,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen { 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)) + if (js_parse_destructuring_element(s, 0, 0, FALSE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0) return -1; } else { if (s->token.val == '{') { @@ -24450,7 +24466,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen emit_atom(s, JS_ATOM_new_target); emit_u16(s, 0); } else { - if (js_parse_postfix_expr(s, FALSE)) + if (js_parse_postfix_expr(s, 0)) return -1; accept_lparen = TRUE; if (s->token.val != '(') { @@ -24503,7 +24519,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen return -1; if (!accept_lparen) return js_parse_error(s, "invalid use of 'import()'"); - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; if (js_parse_expect(s, ')')) return -1; @@ -24636,7 +24652,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen } if (s->token.val == TOK_ELLIPSIS) break; - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; arg_count++; if (s->token.val == ')') @@ -24656,7 +24672,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen if (s->token.val == TOK_ELLIPSIS) { if (next_token(s)) return -1; - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; #if 1 /* XXX: could pass is_last indicator? */ @@ -24687,7 +24703,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen emit_op(s, OP_nip1); #endif } else { - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; /* array idx val */ emit_op(s, OP_define_array_el); @@ -24789,16 +24805,19 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen } 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 { - parse_property: if (!token_is_ident(s->token.val)) { return js_parse_error(s, "expecting field name"); } @@ -24857,7 +24876,7 @@ static __exception int js_parse_delete(JSParseState *s) if (next_token(s)) return -1; - if (js_parse_unary(s, -1)) + if (js_parse_unary(s, PF_POW_FORBIDDEN)) return -1; switch(opcode = get_prev_opcode(fd)) { case OP_get_field: @@ -24896,9 +24915,9 @@ static __exception int js_parse_delete(JSParseState *s) 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_op(s, OP_throw_error); emit_atom(s, JS_ATOM_NULL); - emit_u8(s, JS_THROW_VAR_DELETE_SUPER); + emit_u8(s, JS_THROW_ERROR_DELETE_SUPER); break; default: ret_true: @@ -24909,7 +24928,8 @@ static __exception int js_parse_delete(JSParseState *s) return 0; } -static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) +/* 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; @@ -24922,7 +24942,7 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) op = s->token.val; if (next_token(s)) return -1; - if (js_parse_unary(s, -1)) + if (js_parse_unary(s, PF_POW_FORBIDDEN)) return -1; switch(op) { case '-': @@ -24944,7 +24964,7 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) default: abort(); } - exponentiation_flag = 0; + parse_flags = 0; break; case TOK_DEC: case TOK_INC: @@ -24954,7 +24974,6 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) 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)) @@ -24969,7 +24988,7 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) JSFunctionDef *fd; if (next_token(s)) return -1; - if (js_parse_unary(s, -1)) + if (js_parse_unary(s, PF_POW_FORBIDDEN)) return -1; /* reference access should not return an exception, so we patch the get_var */ @@ -24978,13 +24997,13 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_var_undef; } emit_op(s, OP_typeof); - exponentiation_flag = 0; + parse_flags = 0; } break; case TOK_DELETE: if (js_parse_delete(s)) return -1; - exponentiation_flag = 0; + parse_flags = 0; break; case TOK_AWAIT: if (!(s->cur_func->func_kind & JS_FUNC_ASYNC)) @@ -24993,13 +25012,14 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) return js_parse_error(s, "await in default expression"); if (next_token(s)) return -1; - if (js_parse_unary(s, -1)) + if (js_parse_unary(s, PF_POW_FORBIDDEN)) return -1; emit_op(s, OP_await); - exponentiation_flag = 0; + parse_flags = 0; break; default: - if (js_parse_postfix_expr(s, TRUE)) + 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)) { @@ -25016,21 +25036,21 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) } break; } - if (exponentiation_flag) { + 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 (exponentiation_flag < 0) { + 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, 1)) + if (js_parse_unary(s, PF_POW_ALLOWED)) return -1; emit_op(s, OP_pow); } @@ -25041,13 +25061,13 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) regarding the precedence of prefix operators and the postifx exponential, ES7 specifies that -2**2 is a syntax error. */ - if (exponentiation_flag < 0) { + 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, 1)) + if (js_parse_unary(s, PF_POW_ALLOWED)) return -1; emit_op(s, OP_pow); } @@ -25056,15 +25076,17 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) return 0; } +/* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */ static __exception int js_parse_expr_binary(JSParseState *s, int level, - BOOL in_accepted) + int parse_flags) { int op, opcode; if (level == 0) { - return js_parse_unary(s, 1); + return js_parse_unary(s, (parse_flags & PF_ARROW_FUNC) | + PF_POW_ALLOWED); } - if (js_parse_expr_binary(s, level - 1, in_accepted)) + if (js_parse_expr_binary(s, level - 1, parse_flags)) return -1; for(;;) { op = s->token.val; @@ -25134,7 +25156,7 @@ static __exception int js_parse_expr_binary(JSParseState *s, int level, opcode = OP_instanceof; break; case TOK_IN: - if (in_accepted) { + if (parse_flags & PF_IN_ACCEPTED) { opcode = OP_in; } else { return 0; @@ -25194,23 +25216,24 @@ static __exception int js_parse_expr_binary(JSParseState *s, int level, } if (next_token(s)) return -1; - if (js_parse_expr_binary(s, level - 1, in_accepted)) + 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, - BOOL in_accepted) + int parse_flags) { int label1; if (op == TOK_LAND) { - if (js_parse_expr_binary(s, 8, in_accepted)) + if (js_parse_expr_binary(s, 8, parse_flags)) return -1; } else { - if (js_parse_logical_and_or(s, TOK_LAND, in_accepted)) + if (js_parse_logical_and_or(s, TOK_LAND, parse_flags)) return -1; } if (s->token.val == op) { @@ -25224,10 +25247,11 @@ static __exception int js_parse_logical_and_or(JSParseState *s, int op, emit_op(s, OP_drop); if (op == TOK_LAND) { - if (js_parse_expr_binary(s, 8, in_accepted)) + if (js_parse_expr_binary(s, 8, parse_flags & ~PF_ARROW_FUNC)) return -1; } else { - if (js_parse_logical_and_or(s, TOK_LAND, in_accepted)) + if (js_parse_logical_and_or(s, TOK_LAND, + parse_flags & ~PF_ARROW_FUNC)) return -1; } if (s->token.val != op) { @@ -25242,11 +25266,11 @@ static __exception int js_parse_logical_and_or(JSParseState *s, int op, return 0; } -static __exception int js_parse_coalesce_expr(JSParseState *s, BOOL in_accepted) +static __exception int js_parse_coalesce_expr(JSParseState *s, int parse_flags) { int label1; - if (js_parse_logical_and_or(s, TOK_LOR, in_accepted)) + 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); @@ -25259,7 +25283,7 @@ static __exception int js_parse_coalesce_expr(JSParseState *s, BOOL in_accepted) emit_goto(s, OP_if_false, label1); emit_op(s, OP_drop); - if (js_parse_expr_binary(s, 8, in_accepted)) + if (js_parse_expr_binary(s, 8, parse_flags & ~PF_ARROW_FUNC)) return -1; if (s->token.val != TOK_DOUBLE_QUESTION_MARK) break; @@ -25269,18 +25293,19 @@ static __exception int js_parse_coalesce_expr(JSParseState *s, BOOL in_accepted) return 0; } -static __exception int js_parse_cond_expr(JSParseState *s, BOOL in_accepted) +/* 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, in_accepted)) + 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, TRUE)) + if (js_parse_assign_expr(s)) return -1; if (js_parse_expect(s, ':')) return -1; @@ -25289,7 +25314,7 @@ static __exception int js_parse_cond_expr(JSParseState *s, BOOL in_accepted) emit_label(s, label1); - if (js_parse_assign_expr(s, in_accepted)) + if (js_parse_assign_expr2(s, parse_flags & PF_IN_ACCEPTED)) return -1; emit_label(s, label2); @@ -25299,14 +25324,16 @@ static __exception int js_parse_cond_expr(JSParseState *s, BOOL in_accepted) static void emit_return(JSParseState *s, BOOL hasval); -static __exception int js_parse_assign_expr(JSParseState *s, BOOL in_accepted) +/* 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; + 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) @@ -25323,109 +25350,120 @@ static __exception int js_parse_assign_expr(JSParseState *s, BOOL in_accepted) if (next_token(s)) return -1; } - if (js_parse_assign_expr(s, in_accepted)) + if (js_parse_assign_expr2(s, parse_flags)) return -1; } else { emit_op(s, OP_undefined); } - if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) { + 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; - if (is_star) { - label_loop = new_label(s); - label_yield = new_label(s); - - emit_op(s, OP_for_await_of_start); + label_loop = new_label(s); + label_yield = new_label(s); - /* 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_op(s, is_async ? OP_for_await_of_start : OP_for_of_start); - emit_label(s, label_loop); - emit_op(s, OP_async_iterator_next); + /* 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_get_value_done); - label_next = emit_goto(s, OP_if_true, -1); /* end of loop */ + 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_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); + } 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_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_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_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_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_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_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_async_iterator_get); - emit_u8(s, 2); /* throw the type error exception */ - emit_op(s, OP_drop); /* never reached */ + emit_label(s, label_throw2); - 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); - } + 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_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); - } + + 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); @@ -25436,7 +25474,7 @@ static __exception int js_parse_assign_expr(JSParseState *s, BOOL in_accepted) /* 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)) + if (js_parse_cond_expr(s, parse_flags | PF_ARROW_FUNC)) return -1; op = s->token.val; @@ -25447,7 +25485,7 @@ static __exception int js_parse_assign_expr(JSParseState *s, BOOL in_accepted) if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, (op != '='), op) < 0) return -1; - if (js_parse_assign_expr(s, in_accepted)) { + if (js_parse_assign_expr2(s, parse_flags)) { JS_FreeAtom(s->ctx, name); return -1; } @@ -25491,7 +25529,7 @@ static __exception int js_parse_assign_expr(JSParseState *s, BOOL in_accepted) -1); emit_op(s, OP_drop); - if (js_parse_assign_expr(s, in_accepted)) { + if (js_parse_assign_expr2(s, parse_flags)) { JS_FreeAtom(s->ctx, name); return -1; } @@ -25533,11 +25571,17 @@ static __exception int js_parse_assign_expr(JSParseState *s, BOOL in_accepted) return 0; } -static __exception int js_parse_expr2(JSParseState *s, BOOL in_accepted) +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_expr(s, in_accepted)) + if (js_parse_assign_expr2(s, parse_flags)) return -1; if (comma) { /* prevent get_lvalue from using the last expression @@ -25559,7 +25603,7 @@ static __exception int js_parse_expr2(JSParseState *s, BOOL in_accepted) static __exception int js_parse_expr(JSParseState *s) { - return js_parse_expr2(s, TRUE); + return js_parse_expr2(s, PF_IN_ACCEPTED); } static void push_break_entry(JSFunctionDef *fd, BlockEnv *be, @@ -25655,12 +25699,25 @@ static void emit_return(JSParseState *s, BOOL hasval) } 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); + 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); } @@ -25748,7 +25805,8 @@ static __exception int js_parse_block(JSParseState *s) return 0; } -static __exception int js_parse_var(JSParseState *s, BOOL in_accepted, int tok, +/* 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; @@ -25788,7 +25846,7 @@ static __exception int js_parse_var(JSParseState *s, BOOL in_accepted, int tok, 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)) { + if (js_parse_assign_expr2(s, parse_flags)) { JS_FreeAtom(ctx, name1); goto var_error; } @@ -25796,7 +25854,7 @@ static __exception int js_parse_var(JSParseState *s, BOOL in_accepted, int tok, put_lvalue(s, opcode, scope, name1, label, PUT_LVALUE_NOKEEP, FALSE); } else { - if (js_parse_assign_expr(s, in_accepted)) + 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) ? @@ -25823,7 +25881,7 @@ static __exception int js_parse_var(JSParseState *s, BOOL in_accepted, int tok, 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)) + 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"); @@ -25952,7 +26010,7 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name, 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)) + if (js_parse_destructuring_element(s, tok, 0, TRUE, -1, FALSE) < 0) return -1; has_destructuring = TRUE; } else { @@ -25978,11 +26036,11 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name, 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)) + 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_postfix_expr(s, TRUE)) + if (js_parse_left_hand_side_expr(s)) return -1; if (get_lvalue(s, &opcode, &scope, &var_name, &lvalue_label, NULL, FALSE, TOK_FOR)) @@ -26002,7 +26060,7 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name, /* 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)) { + if (next_token(s) || js_parse_assign_expr2(s, 0)) { JS_FreeAtom(ctx, var_name); return -1; } @@ -26035,7 +26093,7 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name, if (next_token(s)) return -1; if (is_for_of) { - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; } else { if (js_parse_expr(s)) @@ -26088,7 +26146,11 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name, if (is_for_of) { if (is_async) { /* call the next method */ - emit_op(s, OP_for_await_of_next); + /* 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 */ @@ -26646,7 +26708,7 @@ static __exception int js_parse_statement_or_decl(JSParseState *s, 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)) + if (js_parse_destructuring_element(s, TOK_LET, 0, TRUE, -1, TRUE) < 0) goto fail; } else { js_parse_error(s, "identifier expected"); @@ -26712,18 +26774,37 @@ static __exception int js_parse_statement_or_decl(JSParseState *s, } emit_label(s, label_finally); if (s->token.val == TOK_FINALLY) { - int saved_eval_ret_idx; + 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); - saved_eval_ret_idx = s->cur_func->eval_ret_idx; - s->cur_func->eval_ret_idx = -1; - /* 'finally' does not update eval_ret */ + + 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; - s->cur_func->eval_ret_idx = saved_eval_ret_idx; + + 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); @@ -27511,19 +27592,11 @@ static int exported_names_cmp(const void *p1, const void *p2, void *opaque) static JSValue js_get_module_ns(JSContext *ctx, JSModuleDef *m); -static int js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, - void *opaque) +static JSValue 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; + return js_get_module_ns(ctx, m); } static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m) @@ -27741,7 +27814,7 @@ static int js_create_module_bytecode_function(JSContext *ctx, JSModuleDef *m) return -1; } -/* must be done before js_instantiate_module() because of cyclic references */ +/* 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; @@ -27784,7 +27857,7 @@ static int js_create_module_function(JSContext *ctx, JSModuleDef *m) /* Prepare a module to be executed by resolving all the imported variables. */ -static int js_instantiate_module(JSContext *ctx, JSModuleDef *m) +static int js_link_module(JSContext *ctx, JSModuleDef *m) { int i; JSImportEntry *mi; @@ -27792,7 +27865,8 @@ static int js_instantiate_module(JSContext *ctx, JSModuleDef *m) JSVarRef **var_refs, *var_ref; JSObject *p; BOOL is_c_module; - + JSValue ret_val; + if (m->instantiated) return 0; m->instantiated = TRUE; @@ -27806,7 +27880,7 @@ static int js_instantiate_module(JSContext *ctx, JSModuleDef *m) 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) + if (js_link_module(ctx, rme->module) < 0) goto fail; } @@ -27925,6 +27999,12 @@ static int js_instantiate_module(JSContext *ctx, JSModuleDef *m) 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 @@ -28324,7 +28404,7 @@ static __exception int js_parse_export(JSParseState *s) s->token.ptr, s->token.line_num, JS_PARSE_EXPORT_DEFAULT, NULL); } else { - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; } /* set the name of anonymous functions */ @@ -28567,7 +28647,9 @@ static JSFunctionDef *js_new_function_def(JSContext *ctx, 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; @@ -28576,13 +28658,14 @@ static JSFunctionDef *js_new_function_def(JSContext *ctx, 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->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; @@ -28663,10 +28746,10 @@ static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd) } js_free(ctx, fd->args); - for(i = 0; i < fd->hoisted_def_count; i++) { - JS_FreeAtom(ctx, fd->hoisted_def[i].var_name); + for(i = 0; i < fd->global_var_count; i++) { + JS_FreeAtom(ctx, fd->global_vars[i].var_name); } - js_free(ctx, fd->hoisted_def); + js_free(ctx, fd->global_vars); for(i = 0; i < fd->closure_var_count; i++) { JSClosureVar *cv = &fd->closure_var[i]; @@ -29350,19 +29433,27 @@ static int resolve_pseudo_var(JSContext *ctx, JSFunctionDef *s, 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); + 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 */ - var_idx = s->this_active_func_var_idx = add_var(ctx, s, var_name); + 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 */ - var_idx = s->new_target_var_idx = add_var(ctx, s, var_name); + 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 */ - var_idx = s->this_var_idx = add_var_this(ctx, s); + 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; @@ -29371,18 +29462,32 @@ static int resolve_pseudo_var(JSContext *ctx, JSFunctionDef *s, 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 arg_valid) + LabelSlot *ls, int pos_next) { int idx, var_idx, is_put; int label_done; - BOOL is_func_var = FALSE; JSFunctionDef *fd; JSVarDef *vd; - BOOL is_pseudo_var; + BOOL is_pseudo_var, is_arg_scope; label_done = -1; @@ -29400,12 +29505,11 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, 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_putc(bc, OP_throw_error); 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; @@ -29413,33 +29517,16 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, 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++; + 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) { - /* 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; + /* 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) @@ -29448,21 +29535,31 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, 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); + 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); - is_func_var = TRUE; } } 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 (is_func_var) { + 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); @@ -29510,7 +29607,6 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, 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) { @@ -29543,41 +29639,32 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, goto done; } /* check eval object */ - if (s->var_object_idx >= 0 && !is_pseudo_var) { + 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); - 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++; + 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; - 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_putc(bc, OP_throw_error); 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; @@ -29587,26 +29674,19 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, 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++; + 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; - - 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_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); @@ -29614,33 +29694,39 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, break; } if (var_name == JS_ATOM_arguments && fd->has_arguments_binding) { - var_idx = add_arguments_var(ctx, fd, var_name); + 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); - 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; + 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, JS_ATOM__var_, + fd->var_object_idx, vd->var_name, 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++; + 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 */ } @@ -29665,6 +29751,7 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, } 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) { @@ -29678,13 +29765,7 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, } 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++; + var_object_test(ctx, s, var_name, op, bc, &label_done, is_with); } } } @@ -29709,14 +29790,14 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, 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_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 (is_func_var) { + 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); @@ -29956,7 +30037,7 @@ static int resolve_scope_private_field(JSContext *ctx, JSFunctionDef *s, break; case JS_VAR_PRIVATE_SETTER: /* XXX: add clearer error message */ - dbuf_putc(bc, OP_throw_var); + dbuf_putc(bc, OP_throw_error); dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); dbuf_putc(bc, JS_THROW_VAR_RO); break; @@ -29973,7 +30054,7 @@ static int resolve_scope_private_field(JSContext *ctx, JSFunctionDef *s, case JS_VAR_PRIVATE_METHOD: case JS_VAR_PRIVATE_GETTER: /* XXX: add clearer error message */ - dbuf_putc(bc, OP_throw_var); + dbuf_putc(bc, OP_throw_error); dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); dbuf_putc(bc, JS_THROW_VAR_RO); break; @@ -30025,17 +30106,33 @@ static void mark_eval_captured_variables(JSContext *ctx, JSFunctionDef *s, } } +/* 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; + 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 */ @@ -30051,8 +30148,14 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) 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 (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); @@ -30069,7 +30172,6 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) 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) @@ -30084,7 +30186,7 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) } /* add 'arguments' if it was not previously added */ if (!has_arguments_binding && fd->has_arguments_binding) { - add_arguments_var(ctx, fd, JS_ATOM_arguments); + add_arguments_var(ctx, fd); has_arguments_binding = TRUE; } /* add function name */ @@ -30092,6 +30194,7 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) 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; @@ -30099,24 +30202,37 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) 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); + 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); + 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) { @@ -30134,6 +30250,18 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) } } +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, @@ -30141,7 +30269,8 @@ static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s, { 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; @@ -30156,41 +30285,41 @@ static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s, 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); + set_closure_from_var(ctx, cv, vd, i); } 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_) { + 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 = FALSE; + 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]; @@ -30348,59 +30477,93 @@ static BOOL code_match(CodeContext *s, int pos, ...) static void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, DynBuf *bc) { - int i, idx, var_idx; + 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); + } + } - /* add the hoisted functions and variables */ - for(i = 0; i < s->hoisted_def_count; i++) { - JSHoistedDef *hf = &s->hoisted_def[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; - 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; - } + /* 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 (!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 (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) { @@ -30415,37 +30578,35 @@ static void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, Dy } 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)); - } + 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 { - 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); - } + /* 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_hoisted_def: + done_global_var: 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; + + 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, @@ -30525,7 +30686,7 @@ static int get_label_pos(JSFunctionDef *s, int label) 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; + int pos, pos_next, bc_len, op, len, i, idx, line_num; uint8_t *bc_buf; JSAtom var_name; DynBuf bc_out; @@ -30538,47 +30699,43 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) /* 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; + 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); } - - 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); + goto next; } - 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: ; } - arg_valid = 0; line_num = 0; /* avoid warning */ for (pos = 0; pos < bc_len; pos = pos_next) { op = bc_buf[pos]; @@ -30590,9 +30747,6 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) 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); @@ -30618,7 +30772,7 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) 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); + NULL, NULL, pos_next); JS_FreeAtom(ctx, var_name); break; case OP_scope_make_ref: @@ -30631,7 +30785,7 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) 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); + bc_buf, ls, pos_next); JS_FreeAtom(ctx, var_name); } break; @@ -30712,7 +30866,7 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) case OP_return: case OP_return_undef: case OP_throw: - case OP_throw_var: + case OP_throw_error: case OP_ret: if (OPTIMIZE) { /* remove dead code */ @@ -30746,25 +30900,27 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) { int scope_idx, scope = get_u16(bc_buf + pos + 1); - if (scope == 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 (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); + 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 { @@ -31167,6 +31323,8 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) 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 */ @@ -31181,6 +31339,11 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) 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; @@ -31248,7 +31411,7 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) case OP_return_undef: case OP_return_async: case OP_throw: - case OP_throw_var: + case OP_throw_error: pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num); goto no_change; @@ -31969,56 +32132,91 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) /* 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; -static __exception int compute_stack_size_rec(JSContext *ctx, - JSFunctionDef *fd, - StackSizeState *s, - int pos, int op, int stack_len) +/* 'op' is only used for error indication */ +static __exception int ss_check(JSContext *ctx, 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 ((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) - goto stack_overflow; + if (s->stack_len_max > JS_STACK_SIZE_MAX) { + JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos); + return -1; + } } - 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; - } + 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 { - s->stack_level_tab[pos] = stack_len; + 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); - return -1; + goto fail; } oi = &short_opcode_info(op); pos_next = pos + oi->size; - if (pos_next > bc_len) { - buf_overflow: + if (pos_next > s->bc_len) { JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos); - return -1; + goto fail; } n_pop = oi->n_pop; /* call pops a variable number of arguments */ @@ -32034,13 +32232,15 @@ static __exception int compute_stack_size_rec(JSContext *ctx, if (stack_len < n_pop) { JS_ThrowInternalError(ctx, "stack underflow (op=%d, pc=%d)", op, pos); - return -1; + 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) - goto stack_overflow; + 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: @@ -32049,9 +32249,9 @@ static __exception int compute_stack_size_rec(JSContext *ctx, case OP_return_undef: case OP_return_async: case OP_throw: - case OP_throw_var: + case OP_throw_error: case OP_ret: - goto done; + goto done_insn; case OP_goto: diff = get_u32(bc_buf + pos + 1); pos_next = pos + 1 + diff; @@ -32068,73 +32268,57 @@ static __exception int compute_stack_size_rec(JSContext *ctx, 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; + 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 (compute_stack_size_rec(ctx, fd, s, pos + 1 + diff, op, stack_len)) - return -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 (compute_stack_size_rec(ctx, fd, s, pos + 1 + diff, op, stack_len + 1)) - return -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 (compute_stack_size_rec(ctx, fd, s, pos + 5 + diff, op, stack_len + 1)) - return -1; + 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 (compute_stack_size_rec(ctx, fd, s, pos + 5 + diff, op, stack_len + 2)) - return -1; + 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 (compute_stack_size_rec(ctx, fd, s, pos + 5 + diff, op, stack_len - 1)) - return -1; + if (ss_check(ctx, s, pos + 5 + diff, op, stack_len - 1)) + goto fail; break; default: break; } - pos = pos_next; + if (ss_check(ctx, s, pos_next, op, stack_len)) + goto fail; + done_insn: ; } - 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); + js_free(ctx, s->pc_stack); *pstack_size = s->stack_len_max; - return ret; + 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) @@ -32142,14 +32326,14 @@ static int add_module_variables(JSContext *ctx, JSFunctionDef *fd) int i, idx; JSModuleDef *m = fd->module; JSExportEntry *me; - JSHoistedDef *hf; + 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->hoisted_def_count; i++) { - hf = &fd->hoisted_def[i]; + 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; @@ -32187,6 +32371,10 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) 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; @@ -32194,12 +32382,12 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) } for (scope = 2; scope < fd->scope_count; scope++) { JSVarScope *sd = &fd->scopes[scope]; - if (sd->first == -1) + 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 == -1 && vd->scope_level > 1) { + if (vd->scope_next < 0 && vd->scope_level > 1) { scope = fd->scopes[vd->scope_level].parent; vd->scope_next = fd->scopes[scope].first; } @@ -32571,7 +32759,8 @@ static int js_parse_function_check_names(JSParseState *s, JSFunctionDef *fd, /* 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) + if (fd->vars[i].var_name == name && + fd->vars[i].scope_level == 0) goto duplicate; } } @@ -32680,8 +32869,8 @@ static __exception int js_parse_function_decl2(JSParseState *s, 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); + 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"); @@ -32691,21 +32880,23 @@ static __exception int js_parse_function_decl2(JSParseState *s, } if (func_type == JS_PARSE_FUNC_VAR) { - /* Create lexical name here so function closure contains it */ 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 == 1) { + fd->scope_level == fd->body_scope) { /* avoid creating a lexical variable in the global scope. XXX: check annex B */ - JSHoistedDef *hf; - hf = find_hoisted_def(fd, func_name); + 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"); @@ -32780,6 +32971,7 @@ static __exception int js_parse_function_decl2(JSParseState *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; @@ -32792,13 +32984,30 @@ static __exception int js_parse_function_decl2(JSParseState *s, goto fail; fd->defined_arg_count = 1; } else { - if (js_parse_expect(s, '(')) - goto fail; + 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; + int idx, has_initializer; if (s->token.val == TOK_ELLIPSIS) { fd->has_simple_parameter_list = FALSE; @@ -32817,8 +33026,13 @@ static __exception int js_parse_function_decl2(JSParseState *s, emit_op(s, OP_get_arg); emit_u16(s, idx); } - if (js_parse_destructuring_element(s, TOK_VAR, 1, TRUE, -1, TRUE)) + 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); @@ -32829,6 +33043,11 @@ static __exception int js_parse_function_decl2(JSParseState *s, 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; @@ -32837,70 +33056,55 @@ static __exception int js_parse_function_decl2(JSParseState *s, 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; - /* 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; + 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 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); - } + 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_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); + 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++; } } else { js_parse_error(s, "missing formal parameter"); @@ -32922,6 +33126,41 @@ static __exception int js_parse_function_decl2(JSParseState *s, } } + 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; @@ -32933,7 +33172,8 @@ static __exception int js_parse_function_decl2(JSParseState *s, /* 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 */ + push_scope(s); /* enter body scope */ + fd->body_scope = fd->scope_level; if (s->token.val == TOK_ARROW) { if (next_token(s)) @@ -32943,7 +33183,7 @@ static __exception int js_parse_function_decl2(JSParseState *s, if (js_parse_function_check_names(s, fd, func_name)) goto fail; - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) goto fail; if (func_kind != JS_FUNC_NORMAL) @@ -33025,10 +33265,10 @@ done: emit_u32(s, idx); if (create_func_var) { if (s->cur_func->is_global_var) { - JSHoistedDef *hf; + JSGlobalVar *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); + hf = add_global_var(ctx, s->cur_func, func_name); if (!hf) goto fail; /* it is considered as defined at the top level @@ -33058,7 +33298,7 @@ done: } 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; + s->cur_func->vars[lexical_func_idx].func_pool_idx = idx; emit_op(s, OP_drop); } else { /* store function object into its lexical name */ @@ -33074,17 +33314,23 @@ done: 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; + 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 */ - if (!add_hoisted_def(ctx, s->cur_func, idx, func_var_name, -1, FALSE)) + 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)) @@ -33186,7 +33432,7 @@ static JSValue JS_EvalFunctionInternal(JSContext *ctx, JSValue fun_obj, JS_FreeValue(ctx, fun_obj); if (js_create_module_function(ctx, m) < 0) goto fail; - if (js_instantiate_module(ctx, m) < 0) + if (js_link_module(ctx, m) < 0) goto fail; ret_val = js_evaluate_module(ctx, m); if (JS_IsException(ret_val)) { @@ -33308,7 +33554,8 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, 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: @@ -33371,19 +33618,27 @@ static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj, } -JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len, - const char *filename, int eval_flags) +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, ctx->global_obj, input, input_len, filename, + 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) { @@ -33980,7 +34235,6 @@ static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj) 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); @@ -34971,7 +35225,6 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s) 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); @@ -35628,16 +35881,39 @@ static JSAtom find_atom(JSContext *ctx, const char *name) return atom; } -static int JS_InstantiateFunctionListItem(JSContext *ctx, JSObject *p, - JSAtom atom, void *opaque) +static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p, + JSAtom atom, void *opaque) { const JSCFunctionListEntry *e = opaque; - JSValueConst obj = JS_MKPTR(JS_TAG_OBJECT, p); + 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: + 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) { @@ -35654,12 +35930,16 @@ static int JS_InstantiateFunctionListItem(JSContext *ctx, JSObject *p, abort(); } JS_FreeAtom(ctx, atom1); - goto 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_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; @@ -35667,8 +35947,10 @@ static int JS_InstantiateFunctionListItem(JSContext *ctx, JSObject *p, /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */ prop_flags = 0; } - break; - case JS_DEF_CGETSET: + 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; @@ -35692,9 +35974,6 @@ static int JS_InstantiateFunctionListItem(JSContext *ctx, JSObject *p, 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; @@ -35707,10 +35986,11 @@ static int JS_InstantiateFunctionListItem(JSContext *ctx, JSObject *p, case JS_DEF_PROP_UNDEFINED: val = JS_UNDEFINED; break; + case JS_DEF_PROP_STRING: case JS_DEF_OBJECT: - val = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len); - break; + JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP, + (void *)e, prop_flags); + return 0; default: abort(); } @@ -35721,36 +36001,12 @@ static int JS_InstantiateFunctionListItem(JSContext *ctx, JSObject *p, void JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj, const JSCFunctionListEntry *tab, int len) { - int i, prop_flags; + int i; 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_InstantiateFunctionListItem(ctx, obj, atom, e); JS_FreeAtom(ctx, atom); } } @@ -37262,6 +37518,8 @@ static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, 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) { @@ -37273,14 +37531,14 @@ static JSValue js_function_apply(JSContext *ctx, JSValueConst 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) { + 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) { + 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); @@ -37650,7 +37908,7 @@ static JSValue js_array_constructor(JSContext *ctx, JSValueConst new_target, return obj; if (argc == 1 && JS_IsNumber(argv[0])) { uint32_t len; - if (JS_ToArrayLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]))) + 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; @@ -39659,46 +39917,6 @@ static int js_string_get_own_property(JSContext *ctx, 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, @@ -39755,7 +39973,6 @@ static int js_string_delete_property(JSContext *ctx, 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, }; @@ -40359,15 +40576,17 @@ static JSValue js_string___GetSubstitution(JSContext *ctx, JSValueConst this_val 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 (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) { @@ -42805,7 +43024,8 @@ static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val, tab = JS_NewArray(ctx); if (JS_IsException(tab)) goto exception; - if (JS_SetPropertyInt64(ctx, tab, 0, JS_DupValue(ctx, matched)) < 0) + 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; @@ -42817,7 +43037,8 @@ static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val, if (JS_IsException(capN)) goto exception; } - if (JS_SetPropertyInt64(ctx, tab, n, capN) < 0) + if (JS_DefinePropertyValueInt64(ctx, tab, n, capN, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) goto exception; } JS_FreeValue(ctx, namedCaptures); @@ -42825,12 +43046,12 @@ static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val, if (JS_IsException(namedCaptures)) goto exception; if (functionalReplace) { - if (JS_SetPropertyInt64(ctx, tab, n++, JS_NewInt32(ctx, position)) < 0) + if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_NewInt32(ctx, position), JS_PROP_C_W_E | JS_PROP_THROW) < 0) goto exception; - if (JS_SetPropertyInt64(ctx, tab, n++, JS_DupValue(ctx, str)) < 0) + 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_SetPropertyInt64(ctx, tab, n++, JS_DupValue(ctx, namedCaptures)) < 0) + 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; @@ -42917,8 +43138,10 @@ static JSValue js_regexp_Symbol_search(JSContext *ctx, JSValueConst this_val, if (js_same_value(ctx, currentLastIndex, previousLastIndex)) { JS_FreeValue(ctx, previousLastIndex); } else { - if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, previousLastIndex) < 0) + if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, previousLastIndex) < 0) { + previousLastIndex = JS_UNDEFINED; goto exception; + } } JS_FreeValue(ctx, str); JS_FreeValue(ctx, currentLastIndex); @@ -43023,7 +43246,8 @@ static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val, sub = js_sub_string(ctx, strp, p, q); if (JS_IsException(sub)) goto exception; - if (JS_SetPropertyInt64(ctx, A, lengthA++, sub) < 0) + if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) goto exception; if (lengthA == lim) goto done; @@ -43034,7 +43258,7 @@ static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val, sub = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, z, i)); if (JS_IsException(sub)) goto exception; - if (JS_SetPropertyInt64(ctx, A, lengthA++, sub) < 0) + if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0) goto exception; if (lengthA == lim) goto done; @@ -43049,7 +43273,7 @@ add_tail: sub = js_sub_string(ctx, strp, p, size); if (JS_IsException(sub)) goto exception; - if (JS_SetPropertyInt64(ctx, A, lengthA++, sub) < 0) + if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0) goto exception; goto done; exception: @@ -43829,7 +44053,7 @@ void JS_AddIntrinsicJSON(JSContext *ctx) 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); + 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, @@ -46772,26 +46996,15 @@ static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_va { JSValueConst ctor = func_data[0]; JSValueConst onFinally = func_data[1]; - JSValue res, promise, resolving_funcs[2], ret, then_func; + JSValue res, promise, 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); + promise = js_promise_resolve(ctx, ctor, 1, (JSValueConst *)&res, 0); 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 (JS_IsException(promise)) + return promise; if (magic == 0) { then_func = JS_NewCFunctionData(ctx, js_promise_finally_value_thunk, 0, 0, 1, argv); @@ -47503,7 +47716,7 @@ static JSValue JS_SetThisTimeValue(JSContext *ctx, JSValueConst this_val, double 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); + p->u.object_data = JS_NewFloat64(ctx, v); return JS_DupValue(ctx, p->u.object_data); } } @@ -47547,7 +47760,7 @@ 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 fields[9], int is_local, int force) { double dval; int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0; @@ -47606,12 +47819,18 @@ static double time_clip(double t) { 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; +/* 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++) { @@ -47621,7 +47840,8 @@ static double set_date_fields(int64_t fields[], int is_local) { days += md; } days += fields[2] - 1; - h = ((fields[3] * 60 + fields[4]) * 60 + fields[5]) * 1000 + fields[6]; + h = fields[3] * 3600000 + fields[4] * 60000 + + fields[5] * 1000 + fields[6]; d = days * 86400000 + h; if (is_local) d += getTimezoneOffset(d) * 60000; @@ -47632,7 +47852,7 @@ 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]; + double fields[9]; int res, n, is_local; is_local = magic & 0x0F; @@ -47646,14 +47866,14 @@ static JSValue get_date_field(JSContext *ctx, JSValueConst this_val, if (magic & 0x100) { // getYear fields[0] -= 1900; } - return JS_NewInt64(ctx, fields[n]); + 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) - int64_t fields[9]; + double fields[9]; int res, first_field, end_field, is_local, i, n; double d, a; @@ -47695,7 +47915,7 @@ static JSValue get_date_string(JSContext *ctx, JSValueConst this_val, { // _string(obj, fmt, part) char buf[64]; - int64_t fields[9]; + double fields[9]; int res, fmt, part, pos; int y, mon, d, h, m, s, ms, wd, tz; @@ -47843,7 +48063,7 @@ static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target, } val = time_clip(val); } else { - int64_t fields[] = { 0, 0, 1, 0, 0, 0, 0 }; + double fields[] = { 0, 0, 1, 0, 0, 0, 0 }; if (n > 7) n = 7; for(i = 0; i < n; i++) { @@ -47862,12 +48082,12 @@ has_val: JSValueConst args[3]; args[0] = new_target; args[1] = ctx->class_proto[JS_CLASS_DATE]; - args[2] = __JS_NewFloat64(ctx, val); + 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)); + 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(); */ @@ -47883,7 +48103,7 @@ 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 }; + double fields[] = { 0, 0, 1, 0, 0, 0, 0 }; int i, n; double a; @@ -47901,7 +48121,7 @@ static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val, if (i == 0 && fields[0] >= 0 && fields[0] < 100) fields[0] += 1900; } - return __JS_NewFloat64(ctx, set_date_fields(fields, 0)); + return JS_NewFloat64(ctx, set_date_fields(fields, 0)); } static void string_skip_spaces(JSString *sp, int *pp) { @@ -48011,6 +48231,7 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, // 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; @@ -48123,8 +48344,10 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, } } } - d = set_date_fields(fields, is_local) - tz * 60000; - rv = __JS_NewFloat64(ctx, d); + 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); @@ -48194,7 +48417,7 @@ static JSValue js_date_getTime(JSContext *ctx, JSValueConst this_val, if (JS_ThisTimeValue(ctx, &v, this_val)) return JS_EXCEPTION; - return __JS_NewFloat64(ctx, v); + return JS_NewFloat64(ctx, v); } static JSValue js_date_setTime(JSContext *ctx, JSValueConst this_val, @@ -48223,7 +48446,7 @@ static JSValue js_date_setYear(JSContext *ctx, JSValueConst this_val, if (y >= 0 && y < 100) y += 1900; } - args[0] = __JS_NewFloat64(ctx, y); + args[0] = JS_NewFloat64(ctx, y); return set_date_field(ctx, this_val, 1, args, 0x011); } @@ -48714,9 +48937,11 @@ static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val) } static JSValue js_bigint_constructor(JSContext *ctx, - JSValueConst this_val, + 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])); } @@ -48962,7 +49187,7 @@ static const JSCFunctionListEntry js_bigint_proto_funcs[] = { void JS_AddIntrinsicBigInt(JSContext *ctx) { JSRuntime *rt = ctx->rt; - JSValue obj1; + JSValueConst obj1; rt->bigint_ops.to_string = js_bigint_to_string; rt->bigint_ops.from_string = js_string_to_bigint; @@ -48974,9 +49199,8 @@ void JS_AddIntrinsicBigInt(JSContext *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]); + 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)); } @@ -49194,10 +49418,12 @@ static const JSCFunctionListEntry js_bigfloat_proto_funcs[] = { }; static JSValue js_bigfloat_constructor(JSContext *ctx, - JSValueConst this_val, + 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); @@ -49858,8 +50084,7 @@ static const JSCFunctionListEntry js_float_env_proto_funcs[] = { void JS_AddIntrinsicBigFloat(JSContext *ctx) { JSRuntime *rt = ctx->rt; - JSValue obj1; - JSValueConst obj2; + JSValueConst obj1; rt->bigfloat_ops.to_string = js_bigfloat_to_string; rt->bigfloat_ops.from_string = js_string_to_bigfloat; @@ -49873,9 +50098,8 @@ void JS_AddIntrinsicBigFloat(JSContext *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]); + 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)); @@ -49883,10 +50107,10 @@ void JS_AddIntrinsicBigFloat(JSContext *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", + obj1 = JS_NewGlobalCConstructorOnly(ctx, "BigFloatEnv", js_float_env_constructor, 1, ctx->class_proto[JS_CLASS_FLOAT_ENV]); - JS_SetPropertyFunctionList(ctx, obj2, js_float_env_funcs, + JS_SetPropertyFunctionList(ctx, obj1, js_float_env_funcs, countof(js_float_env_funcs)); } @@ -49989,10 +50213,12 @@ static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val, } static JSValue js_bigdecimal_constructor(JSContext *ctx, - JSValueConst this_val, + 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); @@ -50348,7 +50574,7 @@ static const JSCFunctionListEntry js_bigdecimal_funcs[] = { void JS_AddIntrinsicBigDecimal(JSContext *ctx) { JSRuntime *rt = ctx->rt; - JSValue obj1; + JSValueConst obj1; rt->bigdecimal_ops.to_string = js_bigdecimal_to_string; rt->bigdecimal_ops.from_string = js_string_to_bigdecimal; @@ -50360,9 +50586,9 @@ void JS_AddIntrinsicBigDecimal(JSContext *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]); + 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)); } @@ -50818,8 +51044,7 @@ static JSValue js_array_buffer_get_byteLength(JSContext *ctx, JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id); if (!abuf) return JS_EXCEPTION; - if (abuf->detached) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + /* return 0 if detached */ return JS_NewUint32(ctx, abuf->byte_length); } |