diff options
author | bellard <6490144+bellard@users.noreply.github.com> | 2020-09-06 19:10:15 +0200 |
---|---|---|
committer | bellard <6490144+bellard@users.noreply.github.com> | 2020-09-06 19:10:15 +0200 |
commit | 7c312df422572cf867f29a1d80693e8a77f7fb2a (patch) | |
tree | e6b8443835b36b6cea904e4330ef93e4ab5ed040 /quickjs.c | |
parent | 89007660998db0ee55f0ab3b34bb1a84f86fd3c4 (diff) | |
download | quickjs-7c312df422572cf867f29a1d80693e8a77f7fb2a.tar.gz quickjs-7c312df422572cf867f29a1d80693e8a77f7fb2a.zip |
2020-09-06 release
Diffstat (limited to 'quickjs.c')
-rw-r--r-- | quickjs.c | 884 |
1 files changed, 573 insertions, 311 deletions
@@ -825,8 +825,8 @@ typedef struct JSShapeProperty { } JSShapeProperty; struct JSShape { - uint32_t prop_hash_end[0]; /* hash table of size hash_mask + 1 - before the start of the structure. */ + /* hash table of size hash_mask + 1 before the start of the + structure (see prop_hash_end()). */ JSGCObjectHeader header; /* true if the shape is inserted in the shape hash table. If not, JSShape.hash is not valid */ @@ -859,6 +859,7 @@ struct JSObject { uint8_t is_constructor : 1; /* TRUE if object is a constructor function */ uint8_t is_uncatchable_error : 1; /* if TRUE, error is not catchable */ uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */ + uint8_t is_HTMLDDA : 1; /* specific annex B IsHtmlDDA behavior */ uint16_t class_id; /* see JS_CLASS_x */ }; }; @@ -931,7 +932,7 @@ struct JSObject { /* byte sizes: 40/48/72 */ }; enum { - JS_ATOM_NULL, + __JS_ATOM_NULL = JS_ATOM_NULL, #define DEF(name, str) JS_ATOM_ ## name, #include "quickjs-atom.h" #undef DEF @@ -4198,9 +4199,14 @@ static inline JSShape *get_shape_from_alloc(void *sh_alloc, size_t hash_size) return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size); } +static inline uint32_t *prop_hash_end(JSShape *sh) +{ + return (uint32_t *)sh; +} + static inline void *get_alloc_from_shape(JSShape *sh) { - return sh->prop_hash_end - ((intptr_t)sh->prop_hash_mask + 1); + return prop_hash_end(sh) - ((intptr_t)sh->prop_hash_mask + 1); } static inline JSShapeProperty *get_shape_prop(JSShape *sh) @@ -4311,7 +4317,7 @@ static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto, if (proto) JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, proto)); sh->proto = proto; - memset(sh->prop_hash_end - hash_size, 0, sizeof(sh->prop_hash_end[0]) * + memset(prop_hash_end(sh) - hash_size, 0, sizeof(prop_hash_end(sh)[0]) * hash_size); sh->prop_hash_mask = hash_size - 1; sh->prop_size = prop_size; @@ -4440,13 +4446,13 @@ static no_inline int resize_properties(JSContext *ctx, JSShape **psh, list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); new_hash_mask = new_hash_size - 1; sh->prop_hash_mask = new_hash_mask; - memset(sh->prop_hash_end - new_hash_size, 0, - sizeof(sh->prop_hash_end[0]) * new_hash_size); + memset(prop_hash_end(sh) - new_hash_size, 0, + sizeof(prop_hash_end(sh)[0]) * new_hash_size); for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) { if (pr->atom != JS_ATOM_NULL) { h = ((uintptr_t)pr->atom & new_hash_mask); - pr->hash_next = sh->prop_hash_end[-h - 1]; - sh->prop_hash_end[-h - 1] = i + 1; + pr->hash_next = prop_hash_end(sh)[-h - 1]; + prop_hash_end(sh)[-h - 1] = i + 1; } } js_free(ctx, get_alloc_from_shape(old_sh)); @@ -4500,8 +4506,8 @@ static int compact_properties(JSContext *ctx, JSObject *p) memcpy(sh, old_sh, sizeof(JSShape)); list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); - memset(sh->prop_hash_end - new_hash_size, 0, - sizeof(sh->prop_hash_end[0]) * new_hash_size); + memset(prop_hash_end(sh) - new_hash_size, 0, + sizeof(prop_hash_end(sh)[0]) * new_hash_size); j = 0; old_pr = old_sh->prop; @@ -4512,8 +4518,8 @@ static int compact_properties(JSContext *ctx, JSObject *p) pr->atom = old_pr->atom; pr->flags = old_pr->flags; h = ((uintptr_t)old_pr->atom & new_hash_mask); - pr->hash_next = sh->prop_hash_end[-h - 1]; - sh->prop_hash_end[-h - 1] = j + 1; + pr->hash_next = prop_hash_end(sh)[-h - 1]; + prop_hash_end(sh)[-h - 1] = j + 1; prop[j] = prop[i]; j++; pr++; @@ -4575,8 +4581,8 @@ static int add_shape_property(JSContext *ctx, JSShape **psh, /* add in hash table */ hash_mask = sh->prop_hash_mask; h = atom & hash_mask; - pr->hash_next = sh->prop_hash_end[-h - 1]; - sh->prop_hash_end[-h - 1] = sh->prop_count; + pr->hash_next = prop_hash_end(sh)[-h - 1]; + prop_hash_end(sh)[-h - 1] = sh->prop_count; return 0; } @@ -4693,6 +4699,7 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas p->is_constructor = 0; p->is_uncatchable_error = 0; p->tmp_mark = 0; + p->is_HTMLDDA = 0; p->first_weak_ref = NULL; p->u.opaque = NULL; p->shape = sh; @@ -4731,7 +4738,19 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas p->prop[0].u.value = JS_UNDEFINED; break; case JS_CLASS_ARGUMENTS: - case JS_CLASS_UINT8C_ARRAY ... JS_CLASS_FLOAT64_ARRAY: + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_INT8_ARRAY: + case JS_CLASS_UINT8_ARRAY: + case JS_CLASS_INT16_ARRAY: + case JS_CLASS_UINT16_ARRAY: + case JS_CLASS_INT32_ARRAY: + case JS_CLASS_UINT32_ARRAY: +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT64_ARRAY: + case JS_CLASS_BIG_UINT64_ARRAY: +#endif + case JS_CLASS_FLOAT32_ARRAY: + case JS_CLASS_FLOAT64_ARRAY: p->is_exotic = 1; p->fast_array = 1; p->u.array.u.ptr = NULL; @@ -5133,7 +5152,7 @@ static force_inline JSShapeProperty *find_own_property1(JSObject *p, intptr_t h; sh = p->shape; h = (uintptr_t)atom & sh->prop_hash_mask; - h = sh->prop_hash_end[-h - 1]; + h = prop_hash_end(sh)[-h - 1]; prop = get_shape_prop(sh); while (h) { pr = &prop[h - 1]; @@ -5154,7 +5173,7 @@ static force_inline JSShapeProperty *find_own_property(JSProperty **ppr, intptr_t h; sh = p->shape; h = (uintptr_t)atom & sh->prop_hash_mask; - h = sh->prop_hash_end[-h - 1]; + h = prop_hash_end(sh)[-h - 1]; prop = get_shape_prop(sh); while (h) { pr = &prop[h - 1]; @@ -7994,7 +8013,7 @@ static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) redo: sh = p->shape; h1 = atom & sh->prop_hash_mask; - h = sh->prop_hash_end[-h1 - 1]; + h = prop_hash_end(sh)[-h1 - 1]; prop = get_shape_prop(sh); lpr = NULL; lpr_idx = 0; /* prevent warning */ @@ -8015,7 +8034,7 @@ static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) lpr = get_shape_prop(sh) + lpr_idx; lpr->hash_next = pr->hash_next; } else { - sh->prop_hash_end[-h1 - 1] = pr->hash_next; + prop_hash_end(sh)[-h1 - 1] = pr->hash_next; } sh->deleted_prop_count++; /* free the entry */ @@ -8095,15 +8114,16 @@ 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, JSProperty *prop, - 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); if (ret) return -1; + if (likely(p->fast_array)) { uint32_t old_len = p->u.array.count; if (len < old_len) { @@ -8112,11 +8132,11 @@ static int set_array_length(JSContext *ctx, JSObject *p, JSProperty *prop, } p->u.array.count = len; } - prop->u.value = JS_NewUint32(ctx, len); + p->prop[0].u.value = JS_NewUint32(ctx, len); } else { /* Note: length is always a uint32 because the object is an array */ - JS_ToUint32(ctx, &cur_len, prop->u.value); + JS_ToUint32(ctx, &cur_len, p->prop[0].u.value); if (len < cur_len) { uint32_t d; JSShape *sh; @@ -8366,7 +8386,7 @@ retry: (JS_PROP_LENGTH | JS_PROP_WRITABLE)) { assert(p->class_id == JS_CLASS_ARRAY); assert(prop == JS_ATOM_length); - return set_array_length(ctx, p, pr, val, flags); + return set_array_length(ctx, p, val, flags); } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags); } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { @@ -9047,7 +9067,7 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, } if (prs->flags & JS_PROP_LENGTH) { if (flags & JS_PROP_HAS_VALUE) { - res = set_array_length(ctx, p, pr, JS_DupValue(ctx, val), + res = set_array_length(ctx, p, JS_DupValue(ctx, val), flags); } else { res = TRUE; @@ -9806,6 +9826,24 @@ static JSValue JS_ToPrimitive(JSContext *ctx, JSValueConst val, int hint) return JS_ToPrimitiveFree(ctx, JS_DupValue(ctx, val), hint); } +void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return; + p = JS_VALUE_GET_OBJ(obj); + p->is_HTMLDDA = TRUE; +} + +static inline BOOL JS_IsHTMLDDA(JSContext *ctx, JSValueConst obj) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(obj); + return p->is_HTMLDDA; +} + static int JS_ToBoolFree(JSContext *ctx, JSValue val) { uint32_t tag = JS_VALUE_GET_TAG(val); @@ -9843,6 +9881,15 @@ static int JS_ToBoolFree(JSContext *ctx, JSValue val) return ret; } #endif + case JS_TAG_OBJECT: + { + JSObject *p = JS_VALUE_GET_OBJ(val); + BOOL ret; + ret = !p->is_HTMLDDA; + JS_FreeValue(ctx, val); + return ret; + } + break; default: if (JS_TAG_IS_FLOAT64(tag)) { double d = JS_VALUE_GET_FLOAT64(val); @@ -11602,7 +11649,19 @@ static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) case JS_CLASS_ARGUMENTS: JS_DumpValueShort(rt, p->u.array.u.values[i]); break; - case JS_CLASS_UINT8C_ARRAY ... JS_CLASS_FLOAT64_ARRAY: + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_INT8_ARRAY: + case JS_CLASS_UINT8_ARRAY: + case JS_CLASS_INT16_ARRAY: + case JS_CLASS_UINT16_ARRAY: + case JS_CLASS_INT32_ARRAY: + case JS_CLASS_UINT32_ARRAY: +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT64_ARRAY: + case JS_CLASS_BIG_UINT64_ARRAY: +#endif + case JS_CLASS_FLOAT32_ARRAY: + case JS_CLASS_FLOAT64_ARRAY: { int size = 1 << typed_array_size_log2(p->class_id); const uint8_t *b = p->u.array.u.uint8_ptr + i * size; @@ -13668,33 +13727,8 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, JS_FreeValue(ctx, op2); } else if ((tag1 <= JS_TAG_NULL || tag1 == JS_TAG_FLOAT64) && (tag2 <= JS_TAG_NULL || tag2 == JS_TAG_FLOAT64)) { - /* can use floating point comparison */ - double d1, d2; - if (tag1 == JS_TAG_FLOAT64) { - d1 = JS_VALUE_GET_FLOAT64(op1); - } else { - d1 = JS_VALUE_GET_INT(op1); - } - if (tag2 == JS_TAG_FLOAT64) { - d2 = JS_VALUE_GET_FLOAT64(op2); - } else { - d2 = JS_VALUE_GET_INT(op2); - } - switch(op) { - case OP_lt: - res = (d1 < d2); /* if NaN return false */ - break; - case OP_lte: - res = (d1 <= d2); /* if NaN return false */ - break; - case OP_gt: - res = (d1 > d2); /* if NaN return false */ - break; - default: - case OP_gte: - res = (d1 >= d2); /* if NaN return false */ - break; - } + /* fast path for float64/int */ + goto float64_compare; } else { if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) || (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING)) && @@ -13727,20 +13761,51 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, } } - if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_DECIMAL || - JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_DECIMAL) { + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) { res = ctx->rt->bigdecimal_ops.compare(ctx, op, op1, op2); if (res < 0) goto exception; - } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_FLOAT || - JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_FLOAT) { + } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) { res = ctx->rt->bigfloat_ops.compare(ctx, op, op1, op2); if (res < 0) goto exception; - } else { + } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { res = ctx->rt->bigint_ops.compare(ctx, op, op1, op2); if (res < 0) goto exception; + } else { + double d1, d2; + + float64_compare: + /* can use floating point comparison */ + if (tag1 == JS_TAG_FLOAT64) { + d1 = JS_VALUE_GET_FLOAT64(op1); + } else { + d1 = JS_VALUE_GET_INT(op1); + } + if (tag2 == JS_TAG_FLOAT64) { + d2 = JS_VALUE_GET_FLOAT64(op2); + } else { + d2 = JS_VALUE_GET_INT(op2); + } + switch(op) { + case OP_lt: + res = (d1 < d2); /* if NaN return false */ + break; + case OP_lte: + res = (d1 <= d2); /* if NaN return false */ + break; + case OP_gt: + res = (d1 > d2); /* if NaN return false */ + break; + default: + case OP_gte: + res = (d1 >= d2); /* if NaN return false */ + break; + } } } done: @@ -13895,9 +13960,17 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, } goto redo; } else { + /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */ + if ((JS_IsHTMLDDA(ctx, op1) && + (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) || + (JS_IsHTMLDDA(ctx, op2) && + (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) { + res = TRUE; + } else { + res = FALSE; + } JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); - res = FALSE; } done: sp[-2] = JS_NewBool(ctx, res ^ is_neq); @@ -14348,9 +14421,17 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, } goto redo; } else { + /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */ + if ((JS_IsHTMLDDA(ctx, op1) && + (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) || + (JS_IsHTMLDDA(ctx, op2) && + (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) { + res = TRUE; + } else { + res = FALSE; + } JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); - res = FALSE; } sp[-2] = JS_NewBool(ctx, res ^ is_neq); return 0; @@ -14631,7 +14712,7 @@ static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp) return 0; } -static __exception int js_operator_typeof(JSContext *ctx, JSValue op1) +static __exception int js_operator_typeof(JSContext *ctx, JSValueConst op1) { JSAtom atom; uint32_t tag; @@ -14663,10 +14744,16 @@ static __exception int js_operator_typeof(JSContext *ctx, JSValue op1) atom = JS_ATOM_string; break; case JS_TAG_OBJECT: - if (JS_IsFunction(ctx, op1)) - atom = JS_ATOM_function; - else - goto obj_type; + { + JSObject *p; + p = JS_VALUE_GET_OBJ(op1); + if (unlikely(p->is_HTMLDDA)) + atom = JS_ATOM_undefined; + else if (JS_IsFunction(ctx, op1)) + atom = JS_ATOM_function; + else + goto obj_type; + } break; case JS_TAG_NULL: obj_type: @@ -15231,7 +15318,10 @@ static __exception int js_for_of_start(JSContext *ctx, JSValue *sp, return 0; } -/* enum_rec -> enum_rec value done */ +/* enum_rec [objs] -> enum_rec [objs] value done. There are 'offset' + objs. If 'done' is true or in case of exception, 'enum_rec' is set + to undefined. If 'done' is true, 'value' is always set to + undefined. */ static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset) { JSValue value = JS_UNDEFINED; @@ -15246,8 +15336,12 @@ static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset) /* replace the iteration object with undefined */ JS_FreeValue(ctx, sp[offset]); sp[offset] = JS_UNDEFINED; - if (done < 0) + if (done < 0) { return -1; + } else { + JS_FreeValue(ctx, value); + value = JS_UNDEFINED; + } } } sp[0] = value; @@ -15363,10 +15457,10 @@ static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj, static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp) { JSValue iterator, enumobj, method, value; - int pos, is_array_iterator; + int is_array_iterator; JSValue *arrp; - uint32_t i, count32; - + uint32_t i, count32, pos; + if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) { JS_ThrowInternalError(ctx, "invalid index for append"); return -1; @@ -15399,24 +15493,21 @@ static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp) if (is_array_iterator && JS_IsCFunction(ctx, method, (JSCFunction *)js_array_iterator_next, 0) && js_get_fast_array(ctx, sp[-1], &arrp, &count32)) { - int64_t len; - /* Handle fast arrays explicitly */ - if (js_get_length64(ctx, &len, sp[-1])) + uint32_t len; + if (js_get_length32(ctx, &len, sp[-1])) goto exception; + /* if len > count32, the elements >= count32 might be read in + the prototypes and might have side effects */ + if (len != count32) + goto general_case; + /* Handle fast arrays explicitly */ for (i = 0; i < count32; i++) { if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, JS_DupValue(ctx, arrp[i]), JS_PROP_C_W_E) < 0) goto exception; } - if (len > count32) { - /* This is not strictly correct because the trailing elements are - empty instead of undefined. Append undefined entries instead. - */ - pos += len - count32; - if (JS_SetProperty(ctx, sp[-3], JS_ATOM_length, JS_NewUint32(ctx, pos)) < 0) - goto exception; - } } else { + general_case: for (;;) { BOOL done; value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done); @@ -15430,6 +15521,7 @@ static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp) goto exception; } } + /* Note: could raise an error if too many elements */ sp[-2] = JS_NewInt32(ctx, pos); JS_FreeValue(ctx, enumobj); JS_FreeValue(ctx, method); @@ -17742,47 +17834,42 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, BREAK; CASE(OP_add_loc): { - JSValue ops[2]; + JSValue *pv; int idx; idx = *pc; pc += 1; - ops[0] = var_buf[idx]; - ops[1] = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(ops[0], ops[1]))) { + pv = &var_buf[idx]; + if (likely(JS_VALUE_IS_BOTH_INT(*pv, sp[-1]))) { int64_t r; - r = (int64_t)JS_VALUE_GET_INT(ops[0]) + JS_VALUE_GET_INT(ops[1]); + r = (int64_t)JS_VALUE_GET_INT(*pv) + + JS_VALUE_GET_INT(sp[-1]); if (unlikely((int)r != r)) goto add_loc_slow; - var_buf[idx] = JS_NewInt32(ctx, r); + *pv = JS_NewInt32(ctx, r); sp--; - } else if (JS_VALUE_GET_TAG(ops[0]) == JS_TAG_STRING) { + } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING) { + JSValue op1; + op1 = sp[-1]; sp--; - ops[1] = JS_ToPrimitiveFree(ctx, ops[1], HINT_NONE); - if (JS_IsException(ops[1])) { + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); + if (JS_IsException(op1)) goto exception; - } - /* XXX: should not modify the variable in case of - exception */ - ops[0] = JS_ConcatString(ctx, ops[0], ops[1]); - if (JS_IsException(ops[0])) { - var_buf[idx] = JS_UNDEFINED; + op1 = JS_ConcatString(ctx, JS_DupValue(ctx, *pv), op1); + if (JS_IsException(op1)) goto exception; - } - var_buf[idx] = ops[0]; + set_value(ctx, pv, op1); } else { + JSValue ops[2]; add_loc_slow: - /* XXX: should not modify the variable in case of - exception */ - sp--; /* In case of exception, js_add_slow frees ops[0] - and ops[1]. */ - /* XXX: change API */ - if (js_add_slow(ctx, ops + 2)) { - var_buf[idx] = JS_UNDEFINED; + and ops[1], so we must duplicate *pv */ + ops[0] = JS_DupValue(ctx, *pv); + ops[1] = sp[-1]; + sp--; + if (js_add_slow(ctx, ops + 2)) goto exception; - } - var_buf[idx] = ops[0]; + set_value(ctx, pv, ops[0]); } } BREAK; @@ -18427,7 +18514,15 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, } else { goto free_and_set_false; } - CASE(OP_is_function): + /* XXX: could merge to a single opcode */ + CASE(OP_typeof_is_undefined): + /* different from OP_is_undefined because of isHTMLDDA */ + if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_undefined) { + goto free_and_set_true; + } else { + goto free_and_set_false; + } + CASE(OP_typeof_is_function): if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) { goto free_and_set_true; } else { @@ -19656,6 +19751,9 @@ enum { TOK_MATH_POW_ASSIGN, #endif TOK_POW_ASSIGN, + TOK_LAND_ASSIGN, + TOK_LOR_ASSIGN, + TOK_DOUBLE_QUESTION_MARK_ASSIGN, TOK_DEC, TOK_INC, TOK_SHL, @@ -20015,11 +20113,14 @@ static void free_token(JSParseState *s, JSToken *token) JS_FreeValue(s->ctx, token->u.regexp.flags); break; case TOK_IDENT: - case TOK_FIRST_KEYWORD ... TOK_LAST_KEYWORD: case TOK_PRIVATE_NAME: JS_FreeAtom(s->ctx, token->u.ident.atom); break; default: + if (token->val >= TOK_FIRST_KEYWORD && + token->val <= TOK_LAST_KEYWORD) { + JS_FreeAtom(s->ctx, token->u.ident.atom); + } break; } } @@ -20233,6 +20334,8 @@ static __exception int js_parse_string(JSParseState *s, int sep, } if (c == '\\') { c = *p; + /* XXX: need a specific JSON case to avoid + accepting invalid escapes */ switch(c) { case '\0': if (p >= s->buf_end) @@ -20256,18 +20359,23 @@ static __exception int js_parse_string(JSParseState *s, int sep, s->line_num++; continue; default: - if (c >= '0' && c <= '7') { + if (c >= '0' && c <= '9') { if (!s->cur_func) - goto invalid_octal; /* JSON case */ + goto invalid_escape; /* JSON case */ if (!(s->cur_func->js_mode & JS_MODE_STRICT) && sep != '`') goto parse_escape; if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) { p++; c = '\0'; } else { - invalid_octal: - if (do_throw) - js_parse_error(s, "invalid octal syntax in strict mode"); + if (c >= '8' || sep == '`') { + /* Note: according to ES2021, \8 and \9 are not + accepted in strict mode or in templates. */ + goto invalid_escape; + } else { + if (do_throw) + js_parse_error(s, "octal escape sequences are not allowed in strict mode"); + } goto fail; } } else if (c >= 0x80) { @@ -20284,6 +20392,7 @@ static __exception int js_parse_string(JSParseState *s, int sep, parse_escape: ret = lre_parse_escape(&p, TRUE); if (ret == -1) { + invalid_escape: if (do_throw) js_parse_error(s, "malformed escape sequence in string literal"); goto fail; @@ -20634,8 +20743,20 @@ static __exception int next_token(JSParseState *s) } } goto def_token; - case 'a' ... 'z': - case 'A' ... 'Z': + case 'a': case 'b': case 'c': case 'd': + case 'e': case 'f': case 'g': case 'h': + case 'i': case 'j': case 'k': case 'l': + case 'm': case 'n': case 'o': case 'p': + case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': + case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': + case 'E': case 'F': case 'G': case 'H': + case 'I': case 'J': case 'K': case 'L': + case 'M': case 'N': case 'O': case 'P': + case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': case '_': case '$': /* identifier */ @@ -20717,7 +20838,9 @@ static __exception int next_token(JSParseState *s) goto fail; } goto parse_number; - case '1' ... '9': + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': + case '9': /* number */ parse_number: { @@ -20887,8 +21010,13 @@ static __exception int next_token(JSParseState *s) p += 2; s->token.val = TOK_AND_ASSIGN; } else if (p[1] == '&') { - p += 2; - s->token.val = TOK_LAND; + if (p[2] == '=') { + p += 3; + s->token.val = TOK_LAND_ASSIGN; + } else { + p += 2; + s->token.val = TOK_LAND; + } } else { goto def_token; } @@ -20934,16 +21062,26 @@ static __exception int next_token(JSParseState *s) p += 2; s->token.val = TOK_OR_ASSIGN; } else if (p[1] == '|') { - p += 2; - s->token.val = TOK_LOR; + if (p[2] == '=') { + p += 3; + s->token.val = TOK_LOR_ASSIGN; + } else { + p += 2; + s->token.val = TOK_LOR; + } } else { goto def_token; } break; case '?': if (p[1] == '?') { - p += 2; - s->token.val = TOK_DOUBLE_QUESTION_MARK; + if (p[2] == '=') { + p += 3; + s->token.val = TOK_DOUBLE_QUESTION_MARK_ASSIGN; + } else { + p += 2; + s->token.val = TOK_DOUBLE_QUESTION_MARK; + } } else if (p[1] == '.' && !(p[2] >= '0' && p[2] <= '9')) { p += 2; s->token.val = TOK_QUESTION_MARK_DOT; @@ -21135,8 +21273,20 @@ static __exception int json_next_token(JSParseState *s) goto def_token; } break; - case 'a' ... 'z': - case 'A' ... 'Z': + case 'a': case 'b': case 'c': case 'd': + case 'e': case 'f': case 'g': case 'h': + case 'i': case 'j': case 'k': case 'l': + case 'm': case 'n': case 'o': case 'p': + case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': + case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': + case 'E': case 'F': case 'G': case 'H': + case 'I': case 'J': case 'K': case 'L': + case 'M': case 'N': case 'O': case 'P': + case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': case '_': case '$': /* identifier : only pure ascii characters are accepted */ @@ -21161,7 +21311,9 @@ static __exception int json_next_token(JSParseState *s) if (!is_digit(p[1])) goto def_token; goto parse_number; - case '1' ... '9': + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': + case '9': /* number */ parse_number: { @@ -23267,15 +23419,6 @@ static BOOL has_with_scope(JSFunctionDef *s, int scope_level) return FALSE; } -typedef struct JSLValue { - int opcode; - int scope; - int label; - int depth; - int tok; - JSAtom name; -} JSLValue; - static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope, JSAtom *pname, int *plabel, int *pdepth, BOOL keep, int tok) @@ -23400,84 +23543,112 @@ static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope, return 0; } -/* if special = TRUE: specific post inc/dec case */ -/* name has a live reference */ +typedef enum { + PUT_LVALUE_NOKEEP, /* [depth] v -> */ + PUT_LVALUE_NOKEEP_DEPTH, /* [depth] v -> , keep depth (currently + just disable optimizations) */ + PUT_LVALUE_KEEP_TOP, /* [depth] v -> v */ + PUT_LVALUE_KEEP_SECOND, /* [depth] v0 v -> v0 */ + PUT_LVALUE_NOKEEP_BOTTOM, /* v [depth] -> */ +} PutLValueEnum; + +/* name has a live reference. 'is_let' is only used with opcode = + OP_scope_get_var which is never generated by get_lvalue(). */ static void put_lvalue(JSParseState *s, int opcode, int scope, - JSAtom name, int label, BOOL special) + JSAtom name, int label, PutLValueEnum special, + BOOL is_let) { switch(opcode) { case OP_get_field: - if (!special) - emit_op(s, OP_insert2); /* obj v -> v obj v */ - else - emit_op(s, OP_perm3); /* obj v0 v -> v0 obj v */ - emit_op(s, OP_put_field); - emit_u32(s, name); /* name has refcount */ - break; case OP_scope_get_private_field: - if (!special) + /* depth = 1 */ + switch(special) { + case PUT_LVALUE_NOKEEP: + case PUT_LVALUE_NOKEEP_DEPTH: + break; + case PUT_LVALUE_KEEP_TOP: emit_op(s, OP_insert2); /* obj v -> v obj v */ - else + break; + case PUT_LVALUE_KEEP_SECOND: emit_op(s, OP_perm3); /* obj v0 v -> v0 obj v */ - emit_op(s, OP_scope_put_private_field); - emit_u32(s, name); /* name has refcount */ - emit_u16(s, scope); + break; + case PUT_LVALUE_NOKEEP_BOTTOM: + emit_op(s, OP_swap); + break; + default: + abort(); + } break; case OP_get_array_el: - if (!special) - emit_op(s, OP_insert3); /* obj prop v -> v obj prop v */ - else - emit_op(s, OP_perm4); /* obj prop v0 v -> v0 obj prop v */ - emit_op(s, OP_put_array_el); - break; case OP_get_ref_value: - JS_FreeAtom(s->ctx, name); - emit_label(s, label); - if (!special) + /* depth = 2 */ + if (opcode == OP_get_ref_value) { + JS_FreeAtom(s->ctx, name); + emit_label(s, label); + } + switch(special) { + case PUT_LVALUE_NOKEEP: + emit_op(s, OP_nop); /* will trigger optimization */ + break; + case PUT_LVALUE_NOKEEP_DEPTH: + break; + case PUT_LVALUE_KEEP_TOP: emit_op(s, OP_insert3); /* obj prop v -> v obj prop v */ - else + break; + case PUT_LVALUE_KEEP_SECOND: emit_op(s, OP_perm4); /* obj prop v0 v -> v0 obj prop v */ - emit_op(s, OP_put_ref_value); + break; + case PUT_LVALUE_NOKEEP_BOTTOM: + emit_op(s, OP_rot3l); + break; + default: + abort(); + } break; case OP_get_super_value: - if (!special) + /* depth = 3 */ + switch(special) { + case PUT_LVALUE_NOKEEP: + case PUT_LVALUE_NOKEEP_DEPTH: + break; + case PUT_LVALUE_KEEP_TOP: emit_op(s, OP_insert4); /* this obj prop v -> v this obj prop v */ - else + break; + case PUT_LVALUE_KEEP_SECOND: emit_op(s, OP_perm5); /* this obj prop v0 v -> v0 this obj prop v */ - emit_op(s, OP_put_super_value); + break; + case PUT_LVALUE_NOKEEP_BOTTOM: + emit_op(s, OP_rot4l); + break; + default: + abort(); + } break; default: - abort(); + break; } -} - -static void put_lvalue_nokeep(JSParseState *s, int opcode, int scope, - JSAtom name, int label, int var_tok) -{ + switch(opcode) { case OP_scope_get_var: /* val -- */ - emit_op(s, (var_tok == TOK_CONST || var_tok == TOK_LET) ? - OP_scope_put_var_init : OP_scope_put_var); + assert(special == PUT_LVALUE_NOKEEP || + special == PUT_LVALUE_NOKEEP_DEPTH); + emit_op(s, is_let ? OP_scope_put_var_init : OP_scope_put_var); emit_u32(s, name); /* has refcount */ emit_u16(s, scope); break; - case OP_get_field: /* obj val -- */ + case OP_get_field: emit_op(s, OP_put_field); - emit_u32(s, name); /* has refcount */ + emit_u32(s, name); /* name has refcount */ break; case OP_scope_get_private_field: emit_op(s, OP_scope_put_private_field); - emit_u32(s, name); /* has refcount */ + emit_u32(s, name); /* name has refcount */ emit_u16(s, scope); break; - case OP_get_array_el: /* obj prop val -- */ + case OP_get_array_el: emit_op(s, OP_put_array_el); break; - case OP_get_ref_value: /* obj prop val -- */ - /* XXX: currently this reference is never optimized */ - JS_FreeAtom(s->ctx, name); - emit_label(s, label); - //emit_op(s, OP_nop); /* emit 2 bytes for optimizer */ + case OP_get_ref_value: emit_op(s, OP_put_ref_value); break; case OP_get_super_value: @@ -23869,7 +24040,9 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, emit_label(s, label_hasval); } /* store value into lvalue object */ - put_lvalue_nokeep(s, opcode, scope, var_name, label_lvalue, tok); + put_lvalue(s, opcode, scope, var_name, label_lvalue, + PUT_LVALUE_NOKEEP_DEPTH, + (tok == TOK_CONST || tok == TOK_LET)); if (s->token.val == '}') break; /* accept a trailing comma before the '}' */ @@ -23969,8 +24142,9 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, emit_label(s, label_hasval); } /* store value into lvalue object */ - put_lvalue_nokeep(s, opcode, scope, var_name, - label_lvalue, tok); + put_lvalue(s, opcode, scope, var_name, + label_lvalue, PUT_LVALUE_NOKEEP_DEPTH, + (tok == TOK_CONST || tok == TOK_LET)); } if (s->token.val == ']') break; @@ -24786,7 +24960,8 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op)) return -1; emit_op(s, OP_dec + op - TOK_DEC); - put_lvalue(s, opcode, scope, name, label, FALSE); + put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, + FALSE); } break; case TOK_TYPEOF: @@ -24834,7 +25009,8 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op)) return -1; emit_op(s, OP_post_dec + op - TOK_DEC); - put_lvalue(s, opcode, scope, name, label, TRUE); + put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_SECOND, + FALSE); if (next_token(s)) return -1; } @@ -25298,7 +25474,61 @@ static __exception int js_parse_assign_expr(JSParseState *s, BOOL in_accepted) #endif emit_op(s, op); } - put_lvalue(s, opcode, scope, name, label, FALSE); + put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, FALSE); + } else if (op >= TOK_LAND_ASSIGN && op <= TOK_DOUBLE_QUESTION_MARK_ASSIGN) { + int label, label1, depth_lvalue, label2; + + if (next_token(s)) + return -1; + if (get_lvalue(s, &opcode, &scope, &name, &label, + &depth_lvalue, TRUE, op) < 0) + return -1; + + emit_op(s, OP_dup); + if (op == TOK_DOUBLE_QUESTION_MARK_ASSIGN) + emit_op(s, OP_is_undefined_or_null); + label1 = emit_goto(s, op == TOK_LOR_ASSIGN ? OP_if_true : OP_if_false, + -1); + emit_op(s, OP_drop); + + if (js_parse_assign_expr(s, in_accepted)) { + JS_FreeAtom(s->ctx, name); + return -1; + } + + if (opcode == OP_get_ref_value && name == name0) { + set_object_name(s, name); + } + + switch(depth_lvalue) { + case 1: + emit_op(s, OP_insert2); + break; + case 2: + emit_op(s, OP_insert3); + break; + case 3: + emit_op(s, OP_insert4); + break; + default: + abort(); + } + + /* XXX: we disable the OP_put_ref_value optimization by not + using put_lvalue() otherwise depth_lvalue is not correct */ + put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_NOKEEP_DEPTH, + FALSE); + label2 = emit_goto(s, OP_goto, -1); + + emit_label(s, label1); + + /* remove the lvalue stack entries */ + while (depth_lvalue != 0) { + emit_op(s, OP_nip); + depth_lvalue--; + } + + emit_label(s, label2); } return 0; } @@ -25563,8 +25793,8 @@ static __exception int js_parse_var(JSParseState *s, BOOL in_accepted, int tok, goto var_error; } set_object_name(s, name); - put_lvalue(s, opcode, scope, name1, label, FALSE); - emit_op(s, OP_drop); + put_lvalue(s, opcode, scope, name1, label, + PUT_LVALUE_NOKEEP, FALSE); } else { if (js_parse_assign_expr(s, in_accepted)) goto var_error; @@ -25751,29 +25981,14 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name, if (js_parse_destructuring_element(s, 0, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE)) return -1; } else { - int lvalue_label, depth; + int lvalue_label; if (js_parse_postfix_expr(s, TRUE)) return -1; if (get_lvalue(s, &opcode, &scope, &var_name, &lvalue_label, - &depth, FALSE, TOK_FOR)) + NULL, FALSE, TOK_FOR)) return -1; - /* swap value and lvalue object and store it into lvalue object */ - /* enum_rec val [depth] -- enum_rec */ - switch(depth) { - case 1: - emit_op(s, OP_swap); - break; - case 2: - emit_op(s, OP_rot3l); - break; - case 3: - emit_op(s, OP_rot4l); - break; - default: - abort(); - } - put_lvalue_nokeep(s, opcode, scope, var_name, lvalue_label, - TOK_FOR /* not used */); + put_lvalue(s, opcode, scope, var_name, lvalue_label, + PUT_LVALUE_NOKEEP_BOTTOM, FALSE); } var_name = JS_ATOM_NULL; } @@ -26933,32 +27148,20 @@ static JSModuleDef *js_find_loaded_module(JSContext *ctx, JSAtom name) /* return NULL in case of exception (e.g. module could not be loaded) */ static JSModuleDef *js_host_resolve_imported_module(JSContext *ctx, - JSAtom base_module_name, - JSAtom module_name1) + const char *base_cname, + const char *cname1) { JSRuntime *rt = ctx->rt; JSModuleDef *m; char *cname; - const char *base_cname, *cname1; JSAtom module_name; - base_cname = JS_AtomToCString(ctx, base_module_name); - if (!base_cname) - return NULL; - cname1 = JS_AtomToCString(ctx, module_name1); - if (!cname1) { - JS_FreeCString(ctx, base_cname); - return NULL; - } - if (!rt->module_normalize_func) { cname = js_default_module_normalize_name(ctx, base_cname, cname1); } else { cname = rt->module_normalize_func(ctx, base_cname, cname1, rt->module_loader_opaque); } - JS_FreeCString(ctx, base_cname); - JS_FreeCString(ctx, cname1); if (!cname) return NULL; @@ -26992,6 +27195,27 @@ static JSModuleDef *js_host_resolve_imported_module(JSContext *ctx, return m; } +static JSModuleDef *js_host_resolve_imported_module_atom(JSContext *ctx, + JSAtom base_module_name, + JSAtom module_name1) +{ + const char *base_cname, *cname; + JSModuleDef *m; + + base_cname = JS_AtomToCString(ctx, base_module_name); + if (!base_cname) + return NULL; + cname = JS_AtomToCString(ctx, module_name1); + if (!cname) { + JS_FreeCString(ctx, base_cname); + return NULL; + } + m = js_host_resolve_imported_module(ctx, base_cname, cname); + JS_FreeCString(ctx, base_cname); + JS_FreeCString(ctx, cname); + return m; +} + typedef struct JSResolveEntry { JSModuleDef *module; JSAtom name; @@ -27435,8 +27659,8 @@ static int js_resolve_module(JSContext *ctx, JSModuleDef *m) /* resolve each requested module */ for(i = 0; i < m->req_module_entries_count; i++) { JSReqModuleEntry *rme = &m->req_module_entries[i]; - m1 = js_host_resolve_imported_module(ctx, m->module_name, - rme->module_name); + m1 = js_host_resolve_imported_module_atom(ctx, m->module_name, + rme->module_name); if (!m1) return -1; rme->module = m1; @@ -27711,8 +27935,9 @@ static int js_instantiate_module(JSContext *ctx, JSModuleDef *m) return -1; } -/* warning: the returned atom is not allocated */ -static JSAtom js_get_script_or_module_name(JSContext *ctx) +/* return JS_ATOM_NULL if the name cannot be found. Only works with + not striped bytecode functions. */ +JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels) { JSStackFrame *sf; JSFunctionBytecode *b; @@ -27721,14 +27946,22 @@ static JSAtom js_get_script_or_module_name(JSContext *ctx) function. It does not work for eval(). Need to add a ScriptOrModule info in JSFunctionBytecode */ sf = ctx->rt->current_stack_frame; - assert(sf != NULL); - assert(JS_VALUE_GET_TAG(sf->cur_func) == JS_TAG_OBJECT); + if (!sf) + return JS_ATOM_NULL; + while (n_stack_levels-- > 0) { + sf = sf->prev_frame; + if (!sf) + return JS_ATOM_NULL; + } + if (JS_VALUE_GET_TAG(sf->cur_func) != JS_TAG_OBJECT) + return JS_ATOM_NULL; p = JS_VALUE_GET_OBJ(sf->cur_func); - assert(js_class_has_bytecode(p->class_id)); + if (!js_class_has_bytecode(p->class_id)) + return JS_ATOM_NULL; b = p->u.func.function_bytecode; if (!b->has_debug) return JS_ATOM_NULL; - return b->debug.filename; + return JS_DupAtom(ctx, b->debug.filename); } JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m) @@ -27755,13 +27988,14 @@ static JSValue js_import_meta(JSContext *ctx) JSAtom filename; JSModuleDef *m; - filename = js_get_script_or_module_name(ctx); + filename = JS_GetScriptOrModuleName(ctx, 0); if (filename == JS_ATOM_NULL) goto fail; /* XXX: inefficient, need to add a module or script pointer in JSFunctionBytecode */ m = js_find_loaded_module(ctx, filename); + JS_FreeAtom(ctx, filename); if (!m) { fail: JS_ThrowTypeError(ctx, "import.meta not supported in this context"); @@ -27770,6 +28004,31 @@ static JSValue js_import_meta(JSContext *ctx) return JS_GetImportMeta(ctx, m); } +/* used by os.Worker() and import() */ +JSModuleDef *JS_RunModule(JSContext *ctx, const char *basename, + const char *filename) +{ + JSModuleDef *m; + JSValue ret, func_obj; + + m = js_host_resolve_imported_module(ctx, basename, filename); + if (!m) + return NULL; + + if (js_resolve_module(ctx, m) < 0) { + js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED); + return NULL; + } + + /* Evaluate the module code */ + func_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m)); + ret = JS_EvalFunction(ctx, func_obj); + if (JS_IsException(ret)) + return NULL; + JS_FreeValue(ctx, ret); + return m; +} + static JSValue js_dynamic_import_job(JSContext *ctx, int argc, JSValueConst *argv) { @@ -27777,53 +28036,36 @@ static JSValue js_dynamic_import_job(JSContext *ctx, JSValueConst basename_val = argv[2]; JSValueConst specifier = argv[3]; JSModuleDef *m; - JSAtom basename = JS_ATOM_NULL, filename; - JSValue specifierString, ret, func_obj, err, ns; + const char *basename = NULL, *filename; + JSValue ret, err, ns; if (!JS_IsString(basename_val)) { JS_ThrowTypeError(ctx, "no function filename for import()"); goto exception; } - basename = JS_ValueToAtom(ctx, basename_val); - if (basename == JS_ATOM_NULL) + basename = JS_ToCString(ctx, basename_val); + if (!basename) goto exception; - specifierString = JS_ToString(ctx, specifier); - if (JS_IsException(specifierString)) - goto exception; - filename = JS_ValueToAtom(ctx, specifierString); - JS_FreeValue(ctx, specifierString); - if (filename == JS_ATOM_NULL) + filename = JS_ToCString(ctx, specifier); + if (!filename) goto exception; - m = js_host_resolve_imported_module(ctx, basename, filename); - JS_FreeAtom(ctx, filename); - if (!m) { - goto exception; - } - - if (js_resolve_module(ctx, m) < 0) { - js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED); - goto exception; - } - - /* Evaluate the module code */ - func_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m)); - ret = JS_EvalFunction(ctx, func_obj); - if (JS_IsException(ret)) + m = JS_RunModule(ctx, basename, filename); + JS_FreeCString(ctx, filename); + if (!m) goto exception; - JS_FreeValue(ctx, ret); /* return the module namespace */ ns = js_get_module_ns(ctx, m); - if (JS_IsException(ret)) + if (JS_IsException(ns)) goto exception; ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED, 1, (JSValueConst *)&ns); JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */ JS_FreeValue(ctx, ns); - JS_FreeAtom(ctx, basename); + JS_FreeCString(ctx, basename); return JS_UNDEFINED; exception: @@ -27832,7 +28074,7 @@ static JSValue js_dynamic_import_job(JSContext *ctx, 1, (JSValueConst *)&err); JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */ JS_FreeValue(ctx, err); - JS_FreeAtom(ctx, basename); + JS_FreeCString(ctx, basename); return JS_UNDEFINED; } @@ -27842,11 +28084,12 @@ static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier) JSValue promise, resolving_funcs[2], basename_val; JSValueConst args[4]; - basename = js_get_script_or_module_name(ctx); + basename = JS_GetScriptOrModuleName(ctx, 0); if (basename == JS_ATOM_NULL) basename_val = JS_NULL; else basename_val = JS_AtomToValue(ctx, basename); + JS_FreeAtom(ctx, basename); if (JS_IsException(basename_val)) return basename_val; @@ -28957,7 +29200,7 @@ static BOOL can_opt_put_ref_value(const uint8_t *bc_buf, int pos) return (bc_buf[pos + 1] == OP_put_ref_value && (opcode == OP_insert3 || opcode == OP_perm4 || - //opcode == OP_nop || + opcode == OP_nop || opcode == OP_rot3l)); } @@ -28967,7 +29210,7 @@ static BOOL can_opt_put_global_ref_value(const uint8_t *bc_buf, int pos) return (bc_buf[pos + 1] == OP_put_ref_value && (opcode == OP_insert3 || opcode == OP_perm4 || - //opcode == OP_nop || + opcode == OP_nop || opcode == OP_rot3l)); } @@ -29049,20 +29292,22 @@ static int optimize_scope_make_global_ref(JSContext *ctx, JSFunctionDef *s, end_pos = label_pos + 2; op = bc_buf[label_pos]; if (is_strict) { - switch(op) { - case OP_insert3: - op = OP_insert2; - break; - case OP_perm4: - op = OP_perm3; - break; - case OP_rot3l: - op = OP_swap; - break; - default: - abort(); + if (op != OP_nop) { + switch(op) { + case OP_insert3: + op = OP_insert2; + break; + case OP_perm4: + op = OP_perm3; + break; + case OP_rot3l: + op = OP_swap; + break; + default: + abort(); + } + bc_buf[pos++] = op; } - bc_buf[pos++] = op; } else { if (op == OP_insert3) bc_buf[pos++] = OP_dup; @@ -31567,20 +31812,20 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) } goto no_change; +#if SHORT_OPCODES case OP_typeof: if (OPTIMIZE) { /* simplify typeof tests */ if (code_match(&cc, pos_next, OP_push_atom_value, M4(OP_strict_eq, OP_strict_neq, OP_eq, OP_neq), -1)) { if (cc.line_num >= 0) line_num = cc.line_num; int op1 = (cc.op == OP_strict_eq || cc.op == OP_eq) ? OP_strict_eq : OP_strict_neq; -#if SHORT_OPCODES int op2 = -1; switch (cc.atom) { case JS_ATOM_undefined: - op2 = OP_is_undefined; + op2 = OP_typeof_is_undefined; break; case JS_ATOM_function: - op2 = OP_is_function; + op2 = OP_typeof_is_function; break; } if (op2 >= 0) { @@ -31604,19 +31849,10 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) goto has_label; } } -#endif - if (cc.atom == JS_ATOM_undefined) { - /* transform typeof(s) == "undefined" into s === void 0 */ - add_pc2line_info(s, bc_out.size, line_num); - dbuf_putc(&bc_out, OP_undefined); - dbuf_putc(&bc_out, op1); - JS_FreeAtom(ctx, cc.atom); - pos_next = cc.pos; - break; - } } } goto no_change; +#endif default: no_change: @@ -37067,10 +37303,9 @@ static JSValue js_function_bind(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JSBoundFunction *bf; - JSValue func_obj, name1; + JSValue func_obj, name1, len_val; JSObject *p; - int arg_count, i; - uint32_t len1; + int arg_count, i, ret; if (check_function(ctx, this_val)) return JS_EXCEPTION; @@ -37093,6 +37328,44 @@ static JSValue js_function_bind(JSContext *ctx, JSValueConst this_val, } p->u.bound_function = bf; + /* XXX: the spec could be simpler by only using GetOwnProperty */ + ret = JS_GetOwnProperty(ctx, NULL, this_val, JS_ATOM_length); + if (ret < 0) + goto exception; + if (!ret) { + len_val = JS_NewInt32(ctx, 0); + } else { + len_val = JS_GetProperty(ctx, this_val, JS_ATOM_length); + if (JS_IsException(len_val)) + goto exception; + if (JS_VALUE_GET_TAG(len_val) == JS_TAG_INT) { + /* most common case */ + int len1 = JS_VALUE_GET_INT(len_val); + if (len1 <= arg_count) + len1 = 0; + else + len1 -= arg_count; + len_val = JS_NewInt32(ctx, len1); + } else if (JS_VALUE_GET_NORM_TAG(len_val) == JS_TAG_FLOAT64) { + double d = JS_VALUE_GET_FLOAT64(len_val); + if (isnan(d)) { + d = 0.0; + } else { + d = trunc(d); + if (d <= (double)arg_count) + d = 0.0; + else + d -= (double)arg_count; /* also converts -0 to +0 */ + } + len_val = JS_NewFloat64(ctx, d); + } else { + JS_FreeValue(ctx, len_val); + len_val = JS_NewInt32(ctx, 0); + } + } + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, + len_val, JS_PROP_CONFIGURABLE); + name1 = JS_GetProperty(ctx, this_val, JS_ATOM_name); if (JS_IsException(name1)) goto exception; @@ -37105,15 +37378,6 @@ static JSValue js_function_bind(JSContext *ctx, JSValueConst this_val, goto exception; JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name1, JS_PROP_CONFIGURABLE); - if (js_get_length32(ctx, &len1, this_val)) - goto exception; - if (len1 <= (uint32_t)arg_count) - len1 = 0; - else - len1 -= arg_count; - JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, - JS_NewUint32(ctx, len1), - JS_PROP_CONFIGURABLE); return func_obj; exception: JS_FreeValue(ctx, func_obj); @@ -40455,10 +40719,6 @@ static JSValue js_string_pad(JSContext *ctx, JSValueConst this_val, len = p->len; if (len >= n) return str; - if (n > JS_STRING_LEN_MAX) { - JS_ThrowInternalError(ctx, "string too long"); - goto fail2; - } if (argc > 1 && !JS_IsUndefined(argv[1])) { v = JS_ToString(ctx, argv[1]); if (JS_IsException(v)) @@ -40473,6 +40733,10 @@ static JSValue js_string_pad(JSContext *ctx, JSValueConst this_val, p1 = NULL; } } + if (n > JS_STRING_LEN_MAX) { + JS_ThrowInternalError(ctx, "string too long"); + goto fail2; + } if (string_buffer_init(ctx, b, n)) goto fail3; n -= len; @@ -41932,14 +42196,11 @@ static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val, if (JS_IsException(obj)) goto fail; prop_flags = JS_PROP_C_W_E | JS_PROP_THROW; - group_name_ptr = NULL; - if (re_flags & LRE_FLAG_NAMED_GROUPS) { - uint32_t re_bytecode_len; + group_name_ptr = lre_get_groupnames(re_bytecode); + if (group_name_ptr) { groups = JS_NewObjectProto(ctx, JS_NULL); if (JS_IsException(groups)) goto fail; - re_bytecode_len = get_u32(re_bytecode + 3); - group_name_ptr = (char *)(re_bytecode + 7 + re_bytecode_len); } for(i = 0; i < capture_count; i++) { @@ -43724,6 +43985,7 @@ static const JSCFunctionListEntry js_reflect_funcs[] = { JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 1 ), JS_CFUNC_DEF("set", 3, js_reflect_set ), JS_CFUNC_DEF("setPrototypeOf", 2, js_reflect_setPrototypeOf ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Reflect", JS_PROP_CONFIGURABLE ), }; static const JSCFunctionListEntry js_reflect_obj[] = { |