summaryrefslogtreecommitdiff
path: root/quickjs.c
diff options
context:
space:
mode:
authorbellard <6490144+bellard@users.noreply.github.com>2020-09-06 19:10:15 +0200
committerbellard <6490144+bellard@users.noreply.github.com>2020-09-06 19:10:15 +0200
commit7c312df422572cf867f29a1d80693e8a77f7fb2a (patch)
treee6b8443835b36b6cea904e4330ef93e4ab5ed040 /quickjs.c
parent89007660998db0ee55f0ab3b34bb1a84f86fd3c4 (diff)
downloadquickjs-7c312df422572cf867f29a1d80693e8a77f7fb2a.tar.gz
quickjs-7c312df422572cf867f29a1d80693e8a77f7fb2a.zip
2020-09-06 release
Diffstat (limited to 'quickjs.c')
-rw-r--r--quickjs.c884
1 files changed, 573 insertions, 311 deletions
diff --git a/quickjs.c b/quickjs.c
index 8fbb7a3..efc1d54 100644
--- a/quickjs.c
+++ b/quickjs.c
@@ -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[] = {