This is an automated email from the ASF dual-hosted git repository.
vatamane pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/couchdb.git
The following commit(s) were added to refs/heads/main by this push:
new 5eb4adbc4 Update Quickjs: optimize global var access, fix
use-after-free error
5eb4adbc4 is described below
commit 5eb4adbc4ee083b859956ebf6933424e3c994376
Author: Nick Vatamaniuc <[email protected]>
AuthorDate: Mon Nov 3 17:48:00 2025 -0500
Update Quickjs: optimize global var access, fix use-after-free error
Optimized global variable access
https://github.com/bellard/quickjs/commit/a6816be23ae2bc511c7797489326f58934968323
Much faster destructuring at the expense of a slight incompatibility
with the spec when direct evals are present (v8 behaves the same way)
https://github.com/bellard/quickjs/commit/e015918dd834946a20b423310f7db812f63ce251
Remove duplicate test
https://github.com/bellard/quickjs/commit/961478d7bb1c4be7d0923cbf30032c15d6479af8
qjs `--strict` option (we don't use this)
https://github.com/bellard/quickjs/commit/baa186fc6e9c9a196ebceb7b1c762788d2c1517f
Fix use-after-free in Array.prototype.transfer (we don't use this)
https://github.com/bellard/quickjs/commit/75b5230000de87a68743bc8791e186137770012f
Fix `DataView` resizing
https://github.com/bellard/quickjs/commit/7cfddd06649c08c9dc10c65071ba71910e3de4aa
Fix length check in ArrayBuffer.prototype.slice
https://github.com/bellard/quickjs/commit/c6fe5a98fd3ef3b7064e6e0145dfebfe12449fea
More informative "not a constructor" error message
https://github.com/bellard/quickjs/commit/080c01f34696351d21f34f99081e93adb41ae74a
---
.../patches/01-spidermonkey-185-mode.patch | 6 +-
src/couch_quickjs/patches/02-test262-errors.patch | 10 +-
src/couch_quickjs/quickjs/quickjs-opcode.h | 11 +-
src/couch_quickjs/quickjs/quickjs.c | 1515 ++++++++++++--------
src/couch_quickjs/quickjs/test262_errors.txt | 16 +-
5 files changed, 939 insertions(+), 619 deletions(-)
diff --git a/src/couch_quickjs/patches/01-spidermonkey-185-mode.patch
b/src/couch_quickjs/patches/01-spidermonkey-185-mode.patch
index 05b8020c7..46f6cd831 100644
--- a/src/couch_quickjs/patches/01-spidermonkey-185-mode.patch
+++ b/src/couch_quickjs/patches/01-spidermonkey-185-mode.patch
@@ -1,6 +1,6 @@
---- quickjs-master/quickjs.c 2025-10-18 06:04:12
-+++ quickjs/quickjs.c 2025-10-20 10:47:26
-@@ -31017,10 +31017,24 @@
+--- quickjs-master/quickjs.c 2025-11-03 12:53:32
++++ quickjs/quickjs.c 2025-11-03 16:58:35
+@@ -31279,10 +31279,24 @@
if (s->token.val == TOK_FUNCTION ||
(token_is_pseudo_keyword(s, JS_ATOM_async) &&
peek_token(s, TRUE) == TOK_FUNCTION)) {
diff --git a/src/couch_quickjs/patches/02-test262-errors.patch
b/src/couch_quickjs/patches/02-test262-errors.patch
index a75d8b978..ac8106bda 100644
--- a/src/couch_quickjs/patches/02-test262-errors.patch
+++ b/src/couch_quickjs/patches/02-test262-errors.patch
@@ -1,8 +1,8 @@
---- quickjs-master/test262_errors.txt 2025-10-18 06:04:12
-+++ quickjs/test262_errors.txt 2025-10-20 10:49:07
-@@ -6,6 +6,8 @@
-
test262/test/annexB/language/expressions/assignmenttargettype/callexpression.js:33:
SyntaxError: invalid assignment left-hand side
-
test262/test/annexB/language/expressions/assignmenttargettype/cover-callexpression-and-asyncarrowhead.js:20:
SyntaxError: invalid assignment left-hand side
+--- quickjs-master/test262_errors.txt 2025-11-03 12:53:32
++++ quickjs/test262_errors.txt 2025-11-03 16:58:35
+@@ -19,6 +19,8 @@
+
test262/test/language/expressions/compound-assignment/S11.13.2_A6.10_T1.js:24:
Test262Error: #1: innerX === 2. Actual: 5
+
test262/test/language/expressions/compound-assignment/S11.13.2_A6.11_T1.js:24:
Test262Error: #1: innerX === 2. Actual: 5
test262/test/language/identifier-resolution/assign-to-global-undefined.js:20:
strict mode: expected error
+test262/test/language/statements/expression/S12.4_A1.js:15: unexpected error
type: Test262: This statement should not be evaluated.
+test262/test/language/statements/expression/S12.4_A1.js:15: strict mode:
unexpected error type: Test262: This statement should not be evaluated.
diff --git a/src/couch_quickjs/quickjs/quickjs-opcode.h
b/src/couch_quickjs/quickjs/quickjs-opcode.h
index 86c3ec406..d93852133 100644
--- a/src/couch_quickjs/quickjs/quickjs-opcode.h
+++ b/src/couch_quickjs/quickjs/quickjs-opcode.h
@@ -123,17 +123,14 @@ DEF( regexp, 1, 2, 1, none) /* create a RegExp
object from the pattern a
DEF( get_super, 1, 1, 1, none)
DEF( import, 1, 2, 1, none) /* dynamic module import */
-DEF( get_var_undef, 5, 0, 1, atom) /* push undefined if the variable does not
exist */
-DEF( get_var, 5, 0, 1, atom) /* throw an exception if the variable does
not exist */
-DEF( put_var, 5, 1, 0, atom) /* must come after get_var */
-DEF( put_var_init, 5, 1, 0, atom) /* must come after put_var. Used to
initialize a global lexical variable */
+DEF( get_var_undef, 3, 0, 1, var_ref) /* push undefined if the variable does
not exist */
+DEF( get_var, 3, 0, 1, var_ref) /* throw an exception if the variable
does not exist */
+DEF( put_var, 3, 1, 0, var_ref) /* must come after get_var */
+DEF( put_var_init, 3, 1, 0, var_ref) /* must come after put_var. Used to
initialize a global lexical variable */
DEF( get_ref_value, 1, 2, 3, none)
DEF( put_ref_value, 1, 3, 0, none)
-DEF( define_var, 6, 0, 0, atom_u8)
-DEF(check_define_var, 6, 0, 0, atom_u8)
-DEF( define_func, 6, 1, 0, atom_u8)
DEF( get_field, 5, 1, 1, atom)
DEF( get_field2, 5, 1, 2, atom)
DEF( put_field, 5, 2, 0, atom)
diff --git a/src/couch_quickjs/quickjs/quickjs.c
b/src/couch_quickjs/quickjs/quickjs.c
index 73429a705..c7657971d 100644
--- a/src/couch_quickjs/quickjs/quickjs.c
+++ b/src/couch_quickjs/quickjs/quickjs.c
@@ -166,6 +166,7 @@ enum {
JS_CLASS_STRING_ITERATOR, /* u.array_iterator_data */
JS_CLASS_REGEXP_STRING_ITERATOR, /* u.regexp_string_iterator_data */
JS_CLASS_GENERATOR, /* u.generator_data */
+ JS_CLASS_GLOBAL_OBJECT, /* u.global_object */
JS_CLASS_PROXY, /* u.proxy_data */
JS_CLASS_PROMISE, /* u.promise_data */
JS_CLASS_PROMISE_RESOLVE_FUNCTION, /* u.promise_function_data */
@@ -378,6 +379,8 @@ typedef struct JSVarRef {
int __gc_ref_count; /* corresponds to header.ref_count */
uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */
uint8_t is_detached;
+ uint8_t is_lexical; /* only used with global variables */
+ uint8_t is_const; /* only used with global variables */
};
};
JSValue *pvalue; /* pointer to the value, either on the stack or
@@ -542,11 +545,23 @@ typedef struct JSStringRope {
JSValue right; /* might be the empty string */
} JSStringRope;
+typedef enum {
+ JS_CLOSURE_LOCAL, /* 'var_idx' is the index of a local variable in the
parent function */
+ JS_CLOSURE_ARG, /* 'var_idx' is the index of a argument variable in the
parent function */
+ JS_CLOSURE_REF, /* 'var_idx' is the index of a closure variable in the
parent function */
+ JS_CLOSURE_GLOBAL_REF, /* 'var_idx' in the index of a closure
+ variable in the parent function
+ referencing a global variable */
+ JS_CLOSURE_GLOBAL_DECL, /* global variable declaration (eval code only) */
+ JS_CLOSURE_GLOBAL, /* global variable (eval code only) */
+ JS_CLOSURE_MODULE_DECL, /* definition of a module variable (eval code
only) */
+ JS_CLOSURE_MODULE_IMPORT, /* definition of a module import (eval code
only) */
+} JSClosureTypeEnum;
+
typedef struct JSClosureVar {
- uint8_t is_local : 1;
- uint8_t is_arg : 1;
- uint8_t is_const : 1;
- uint8_t is_lexical : 1;
+ JSClosureTypeEnum closure_type : 3;
+ uint8_t is_lexical : 1; /* lexical variable */
+ uint8_t is_const : 1; /* const variable (is_lexical = 1 if is_const = 1 */
uint8_t var_kind : 4; /* see JSVarKindEnum */
/* 8 bits available */
uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the
@@ -576,6 +591,7 @@ typedef enum {
JS_VAR_PRIVATE_GETTER,
JS_VAR_PRIVATE_SETTER, /* must come after JS_VAR_PRIVATE_GETTER */
JS_VAR_PRIVATE_GETTER_SETTER, /* must come after JS_VAR_PRIVATE_SETTER */
+ JS_VAR_GLOBAL_FUNCTION_DECL, /* global function definition, only in
JSVarDef */
} JSVarKindEnum;
/* XXX: could use a different structure in bytecode functions to save
@@ -715,6 +731,10 @@ typedef struct JSTypedArray {
BOOL track_rab; /* auto-track length of backing array buffer */
} JSTypedArray;
+typedef struct JSGlobalObject {
+ JSValue uninitialized_vars; /* hidden object containing the list of
uninitialized variables */
+} JSGlobalObject;
+
typedef struct JSAsyncFunctionState {
JSGCObjectHeader header;
JSValue this_val; /* 'this' argument */
@@ -1004,6 +1024,7 @@ struct JSObject {
} array; /* 12/20 bytes */
JSRegExp regexp; /* JS_CLASS_REGEXP: 8/16 bytes */
JSValue object_data; /* for JS_SetObjectData(): 8/16/16 bytes */
+ JSGlobalObject global_object;
} u;
};
@@ -1106,6 +1127,7 @@ static __maybe_unused void JS_DumpString(JSRuntime *rt,
const JSString *p);
static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt);
static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p);
static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p);
+static __maybe_unused void JS_DumpAtom(JSContext *ctx, const char *str, JSAtom
atom);
static __maybe_unused void JS_DumpValueRT(JSRuntime *rt, const char *str,
JSValueConst val);
static __maybe_unused void JS_DumpValue(JSContext *ctx, const char *str,
JSValueConst val);
static __maybe_unused void JS_DumpShapes(JSRuntime *rt);
@@ -1156,6 +1178,9 @@ static void js_regexp_string_iterator_mark(JSRuntime *rt,
JSValueConst val,
static void js_generator_finalizer(JSRuntime *rt, JSValue obj);
static void js_generator_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func);
+static void js_global_object_finalizer(JSRuntime *rt, JSValue obj);
+static void js_global_object_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
static void js_promise_finalizer(JSRuntime *rt, JSValue val);
static void js_promise_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func);
@@ -1230,6 +1255,7 @@ static BOOL typed_array_is_oob(JSObject *p);
static int js_typed_array_get_length_unsafe(JSContext *ctx, JSValueConst obj);
static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx);
static JSValue JS_ThrowTypeErrorArrayBufferOOB(JSContext *ctx);
+static JSVarRef *js_create_var_ref(JSContext *ctx, BOOL is_lexical);
static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx,
BOOL is_arg);
static void __async_func_free(JSRuntime *rt, JSAsyncFunctionState *s);
@@ -1321,6 +1347,8 @@ static JSValue get_date_string(JSContext *ctx,
JSValueConst this_val,
int argc, JSValueConst *argv, int magic);
static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv);
+static JSVarRef *js_global_object_find_uninitialized_var(JSContext *ctx,
JSObject *p,
+ JSAtom atom, BOOL
is_lexical);
static const JSClassExoticMethods js_arguments_exotic_methods;
static const JSClassExoticMethods js_string_exotic_methods;
@@ -1572,6 +1600,7 @@ static JSClassShortDef const js_std_class_def[] = {
{ JS_ATOM_String_Iterator, js_array_iterator_finalizer,
js_array_iterator_mark }, /* JS_CLASS_STRING_ITERATOR */
{ JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer,
js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */
{ JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /*
JS_CLASS_GENERATOR */
+ { JS_ATOM_Object, js_global_object_finalizer, js_global_object_mark }, /*
JS_CLASS_GLOBAL_OBJECT */
};
static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab,
@@ -5239,6 +5268,9 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx,
JSShape *sh, JSClassID clas
p->u.regexp.pattern = NULL;
p->u.regexp.bytecode = NULL;
break;
+ case JS_CLASS_GLOBAL_OBJECT:
+ p->u.global_object.uninitialized_vars = JS_UNDEFINED;
+ break;
default:
set_exotic:
if (ctx->rt->class_array[class_id].exotic) {
@@ -7294,6 +7326,22 @@ static JSValue JS_ThrowTypeErrorNotAnObject(JSContext
*ctx)
return JS_ThrowTypeError(ctx, "not an object");
}
+static JSValue JS_ThrowTypeErrorNotAConstructor(JSContext *ctx,
+ JSValueConst func_obj)
+{
+ const char *name;
+ if (!JS_IsFunction(ctx, func_obj))
+ goto fail;
+ name = get_prop_string(ctx, func_obj, JS_ATOM_name);
+ if (!name) {
+ fail:
+ return JS_ThrowTypeError(ctx, "not a constructor");
+ }
+ JS_ThrowTypeError(ctx, "%s is not a constructor", name);
+ JS_FreeCString(ctx, name);
+ return JS_EXCEPTION;
+}
+
static JSValue JS_ThrowTypeErrorNotASymbol(JSContext *ctx)
{
return JS_ThrowTypeError(ctx, "not a symbol");
@@ -7680,6 +7728,16 @@ static int JS_AutoInitProperty(JSContext *ctx, JSObject
*p, JSAtom prop,
prs->flags |= JS_PROP_VARREF;
pr->u.var_ref = JS_VALUE_GET_PTR(val);
pr->u.var_ref->header.ref_count++;
+ } else if (p->class_id == JS_CLASS_GLOBAL_OBJECT) {
+ JSVarRef *var_ref;
+ /* in the global object we use references */
+ var_ref = js_create_var_ref(ctx, FALSE);
+ if (!var_ref)
+ return -1;
+ prs->flags |= JS_PROP_VARREF;
+ pr->u.var_ref = var_ref;
+ var_ref->value = val;
+ var_ref->is_const = !(prs->flags & JS_PROP_WRITABLE);
} else {
pr->u.value = val;
}
@@ -8742,6 +8800,29 @@ static no_inline __exception int
convert_fast_array_to_array(JSContext *ctx,
return 0;
}
+static int remove_global_object_property(JSContext *ctx, JSObject *p,
+ JSShapeProperty *prs, JSProperty *pr)
+{
+ JSVarRef *var_ref;
+ JSObject *p1;
+ JSProperty *pr1;
+
+ var_ref = pr->u.var_ref;
+ if (var_ref->header.ref_count == 1)
+ return 0;
+ p1 = JS_VALUE_GET_OBJ(p->u.global_object.uninitialized_vars);
+ pr1 = add_property(ctx, p1, prs->atom, JS_PROP_C_W_E | JS_PROP_VARREF);
+ if (!pr1)
+ return -1;
+ pr1->u.var_ref = var_ref;
+ var_ref->header.ref_count++;
+ JS_FreeValue(ctx, var_ref->value);
+ var_ref->is_lexical = FALSE;
+ var_ref->is_const = FALSE;
+ var_ref->value = JS_UNINITIALIZED;
+ return 0;
+}
+
static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom)
{
JSShape *sh;
@@ -8779,6 +8860,11 @@ static int delete_property(JSContext *ctx, JSObject *p,
JSAtom atom)
sh->deleted_prop_count++;
/* free the entry */
pr1 = &p->prop[h - 1];
+ if (unlikely(p->class_id == JS_CLASS_GLOBAL_OBJECT)) {
+ if ((pr->flags & JS_PROP_TMASK) == JS_PROP_VARREF)
+ if (remove_global_object_property(ctx, p, pr, pr1))
+ return -1;
+ }
free_property(ctx->rt, pr1, pr->flags);
JS_FreeAtom(ctx, pr->atom);
/* put default values */
@@ -9137,10 +9223,9 @@ int JS_SetPropertyInternal(JSContext *ctx, JSValueConst
obj,
} else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags);
} else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
- /* JS_PROP_WRITABLE is always true for variable
- references, but they are write protected in module name
- spaces. */
- if (p->class_id == JS_CLASS_MODULE_NS)
+ /* XXX: already use var_ref->is_const. Cannot simplify use the
+ writable flag for JS_CLASS_MODULE_NS. */
+ if (p->class_id == JS_CLASS_MODULE_NS || pr->u.var_ref->is_const)
goto read_only_prop;
set_value(ctx, pr->u.var_ref->pvalue, val);
return TRUE;
@@ -9299,6 +9384,8 @@ int JS_SetPropertyInternal(JSContext *ctx, JSValueConst
obj,
goto generic_create_prop;
}
} else {
+ if (unlikely(p->class_id == JS_CLASS_GLOBAL_OBJECT))
+ goto generic_create_prop;
pr = add_property(ctx, p, prop, JS_PROP_C_W_E);
if (unlikely(!pr)) {
JS_FreeValue(ctx, val);
@@ -9533,7 +9620,9 @@ static int JS_CreateProperty(JSContext *ctx, JSObject *p,
{
JSProperty *pr;
int ret, prop_flags;
-
+ JSVarRef *var_ref;
+ JSObject *delete_obj;
+
/* add a new property or modify an existing exotic one */
if (p->is_exotic) {
if (p->class_id == JS_CLASS_ARRAY) {
@@ -9608,15 +9697,37 @@ static int JS_CreateProperty(JSContext *ctx, JSObject
*p,
return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not
extensible");
}
+ var_ref = NULL;
+ delete_obj = NULL;
if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
prop_flags = (flags & (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) |
JS_PROP_GETSET;
} else {
prop_flags = flags & JS_PROP_C_W_E;
+ if (p->class_id == JS_CLASS_GLOBAL_OBJECT) {
+ JSObject *p1 =
JS_VALUE_GET_OBJ(p->u.global_object.uninitialized_vars);
+ JSShapeProperty *prs1;
+ JSProperty *pr1;
+ prs1 = find_own_property(&pr1, p1, prop);
+ if (prs1) {
+ delete_obj = p1;
+ var_ref = pr1->u.var_ref;
+ var_ref->header.ref_count++;
+ } else {
+ var_ref = js_create_var_ref(ctx, FALSE);
+ if (!var_ref)
+ return -1;
+ }
+ var_ref->is_const = !(prop_flags & JS_PROP_WRITABLE);
+ prop_flags |= JS_PROP_VARREF;
+ }
}
pr = add_property(ctx, p, prop, prop_flags);
- if (unlikely(!pr))
+ if (unlikely(!pr)) {
+ if (var_ref)
+ free_var_ref(ctx->rt, var_ref);
return -1;
+ }
if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
pr->u.getset.getter = NULL;
if ((flags & JS_PROP_HAS_GET) && JS_IsFunction(ctx, getter)) {
@@ -9628,6 +9739,15 @@ static int JS_CreateProperty(JSContext *ctx, JSObject *p,
pr->u.getset.setter =
JS_VALUE_GET_OBJ(JS_DupValue(ctx, setter));
}
+ } else if (p->class_id == JS_CLASS_GLOBAL_OBJECT) {
+ if (delete_obj)
+ delete_property(ctx, delete_obj, prop);
+ pr->u.var_ref = var_ref;
+ if (flags & JS_PROP_HAS_VALUE) {
+ *var_ref->pvalue = JS_DupValue(ctx, val);
+ } else {
+ *var_ref->pvalue = JS_UNDEFINED;
+ }
} else {
if (flags & JS_PROP_HAS_VALUE) {
pr->u.value = JS_DupValue(ctx, val);
@@ -9782,6 +9902,10 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst
this_obj,
return -1;
/* convert to getset */
if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
+ if (unlikely(p->class_id == JS_CLASS_GLOBAL_OBJECT)) {
+ if (remove_global_object_property(ctx, p, prs, pr))
+ return -1;
+ }
free_var_ref(ctx->rt, pr->u.var_ref);
} else {
JS_FreeValue(ctx, pr->u.value);
@@ -9820,14 +9944,31 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst
this_obj,
} else {
if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
/* convert to data descriptor */
- if (js_shape_prepare_update(ctx, p, &prs))
+ JSVarRef *var_ref;
+ if (unlikely(p->class_id == JS_CLASS_GLOBAL_OBJECT)) {
+ var_ref = js_global_object_find_uninitialized_var(ctx,
p, prop, FALSE);
+ if (!var_ref)
+ return -1;
+ } else {
+ var_ref = NULL;
+ }
+ if (js_shape_prepare_update(ctx, p, &prs)) {
+ if (var_ref)
+ free_var_ref(ctx->rt, var_ref);
return -1;
+ }
if (pr->u.getset.getter)
JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT,
pr->u.getset.getter));
if (pr->u.getset.setter)
JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT,
pr->u.getset.setter));
- prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE);
- pr->u.value = JS_UNDEFINED;
+ if (var_ref) {
+ prs->flags = (prs->flags & ~JS_PROP_TMASK) |
+ JS_PROP_VARREF | JS_PROP_WRITABLE;
+ pr->u.var_ref = var_ref;
+ } else {
+ prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE);
+ pr->u.value = JS_UNDEFINED;
+ }
} else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
/* Note: JS_PROP_VARREF is always writable */
} else {
@@ -9854,8 +9995,6 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst
this_obj,
JS_DupValue(ctx, val));
}
}
- /* if writable is set to false, no longer a
- reference (for mapped arguments) */
if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) ==
JS_PROP_HAS_WRITABLE) {
JSValue val1;
if (p->class_id == JS_CLASS_MODULE_NS) {
@@ -9863,10 +10002,17 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst
this_obj,
}
if (js_shape_prepare_update(ctx, p, &prs))
return -1;
- val1 = JS_DupValue(ctx, *pr->u.var_ref->pvalue);
- free_var_ref(ctx->rt, pr->u.var_ref);
- pr->u.value = val1;
- prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE);
+ if (p->class_id == JS_CLASS_GLOBAL_OBJECT) {
+ pr->u.var_ref->is_const = TRUE; /* mark as
read-only */
+ prs->flags &= ~JS_PROP_WRITABLE;
+ } else {
+ /* if writable is set to false, no longer a
+ reference (for mapped arguments) */
+ val1 = JS_DupValue(ctx, *pr->u.var_ref->pvalue);
+ free_var_ref(ctx->rt, pr->u.var_ref);
+ pr->u.value = val1;
+ prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE);
+ }
}
} else if (prs->flags & JS_PROP_LENGTH) {
if (flags & JS_PROP_HAS_VALUE) {
@@ -10199,91 +10345,6 @@ static int JS_CheckDefineGlobalVar(JSContext *ctx,
JSAtom prop, int flags)
return 0;
}
-/* def_flags is (0, DEFINE_GLOBAL_LEX_VAR) |
- JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE */
-/* XXX: could support exotic global object. */
-static int JS_DefineGlobalVar(JSContext *ctx, JSAtom prop, int def_flags)
-{
- JSObject *p;
- JSShapeProperty *prs;
- JSProperty *pr;
- JSValue val;
- int flags;
-
- if (def_flags & DEFINE_GLOBAL_LEX_VAR) {
- p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
- flags = JS_PROP_ENUMERABLE | (def_flags & JS_PROP_WRITABLE) |
- JS_PROP_CONFIGURABLE;
- val = JS_UNINITIALIZED;
- } else {
- p = JS_VALUE_GET_OBJ(ctx->global_obj);
- flags = JS_PROP_ENUMERABLE | JS_PROP_WRITABLE |
- (def_flags & JS_PROP_CONFIGURABLE);
- val = JS_UNDEFINED;
- }
- prs = find_own_property1(p, prop);
- if (prs)
- return 0;
- if (!p->extensible)
- return 0;
- pr = add_property(ctx, p, prop, flags);
- if (unlikely(!pr))
- return -1;
- pr->u.value = val;
- return 0;
-}
-
-/* 'def_flags' is 0 or JS_PROP_CONFIGURABLE. */
-/* XXX: could support exotic global object. */
-static int JS_DefineGlobalFunction(JSContext *ctx, JSAtom prop,
- JSValueConst func, int def_flags)
-{
-
- JSObject *p;
- JSShapeProperty *prs;
- int flags;
-
- p = JS_VALUE_GET_OBJ(ctx->global_obj);
- prs = find_own_property1(p, prop);
- flags = JS_PROP_HAS_VALUE | JS_PROP_THROW;
- if (!prs || (prs->flags & JS_PROP_CONFIGURABLE)) {
- flags |= JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | def_flags |
- JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE |
JS_PROP_HAS_ENUMERABLE;
- }
- if (JS_DefineProperty(ctx, ctx->global_obj, prop, func,
- JS_UNDEFINED, JS_UNDEFINED, flags) < 0)
- return -1;
- return 0;
-}
-
-static inline JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop,
- BOOL throw_ref_error)
-{
- JSObject *p;
- JSShapeProperty *prs;
- JSProperty *pr;
-
- /* no exotic behavior is possible in global_var_obj */
- p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
- prs = find_own_property(&pr, p, prop);
- if (prs) {
- /* XXX: should handle JS_PROP_TMASK properties */
- if (unlikely(JS_IsUninitialized(pr->u.value)))
- return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
- return JS_DupValue(ctx, pr->u.value);
- }
-
- /* fast path */
- p = JS_VALUE_GET_OBJ(ctx->global_obj);
- prs = find_own_property(&pr, p, prop);
- if (prs) {
- if (likely((prs->flags & JS_PROP_TMASK) == 0))
- return JS_DupValue(ctx, pr->u.value);
- }
- return JS_GetPropertyInternal(ctx, ctx->global_obj, prop,
- ctx->global_obj, throw_ref_error);
-}
-
/* construct a reference to a global variable */
static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom prop, JSValue *sp)
{
@@ -10295,10 +10356,9 @@ static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom
prop, JSValue *sp)
p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
prs = find_own_property(&pr, p, prop);
if (prs) {
- /* XXX: should handle JS_PROP_AUTOINIT properties? */
/* XXX: conformance: do these tests in
OP_put_var_ref/OP_get_var_ref ? */
- if (unlikely(JS_IsUninitialized(pr->u.value))) {
+ if (unlikely(JS_IsUninitialized(*pr->u.var_ref->pvalue))) {
JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
return -1;
}
@@ -10321,62 +10381,6 @@ static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom
prop, JSValue *sp)
return 0;
}
-/* flag = 0: normal variable write
- flag = 1: initialize lexical variable
-*/
-static inline int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val,
- int flag)
-{
- JSObject *p;
- JSShapeProperty *prs;
- JSProperty *pr;
- int ret;
-
- /* no exotic behavior is possible in global_var_obj */
- p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
- prs = find_own_property(&pr, p, prop);
- if (prs) {
- /* XXX: should handle JS_PROP_AUTOINIT properties? */
- if (flag != 1) {
- if (unlikely(JS_IsUninitialized(pr->u.value))) {
- JS_FreeValue(ctx, val);
- JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
- return -1;
- }
- if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) {
- JS_FreeValue(ctx, val);
- return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop);
- }
- }
- set_value(ctx, &pr->u.value, val);
- return 0;
- }
-
- p = JS_VALUE_GET_OBJ(ctx->global_obj);
- prs = find_own_property(&pr, p, prop);
- if (prs) {
- if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE |
- JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) {
- /* fast path */
- set_value(ctx, &pr->u.value, val);
- return 0;
- }
- }
- /* slow path */
- ret = JS_HasProperty(ctx, ctx->global_obj, prop);
- if (ret < 0) {
- JS_FreeValue(ctx, val);
- return -1;
- }
- if (ret == 0 && is_strict_mode(ctx)) {
- JS_FreeValue(ctx, val);
- JS_ThrowReferenceErrorNotDefined(ctx, prop);
- return -1;
- }
- return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val,
ctx->global_obj,
- JS_PROP_THROW_STRICT);
-}
-
/* return -1, FALSE or TRUE */
static int JS_DeleteGlobalVar(JSContext *ctx, JSAtom prop)
{
@@ -13956,6 +13960,13 @@ static __maybe_unused void print_atom(JSContext *ctx,
JSAtom atom)
js_print_atom(s, atom);
}
+static __maybe_unused void JS_DumpAtom(JSContext *ctx, const char *str, JSAtom
atom)
+{
+ printf("%s=", str);
+ print_atom(ctx, atom);
+ printf("\n");
+}
+
static __maybe_unused void JS_DumpValue(JSContext *ctx, const char *str,
JSValueConst val)
{
printf("%s=", str);
@@ -16483,6 +16494,25 @@ static JSValueConst JS_GetActiveFunction(JSContext
*ctx)
return ctx->rt->current_stack_frame->cur_func;
}
+static JSVarRef *js_create_var_ref(JSContext *ctx, BOOL is_lexical)
+{
+ JSVarRef *var_ref;
+ var_ref = js_malloc(ctx, sizeof(JSVarRef));
+ if (!var_ref)
+ return NULL;
+ var_ref->header.ref_count = 1;
+ if (is_lexical)
+ var_ref->value = JS_UNINITIALIZED;
+ else
+ var_ref->value = JS_UNDEFINED;
+ var_ref->pvalue = &var_ref->value;
+ var_ref->is_detached = TRUE;
+ var_ref->is_lexical = FALSE;
+ var_ref->is_const = FALSE;
+ add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
+ return var_ref;
+}
+
static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf,
int var_idx, BOOL is_arg)
{
@@ -16509,6 +16539,8 @@ static JSVarRef *get_var_ref(JSContext *ctx,
JSStackFrame *sf,
var_ref->header.ref_count = 1;
add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
var_ref->is_detached = FALSE;
+ var_ref->is_lexical = FALSE;
+ var_ref->is_const = FALSE;
list_add_tail(&var_ref->var_ref_link, &sf->var_ref_list);
if (sf->js_mode & JS_MODE_ASYNC) {
/* The stack frame is detached and may be destroyed at any
@@ -16528,10 +16560,212 @@ static JSVarRef *get_var_ref(JSContext *ctx,
JSStackFrame *sf,
return var_ref;
}
+static void js_global_object_finalizer(JSRuntime *rt, JSValue obj)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ JS_FreeValueRT(rt, p->u.global_object.uninitialized_vars);
+}
+
+static void js_global_object_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JS_MarkValue(rt, p->u.global_object.uninitialized_vars, mark_func);
+}
+
+static JSVarRef *js_global_object_get_uninitialized_var(JSContext *ctx,
JSObject *p1,
+ JSAtom atom)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(p1->u.global_object.uninitialized_vars);
+ JSShapeProperty *prs;
+ JSProperty *pr;
+ JSVarRef *var_ref;
+
+ prs = find_own_property(&pr, p, atom);
+ if (prs) {
+ assert((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF);
+ var_ref = pr->u.var_ref;
+ var_ref->header.ref_count++;
+ return var_ref;
+ }
+
+ var_ref = js_create_var_ref(ctx, TRUE);
+ if (!var_ref)
+ return NULL;
+ pr = add_property(ctx, p, atom, JS_PROP_C_W_E | JS_PROP_VARREF);
+ if (unlikely(!pr)) {
+ free_var_ref(ctx->rt, var_ref);
+ return NULL;
+ }
+ pr->u.var_ref = var_ref;
+ var_ref->header.ref_count++;
+ return var_ref;
+}
+
+/* return a new variable reference. Get it from the uninitialized
+ variables if it is present. Return NULL in case of memory error. */
+static JSVarRef *js_global_object_find_uninitialized_var(JSContext *ctx,
JSObject *p,
+ JSAtom atom, BOOL
is_lexical)
+{
+ JSObject *p1;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+ JSVarRef *var_ref;
+
+ p1 = JS_VALUE_GET_OBJ(p->u.global_object.uninitialized_vars);
+ prs = find_own_property(&pr, p1, atom);
+ if (prs) {
+ assert((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF);
+ var_ref = pr->u.var_ref;
+ var_ref->header.ref_count++;
+ delete_property(ctx, p1, atom);
+ if (!is_lexical)
+ var_ref->value = JS_UNDEFINED;
+ } else {
+ var_ref = js_create_var_ref(ctx, is_lexical);
+ if (!var_ref)
+ return NULL;
+ }
+ return var_ref;
+}
+
+static JSVarRef *js_closure_define_global_var(JSContext *ctx, JSClosureVar *cv,
+ BOOL is_direct_or_indirect_eval)
+{
+ JSObject *p, *p1;
+ JSShapeProperty *prs;
+ int flags;
+ JSProperty *pr;
+ JSVarRef *var_ref;
+
+ if (cv->is_lexical) {
+ p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
+ flags = JS_PROP_ENUMERABLE | JS_PROP_CONFIGURABLE;
+ if (!cv->is_const)
+ flags |= JS_PROP_WRITABLE;
+
+ prs = find_own_property(&pr, p, cv->var_name);
+ if (prs) {
+ assert((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF);
+ var_ref = pr->u.var_ref;
+ var_ref->header.ref_count++;
+ return var_ref;
+ }
+
+ /* if there is a corresponding global variable, reuse its
+ reference and create a new one for the global variable */
+ p1 = JS_VALUE_GET_OBJ(ctx->global_obj);
+ prs = find_own_property(&pr, p1, cv->var_name);
+ if (prs && (prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
+ JSVarRef *var_ref1;
+ var_ref1 = js_create_var_ref(ctx, FALSE);
+ if (!var_ref1)
+ return NULL;
+ var_ref = pr->u.var_ref;
+ var_ref1->value = var_ref->value;
+ var_ref->value = JS_UNINITIALIZED;
+ pr->u.var_ref = var_ref1;
+ goto add_var_ref;
+ }
+ } else {
+ p = JS_VALUE_GET_OBJ(ctx->global_obj);
+ flags = JS_PROP_ENUMERABLE | JS_PROP_WRITABLE;
+ if (is_direct_or_indirect_eval)
+ flags |= JS_PROP_CONFIGURABLE;
+
+ prs = find_own_property(&pr, p, cv->var_name);
+ if (prs) {
+ if ((prs->flags & JS_PROP_TMASK) != JS_PROP_VARREF) {
+ var_ref = js_global_object_get_uninitialized_var(ctx, p,
cv->var_name);
+ if (!var_ref)
+ return NULL;
+ } else {
+ var_ref = pr->u.var_ref;
+ var_ref->header.ref_count++;
+ }
+ if (cv->var_kind == JS_VAR_GLOBAL_FUNCTION_DECL &&
+ (prs->flags & JS_PROP_CONFIGURABLE)) {
+ /* update the property flags if possible when
+ declaring a global function */
+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
+ free_property(ctx->rt, pr, prs->flags);
+ prs->flags = flags | JS_PROP_VARREF;
+ pr->u.var_ref = var_ref;
+ var_ref->header.ref_count++;
+ } else {
+ assert((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF);
+ prs->flags = (prs->flags & ~JS_PROP_C_W_E) | flags;
+ }
+ var_ref->is_const = FALSE;
+ }
+ return var_ref;
+ }
+
+ if (!p->extensible) {
+ return js_global_object_get_uninitialized_var(ctx, p,
cv->var_name);
+ }
+ }
+
+ /* if there is a corresponding uninitialized variable, use it */
+ p1 = JS_VALUE_GET_OBJ(ctx->global_obj);
+ var_ref = js_global_object_find_uninitialized_var(ctx, p1, cv->var_name,
cv->is_lexical);
+ if (!var_ref)
+ return NULL;
+ add_var_ref:
+ if (cv->is_lexical) {
+ var_ref->is_lexical = TRUE;
+ var_ref->is_const = cv->is_const;
+ }
+
+ pr = add_property(ctx, p, cv->var_name, flags | JS_PROP_VARREF);
+ if (unlikely(!pr)) {
+ free_var_ref(ctx->rt, var_ref);
+ return NULL;
+ }
+ pr->u.var_ref = var_ref;
+ var_ref->header.ref_count++;
+ return var_ref;
+}
+
+static JSVarRef *js_closure_global_var(JSContext *ctx, JSClosureVar *cv)
+{
+ JSObject *p;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+ JSVarRef *var_ref;
+
+ p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
+ prs = find_own_property(&pr, p, cv->var_name);
+ if (prs) {
+ assert((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF);
+ var_ref = pr->u.var_ref;
+ var_ref->header.ref_count++;
+ return var_ref;
+ }
+ p = JS_VALUE_GET_OBJ(ctx->global_obj);
+ redo:
+ prs = find_own_property(&pr, p, cv->var_name);
+ if (prs) {
+ if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT)) {
+ /* Instantiate property and retry */
+ if (JS_AutoInitProperty(ctx, p, cv->var_name, pr, prs))
+ return NULL;
+ goto redo;
+ }
+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
+ var_ref = pr->u.var_ref;
+ var_ref->header.ref_count++;
+ return var_ref;
+ }
+ }
+ return js_global_object_get_uninitialized_var(ctx, p, cv->var_name);
+}
+
static JSValue js_closure2(JSContext *ctx, JSValue func_obj,
JSFunctionBytecode *b,
JSVarRef **cur_var_refs,
- JSStackFrame *sf)
+ JSStackFrame *sf,
+ BOOL is_eval, JSModuleDef *m)
{
JSObject *p;
JSVarRef **var_refs;
@@ -16546,18 +16780,56 @@ static JSValue js_closure2(JSContext *ctx, JSValue
func_obj,
if (!var_refs)
goto fail;
p->u.func.var_refs = var_refs;
+ if (is_eval) {
+ /* first pass to check the global variable definitions */
+ for(i = 0; i < b->closure_var_count; i++) {
+ JSClosureVar *cv = &b->closure_var[i];
+ if (cv->closure_type == JS_CLOSURE_GLOBAL_DECL) {
+ int flags;
+ flags = 0;
+ if (cv->is_lexical)
+ flags |= DEFINE_GLOBAL_LEX_VAR;
+ if (cv->var_kind == JS_VAR_GLOBAL_FUNCTION_DECL)
+ flags |= DEFINE_GLOBAL_FUNC_VAR;
+ if (JS_CheckDefineGlobalVar(ctx, cv->var_name, flags))
+ goto fail;
+ }
+ }
+ }
for(i = 0; i < b->closure_var_count; i++) {
JSClosureVar *cv = &b->closure_var[i];
JSVarRef *var_ref;
- if (cv->is_local) {
+ switch(cv->closure_type) {
+ case JS_CLOSURE_MODULE_IMPORT:
+ /* imported from other modules */
+ continue;
+ case JS_CLOSURE_MODULE_DECL:
+ var_ref = js_create_var_ref(ctx, cv->is_lexical);
+ break;
+ case JS_CLOSURE_GLOBAL_DECL:
+ var_ref = js_closure_define_global_var(ctx, cv,
b->is_direct_or_indirect_eval);
+ break;
+ case JS_CLOSURE_GLOBAL:
+ var_ref = js_closure_global_var(ctx, cv);
+ break;
+ case JS_CLOSURE_LOCAL:
/* reuse the existing variable reference if it already exists
*/
- var_ref = get_var_ref(ctx, sf, cv->var_idx, cv->is_arg);
- if (!var_ref)
- goto fail;
- } else {
+ var_ref = get_var_ref(ctx, sf, cv->var_idx, FALSE);
+ break;
+ case JS_CLOSURE_ARG:
+ /* reuse the existing variable reference if it already exists
*/
+ var_ref = get_var_ref(ctx, sf, cv->var_idx, TRUE);
+ break;
+ case JS_CLOSURE_REF:
+ case JS_CLOSURE_GLOBAL_REF:
var_ref = cur_var_refs[cv->var_idx];
var_ref->header.ref_count++;
+ break;
+ default:
+ abort();
}
+ if (!var_ref)
+ goto fail;
var_refs[i] = var_ref;
}
}
@@ -16598,7 +16870,7 @@ static const uint16_t func_kind_to_class_id[] = {
static JSValue js_closure(JSContext *ctx, JSValue bfunc,
JSVarRef **cur_var_refs,
- JSStackFrame *sf)
+ JSStackFrame *sf, BOOL is_eval)
{
JSFunctionBytecode *b;
JSValue func_obj;
@@ -16610,7 +16882,7 @@ static JSValue js_closure(JSContext *ctx, JSValue bfunc,
JS_FreeValue(ctx, bfunc);
return JS_EXCEPTION;
}
- func_obj = js_closure2(ctx, func_obj, b, cur_var_refs, sf);
+ func_obj = js_closure2(ctx, func_obj, b, cur_var_refs, sf, is_eval, NULL);
if (JS_IsException(func_obj)) {
/* bfunc has been freed */
goto fail;
@@ -16697,7 +16969,7 @@ static int js_op_define_class(JSContext *ctx, JSValue
*sp,
JS_CLASS_BYTECODE_FUNCTION);
if (JS_IsException(ctor))
goto fail;
- ctor = js_closure2(ctx, ctor, b, cur_var_refs, sf);
+ ctor = js_closure2(ctx, ctor, b, cur_var_refs, sf, FALSE, NULL);
bfunc = JS_UNDEFINED;
if (JS_IsException(ctor))
goto fail;
@@ -17130,7 +17402,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx,
JSValueConst func_obj,
*sp++ = JS_DupValue(ctx, b->cpool[*pc++]);
BREAK;
CASE(OP_fclosure8):
- *sp++ = js_closure(ctx, JS_DupValue(ctx, b->cpool[*pc++]),
var_refs, sf);
+ *sp++ = js_closure(ctx, JS_DupValue(ctx, b->cpool[*pc++]),
var_refs, sf, FALSE);
if (unlikely(JS_IsException(sp[-1])))
goto exception;
BREAK;
@@ -17384,7 +17656,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx,
JSValueConst func_obj,
{
JSValue bfunc = JS_DupValue(ctx, b->cpool[get_u32(pc)]);
pc += 4;
- *sp++ = js_closure(ctx, bfunc, var_refs, sf);
+ *sp++ = js_closure(ctx, bfunc, var_refs, sf, FALSE);
if (unlikely(JS_IsException(sp[-1])))
goto exception;
}
@@ -17683,74 +17955,72 @@ static JSValue JS_CallInternal(JSContext *caller_ctx,
JSValueConst func_obj,
CASE(OP_get_var_undef):
CASE(OP_get_var):
{
+ int idx;
JSValue val;
- JSAtom atom;
- atom = get_u32(pc);
- pc += 4;
- sf->cur_pc = pc;
-
- val = JS_GetGlobalVar(ctx, atom, opcode - OP_get_var_undef);
- if (unlikely(JS_IsException(val)))
- goto exception;
- *sp++ = val;
+ idx = get_u16(pc);
+ pc += 2;
+ val = *var_refs[idx]->pvalue;
+ if (unlikely(JS_IsUninitialized(val))) {
+ JSClosureVar *cv = &b->closure_var[idx];
+ if (cv->is_lexical) {
+ JS_ThrowReferenceErrorUninitialized(ctx, cv->var_name);
+ goto exception;
+ } else {
+ sf->cur_pc = pc;
+ sp[0] = JS_GetPropertyInternal(ctx, ctx->global_obj,
+ cv->var_name,
+ ctx->global_obj,
+ opcode -
OP_get_var_undef);
+ if (JS_IsException(sp[0]))
+ goto exception;
+ }
+ } else {
+ sp[0] = JS_DupValue(ctx, val);
+ }
+ sp++;
}
BREAK;
CASE(OP_put_var):
CASE(OP_put_var_init):
{
- int ret;
- JSAtom atom;
- atom = get_u32(pc);
- pc += 4;
- sf->cur_pc = pc;
-
- ret = JS_SetGlobalVar(ctx, atom, sp[-1], opcode - OP_put_var);
- sp--;
- if (unlikely(ret < 0))
- goto exception;
- }
- BREAK;
-
- CASE(OP_check_define_var):
- {
- JSAtom atom;
- int flags;
- atom = get_u32(pc);
- flags = pc[4];
- pc += 5;
- sf->cur_pc = pc;
- if (JS_CheckDefineGlobalVar(ctx, atom, flags))
- goto exception;
- }
- BREAK;
- CASE(OP_define_var):
- {
- JSAtom atom;
- int flags;
- atom = get_u32(pc);
- flags = pc[4];
- pc += 5;
- sf->cur_pc = pc;
- if (JS_DefineGlobalVar(ctx, atom, flags))
- goto exception;
- }
- BREAK;
- CASE(OP_define_func):
- {
- JSAtom atom;
- int flags;
- atom = get_u32(pc);
- flags = pc[4];
- pc += 5;
- sf->cur_pc = pc;
- if (JS_DefineGlobalFunction(ctx, atom, sp[-1], flags))
- goto exception;
- JS_FreeValue(ctx, sp[-1]);
+ int idx, ret;
+ JSVarRef *var_ref;
+ idx = get_u16(pc);
+ pc += 2;
+ var_ref = var_refs[idx];
+ if (unlikely(JS_IsUninitialized(*var_ref->pvalue) ||
+ var_ref->is_const)) {
+ JSClosureVar *cv = &b->closure_var[idx];
+ if (var_ref->is_lexical) {
+ if (opcode == OP_put_var_init)
+ goto put_var_ok;
+ if (JS_IsUninitialized(*var_ref->pvalue))
+ JS_ThrowReferenceErrorUninitialized(ctx,
cv->var_name);
+ else
+ JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW,
cv->var_name);
+ goto exception;
+ } else {
+ sf->cur_pc = pc;
+ ret = JS_HasProperty(ctx, ctx->global_obj,
cv->var_name);
+ if (ret < 0)
+ goto exception;
+ if (ret == 0 && is_strict_mode(ctx)) {
+ JS_ThrowReferenceErrorNotDefined(ctx,
cv->var_name);
+ goto exception;
+ }
+ ret = JS_SetPropertyInternal(ctx, ctx->global_obj,
cv->var_name, sp[-1],
+ ctx->global_obj,
JS_PROP_THROW_STRICT);
+ if (ret < 0)
+ goto exception;
+ }
+ } else {
+ put_var_ok:
+ set_value(ctx, var_ref->pvalue, sp[-1]);
+ }
sp--;
}
BREAK;
-
CASE(OP_get_loc):
{
int idx;
@@ -19832,7 +20102,7 @@ static JSValue JS_CallConstructorInternal(JSContext
*ctx,
goto not_a_function;
p = JS_VALUE_GET_OBJ(func_obj);
if (unlikely(!p->is_constructor))
- return JS_ThrowTypeError(ctx, "not a constructor");
+ return JS_ThrowTypeErrorNotAConstructor(ctx, func_obj);
if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) {
JSClassCall *call_func;
call_func = ctx->rt->class_array[p->class_id].call;
@@ -24889,18 +25159,19 @@ done:
return js_parse_expect(s, ']');
}
-/* XXX: remove */
+/* check if scope chain contains a with statement */
static BOOL has_with_scope(JSFunctionDef *s, int scope_level)
{
- /* check if scope chain contains a with statement */
while (s) {
- int scope_idx = s->scopes[scope_level].first;
- while (scope_idx >= 0) {
- JSVarDef *vd = &s->vars[scope_idx];
-
- if (vd->var_name == JS_ATOM__with_)
- return TRUE;
- scope_idx = vd->scope_next;
+ /* no with in strict mode */
+ if (!(s->js_mode & JS_MODE_STRICT)) {
+ int scope_idx = s->scopes[scope_level].first;
+ while (scope_idx >= 0) {
+ JSVarDef *vd = &s->vars[scope_idx];
+ if (vd->var_name == JS_ATOM__with_)
+ return TRUE;
+ scope_idx = vd->scope_next;
+ }
}
/* check parent scopes */
scope_level = s->parent_scope_level;
@@ -24933,7 +25204,11 @@ static __exception int get_lvalue(JSParseState *s, int
*popcode, int *pscope,
}
if (name == JS_ATOM_this || name == JS_ATOM_new_target)
goto invalid_lvalue;
- depth = 2; /* will generate OP_get_ref_value */
+ if (has_with_scope(fd, scope)) {
+ depth = 2; /* will generate OP_get_ref_value */
+ } else {
+ depth = 0;
+ }
break;
case OP_get_field:
name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
@@ -24970,16 +25245,22 @@ static __exception int get_lvalue(JSParseState *s,
int *popcode, int *pscope,
/* get the value but keep the object/fields on the stack */
switch(opcode) {
case OP_scope_get_var:
- label = new_label(s);
- if (label < 0)
- return -1;
- emit_op(s, OP_scope_make_ref);
- emit_atom(s, name);
- emit_u32(s, label);
- emit_u16(s, scope);
- update_label(fd, label, 1);
- emit_op(s, OP_get_ref_value);
- opcode = OP_get_ref_value;
+ if (depth != 0) {
+ label = new_label(s);
+ if (label < 0)
+ return -1;
+ emit_op(s, OP_scope_make_ref);
+ emit_atom(s, name);
+ emit_u32(s, label);
+ emit_u16(s, scope);
+ update_label(fd, label, 1);
+ emit_op(s, OP_get_ref_value);
+ opcode = OP_get_ref_value;
+ } else {
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, name);
+ emit_u16(s, scope);
+ }
break;
case OP_get_field:
emit_op(s, OP_get_field2);
@@ -25004,15 +25285,17 @@ static __exception int get_lvalue(JSParseState *s,
int *popcode, int *pscope,
} else {
switch(opcode) {
case OP_scope_get_var:
- label = new_label(s);
- if (label < 0)
- return -1;
- emit_op(s, OP_scope_make_ref);
- emit_atom(s, name);
- emit_u32(s, label);
- emit_u16(s, scope);
- update_label(fd, label, 1);
- opcode = OP_get_ref_value;
+ if (depth != 0) {
+ label = new_label(s);
+ if (label < 0)
+ return -1;
+ emit_op(s, OP_scope_make_ref);
+ emit_atom(s, name);
+ emit_u32(s, label);
+ emit_u16(s, scope);
+ update_label(fd, label, 1);
+ opcode = OP_get_ref_value;
+ }
break;
default:
break;
@@ -25046,6 +25329,21 @@ static void put_lvalue(JSParseState *s, int opcode,
int scope,
BOOL is_let)
{
switch(opcode) {
+ case OP_scope_get_var:
+ /* depth = 0 */
+ switch(special) {
+ case PUT_LVALUE_NOKEEP:
+ case PUT_LVALUE_NOKEEP_DEPTH:
+ case PUT_LVALUE_KEEP_SECOND:
+ case PUT_LVALUE_NOKEEP_BOTTOM:
+ break;
+ case PUT_LVALUE_KEEP_TOP:
+ emit_op(s, OP_dup);
+ break;
+ default:
+ abort();
+ }
+ break;
case OP_get_field:
case OP_scope_get_private_field:
/* depth = 1 */
@@ -25117,8 +25415,6 @@ static void put_lvalue(JSParseState *s, int opcode, int
scope,
switch(opcode) {
case OP_scope_get_var: /* val -- */
- assert(special == PUT_LVALUE_NOKEEP ||
- special == PUT_LVALUE_NOKEEP_DEPTH);
emit_op(s, is_let ? OP_scope_put_var_init : OP_scope_put_var);
emit_u32(s, name); /* has refcount */
emit_u16(s, scope);
@@ -25472,6 +25768,8 @@ static int js_parse_destructuring_element(JSParseState
*s, int tok, int is_arg,
/* swap ref and lvalue object if any */
if (prop_name == JS_ATOM_NULL) {
switch(depth_lvalue) {
+ case 0:
+ break;
case 1:
/* source prop x -> x source prop */
emit_op(s, OP_rot3r);
@@ -25485,9 +25783,13 @@ static int js_parse_destructuring_element(JSParseState
*s, int tok, int is_arg,
emit_op(s, OP_rot5l);
emit_op(s, OP_rot5l);
break;
+ default:
+ abort();
}
} else {
switch(depth_lvalue) {
+ case 0:
+ break;
case 1:
/* source x -> x source */
emit_op(s, OP_swap);
@@ -25500,6 +25802,8 @@ static int js_parse_destructuring_element(JSParseState
*s, int tok, int is_arg,
/* source x y z -> x y z source */
emit_op(s, OP_rot4l);
break;
+ default:
+ abort();
}
}
}
@@ -27151,7 +27455,7 @@ static __exception int
js_parse_assign_expr2(JSParseState *s, int parse_flags)
}
if (op == '=') {
- if (opcode == OP_get_ref_value && name == name0) {
+ if ((opcode == OP_get_ref_value || opcode == OP_scope_get_var) &&
name == name0) {
set_object_name(s, name);
}
} else {
@@ -27186,11 +27490,14 @@ static __exception int
js_parse_assign_expr2(JSParseState *s, int parse_flags)
return -1;
}
- if (opcode == OP_get_ref_value && name == name0) {
+ if ((opcode == OP_get_ref_value || opcode == OP_scope_get_var) && name
== name0) {
set_object_name(s, name);
}
switch(depth_lvalue) {
+ case 0:
+ emit_op(s, OP_dup);
+ break;
case 1:
emit_op(s, OP_insert2);
break;
@@ -29486,31 +29793,11 @@ static int js_resolve_module(JSContext *ctx,
JSModuleDef *m)
return 0;
}
-static JSVarRef *js_create_module_var(JSContext *ctx, BOOL is_lexical)
-{
- JSVarRef *var_ref;
- var_ref = js_malloc(ctx, sizeof(JSVarRef));
- if (!var_ref)
- return NULL;
- var_ref->header.ref_count = 1;
- if (is_lexical)
- var_ref->value = JS_UNINITIALIZED;
- else
- var_ref->value = JS_UNDEFINED;
- var_ref->pvalue = &var_ref->value;
- var_ref->is_detached = TRUE;
- add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
- return var_ref;
-}
-
/* Create the <eval> function associated with the module */
static int js_create_module_bytecode_function(JSContext *ctx, JSModuleDef *m)
{
JSFunctionBytecode *b;
- int i;
- JSVarRef **var_refs;
JSValue func_obj, bfunc;
- JSObject *p;
bfunc = m->func_obj;
func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
@@ -29519,40 +29806,14 @@ static int
js_create_module_bytecode_function(JSContext *ctx, JSModuleDef *m)
if (JS_IsException(func_obj))
return -1;
b = JS_VALUE_GET_PTR(bfunc);
-
- p = JS_VALUE_GET_OBJ(func_obj);
- p->u.func.function_bytecode = b;
- b->header.ref_count++;
- p->u.func.home_object = NULL;
- p->u.func.var_refs = NULL;
- if (b->closure_var_count) {
- var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count);
- if (!var_refs)
- goto fail;
- p->u.func.var_refs = var_refs;
-
- /* create the global variables. The other variables are
- imported from other modules */
- for(i = 0; i < b->closure_var_count; i++) {
- JSClosureVar *cv = &b->closure_var[i];
- JSVarRef *var_ref;
- if (cv->is_local) {
- var_ref = js_create_module_var(ctx, cv->is_lexical);
- if (!var_ref)
- goto fail;
-#ifdef DUMP_MODULE_RESOLVE
- printf("local %d: %p\n", i, var_ref);
-#endif
- var_refs[i] = var_ref;
- }
- }
+ func_obj = js_closure2(ctx, func_obj, b, NULL, NULL, TRUE, m);
+ if (JS_IsException(func_obj)) {
+ m->func_obj = JS_UNDEFINED; /* XXX: keep it ? */
+ JS_FreeValue(ctx, func_obj);
+ return -1;
}
m->func_obj = func_obj;
- JS_FreeValue(ctx, bfunc);
return 0;
- fail:
- JS_FreeValue(ctx, func_obj);
- return -1;
}
/* must be done before js_link_module() because of cyclic references */
@@ -29572,7 +29833,7 @@ static int js_create_module_function(JSContext *ctx,
JSModuleDef *m)
for(i = 0; i < m->export_entries_count; i++) {
JSExportEntry *me = &m->export_entries[i];
if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
- var_ref = js_create_module_var(ctx, FALSE);
+ var_ref = js_create_var_ref(ctx, FALSE);
if (!var_ref)
return -1;
me->u.local.var_ref = var_ref;
@@ -29731,7 +29992,7 @@ static int js_inner_module_linking(JSContext *ctx,
JSModuleDef *m,
val = JS_GetModuleNamespace(ctx, m2);
if (JS_IsException(val))
goto fail;
- var_ref = js_create_module_var(ctx, TRUE);
+ var_ref = js_create_var_ref(ctx, TRUE);
if (!var_ref) {
JS_FreeValue(ctx, val);
goto fail;
@@ -30835,7 +31096,7 @@ static __exception int js_parse_export(JSParseState *s)
}
static int add_closure_var(JSContext *ctx, JSFunctionDef *s,
- BOOL is_local, BOOL is_arg,
+ JSClosureTypeEnum closure_type,
int var_idx, JSAtom var_name,
BOOL is_const, BOOL is_lexical,
JSVarKindEnum var_kind);
@@ -30857,9 +31118,10 @@ static int add_import(JSParseState *s, JSModuleDef *m,
}
}
- var_idx = add_closure_var(ctx, s->cur_func, is_star, FALSE,
+ var_idx = add_closure_var(ctx, s->cur_func,
+ is_star ? JS_CLOSURE_MODULE_DECL :
JS_CLOSURE_MODULE_IMPORT,
m->import_entries_count,
- local_name, TRUE, TRUE, FALSE);
+ local_name, TRUE, TRUE, JS_VAR_NORMAL);
if (var_idx < 0)
return -1;
if (js_resize_array(ctx, (void **)&m->import_entries,
@@ -31623,12 +31885,39 @@ static __maybe_unused void
js_dump_function_bytecode(JSContext *ctx, JSFunctionB
printf(" closure vars:\n");
for(i = 0; i < b->closure_var_count; i++) {
JSClosureVar *cv = &b->closure_var[i];
- printf("%5d: %s %s:%s%d %s\n", i,
- JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf),
cv->var_name),
- cv->is_local ? "local" : "parent",
- cv->is_arg ? "arg" : "loc", cv->var_idx,
+ printf("%5d: %s %s", i,
cv->is_const ? "const" :
- cv->is_lexical ? "let" : "var");
+ cv->is_lexical ? "let" : "var",
+ JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf),
cv->var_name));
+ switch(cv->closure_type) {
+ case JS_CLOSURE_LOCAL:
+ printf(" [loc%d]\n", cv->var_idx);
+ break;
+ case JS_CLOSURE_ARG:
+ printf(" [arg%d]\n", cv->var_idx);
+ break;
+ case JS_CLOSURE_REF:
+ printf(" [ref%d]\n", cv->var_idx);
+ break;
+ case JS_CLOSURE_GLOBAL_REF:
+ printf(" [global_ref%d]\n", cv->var_idx);
+ break;
+ case JS_CLOSURE_GLOBAL_DECL:
+ printf(" [global_decl]\n");
+ break;
+ case JS_CLOSURE_GLOBAL:
+ printf(" [global]\n");
+ break;
+ case JS_CLOSURE_MODULE_DECL:
+ printf(" [module_decl]\n");
+ break;
+ case JS_CLOSURE_MODULE_IMPORT:
+ printf(" [module_import]\n");
+ break;
+ default:
+ printf(" [?]\n");
+ break;
+ }
}
}
printf(" stack_size: %d\n", b->stack_size);
@@ -31649,7 +31938,7 @@ static __maybe_unused void
js_dump_function_bytecode(JSContext *ctx, JSFunctionB
#endif
static int add_closure_var(JSContext *ctx, JSFunctionDef *s,
- BOOL is_local, BOOL is_arg,
+ JSClosureTypeEnum closure_type,
int var_idx, JSAtom var_name,
BOOL is_const, BOOL is_lexical,
JSVarKindEnum var_kind)
@@ -31667,8 +31956,7 @@ static int add_closure_var(JSContext *ctx,
JSFunctionDef *s,
&s->closure_var_size, s->closure_var_count + 1))
return -1;
cv = &s->closure_var[s->closure_var_count++];
- cv->is_local = is_local;
- cv->is_arg = is_arg;
+ cv->closure_type = closure_type;
cv->is_const = is_const;
cv->is_lexical = is_lexical;
cv->var_kind = var_kind;
@@ -31689,46 +31977,34 @@ static int find_closure_var(JSContext *ctx,
JSFunctionDef *s,
return -1;
}
-/* 'fd' must be a parent of 's'. Create in 's' a closure referencing a
- local variable (is_local = TRUE) or a closure (is_local = FALSE) in
- 'fd' */
-static int get_closure_var2(JSContext *ctx, JSFunctionDef *s,
- JSFunctionDef *fd, BOOL is_local,
- BOOL is_arg, int var_idx, JSAtom var_name,
- BOOL is_const, BOOL is_lexical,
- JSVarKindEnum var_kind)
+/* 'fd' must be a parent of 's'. Create in 's' a closure referencing
+ another one in 'fd' */
+static int get_closure_var(JSContext *ctx, JSFunctionDef *s,
+ JSFunctionDef *fd, JSClosureTypeEnum closure_type,
+ int var_idx, JSAtom var_name,
+ BOOL is_const, BOOL is_lexical,
+ JSVarKindEnum var_kind)
{
int i;
if (fd != s->parent) {
- var_idx = get_closure_var2(ctx, s->parent, fd, is_local,
- is_arg, var_idx, var_name,
- is_const, is_lexical, var_kind);
+ var_idx = get_closure_var(ctx, s->parent, fd, closure_type,
+ var_idx, var_name,
+ is_const, is_lexical, var_kind);
if (var_idx < 0)
return -1;
- is_local = FALSE;
+ if (closure_type != JS_CLOSURE_GLOBAL_REF)
+ closure_type = JS_CLOSURE_REF;
}
for(i = 0; i < s->closure_var_count; i++) {
JSClosureVar *cv = &s->closure_var[i];
- if (cv->var_idx == var_idx && cv->is_arg == is_arg &&
- cv->is_local == is_local)
+ if (cv->var_idx == var_idx && cv->closure_type == closure_type)
return i;
}
- return add_closure_var(ctx, s, is_local, is_arg, var_idx, var_name,
+ return add_closure_var(ctx, s, closure_type, var_idx, var_name,
is_const, is_lexical, var_kind);
}
-static int get_closure_var(JSContext *ctx, JSFunctionDef *s,
- JSFunctionDef *fd, BOOL is_arg,
- int var_idx, JSAtom var_name,
- BOOL is_const, BOOL is_lexical,
- JSVarKindEnum var_kind)
-{
- return get_closure_var2(ctx, s, fd, TRUE, is_arg,
- var_idx, var_name, is_const, is_lexical,
- var_kind);
-}
-
static int get_with_scope_opcode(int op)
{
if (op == OP_scope_get_var_undef)
@@ -31801,41 +32077,6 @@ static int optimize_scope_make_ref(JSContext *ctx,
JSFunctionDef *s,
return pos_next;
}
-static int optimize_scope_make_global_ref(JSContext *ctx, JSFunctionDef *s,
- DynBuf *bc, uint8_t *bc_buf,
- LabelSlot *ls, int pos_next,
- JSAtom var_name)
-{
- int label_pos, end_pos, pos, op;
-
- /* replace the reference get/put with normal variable
- accesses */
- /* XXX: need 2 extra OP_true if destructuring an array */
- if (bc_buf[pos_next] == OP_get_ref_value) {
- dbuf_putc(bc, OP_get_var);
- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
- pos_next++;
- }
- /* remove the OP_label to make room for replacement */
- /* label should have a refcount of 0 anyway */
- /* XXX: should have emitted several OP_nop to avoid this kludge */
- label_pos = ls->pos;
- pos = label_pos - 5;
- assert(bc_buf[pos] == OP_label);
- end_pos = label_pos + 2;
- op = bc_buf[label_pos];
- if (op == OP_insert3)
- bc_buf[pos++] = OP_dup;
- bc_buf[pos] = OP_put_var;
- /* XXX: need 2 extra OP_drop if destructuring an array */
- put_u32(bc_buf + pos + 1, JS_DupAtom(ctx, var_name));
- pos += 5;
- /* pad with OP_nop */
- while (pos < end_pos)
- bc_buf[pos++] = OP_nop;
- return pos_next;
-}
-
static int add_var_this(JSContext *ctx, JSFunctionDef *fd)
{
int idx;
@@ -31909,7 +32150,7 @@ static void var_object_test(JSContext *ctx,
JSFunctionDef *s,
s->jump_size++;
}
-/* return the position of the next opcode */
+/* return the position of the next opcode or -1 if error */
static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s,
JSAtom var_name, int scope_level, int op,
DynBuf *bc, uint8_t *bc_buf,
@@ -32028,14 +32269,22 @@ static int resolve_scope_var(JSContext *ctx,
JSFunctionDef *s,
}
}
break;
+ case OP_scope_put_var:
+ if (!(var_idx & ARGUMENT_VAR_OFFSET) &&
+ s->vars[var_idx].var_kind == JS_VAR_FUNCTION_NAME) {
+ /* in non strict mode, modifying the function name is ignored
*/
+ dbuf_putc(bc, OP_drop);
+ goto done;
+ }
+ goto local_scope_var;
case OP_scope_get_ref:
dbuf_putc(bc, OP_undefined);
- /* fall thru */
+ goto local_scope_var;
case OP_scope_get_var_checkthis:
case OP_scope_get_var_undef:
case OP_scope_get_var:
- case OP_scope_put_var:
case OP_scope_put_var_init:
+ local_scope_var:
is_put = (op == OP_scope_put_var || op == OP_scope_put_var_init);
if (var_idx & ARGUMENT_VAR_OFFSET) {
dbuf_putc(bc, OP_get_arg + is_put);
@@ -32108,7 +32357,7 @@ static int resolve_scope_var(JSContext *ctx,
JSFunctionDef *s,
break;
} else if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) {
vd->is_captured = 1;
- idx = get_closure_var(ctx, s, fd, FALSE, idx, vd->var_name,
FALSE, FALSE, JS_VAR_NORMAL);
+ idx = get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL, idx,
vd->var_name, FALSE, FALSE, JS_VAR_NORMAL);
if (idx >= 0) {
dbuf_putc(bc, OP_get_var_ref);
dbuf_put_u16(bc, idx);
@@ -32145,7 +32394,7 @@ static int resolve_scope_var(JSContext *ctx,
JSFunctionDef *s,
if (!is_arg_scope && fd->var_object_idx >= 0 && !is_pseudo_var) {
vd = &fd->vars[fd->var_object_idx];
vd->is_captured = 1;
- idx = get_closure_var(ctx, s, fd, FALSE,
+ idx = get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL,
fd->var_object_idx, vd->var_name,
FALSE, FALSE, JS_VAR_NORMAL);
dbuf_putc(bc, OP_get_var_ref);
@@ -32157,7 +32406,7 @@ static int resolve_scope_var(JSContext *ctx,
JSFunctionDef *s,
if (fd->arg_var_object_idx >= 0 && !is_pseudo_var) {
vd = &fd->vars[fd->arg_var_object_idx];
vd->is_captured = 1;
- idx = get_closure_var(ctx, s, fd, FALSE,
+ idx = get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL,
fd->arg_var_object_idx, vd->var_name,
FALSE, FALSE, JS_VAR_NORMAL);
dbuf_putc(bc, OP_get_var_ref);
@@ -32179,25 +32428,37 @@ static int resolve_scope_var(JSContext *ctx,
JSFunctionDef *s,
JSClosureVar *cv = &fd->closure_var[idx1];
if (var_name == cv->var_name) {
if (fd != s) {
- idx = get_closure_var2(ctx, s, fd,
- FALSE,
- cv->is_arg, idx1,
- cv->var_name, cv->is_const,
- cv->is_lexical, cv->var_kind);
+ JSClosureTypeEnum closure_type;
+ if (cv->closure_type == JS_CLOSURE_GLOBAL ||
+ cv->closure_type == JS_CLOSURE_GLOBAL_DECL ||
+ cv->closure_type == JS_CLOSURE_GLOBAL_REF)
+ closure_type = JS_CLOSURE_GLOBAL_REF;
+ else
+ closure_type = JS_CLOSURE_REF;
+ idx = get_closure_var(ctx, s, fd,
+ closure_type,
+ idx1,
+ cv->var_name, cv->is_const,
+ cv->is_lexical, cv->var_kind);
} else {
idx = idx1;
}
- goto has_idx;
+ if (cv->closure_type == JS_CLOSURE_GLOBAL ||
+ cv->closure_type == JS_CLOSURE_GLOBAL_DECL ||
+ cv->closure_type == JS_CLOSURE_GLOBAL_REF)
+ goto has_global_idx;
+ else
+ goto has_idx;
} else if ((cv->var_name == JS_ATOM__var_ ||
cv->var_name == JS_ATOM__arg_var_ ||
cv->var_name == JS_ATOM__with_) && !is_pseudo_var) {
int is_with = (cv->var_name == JS_ATOM__with_);
if (fd != s) {
- idx = get_closure_var2(ctx, s, fd,
- FALSE,
- cv->is_arg, idx1,
- cv->var_name, FALSE, FALSE,
- JS_VAR_NORMAL);
+ idx = get_closure_var(ctx, s, fd,
+ JS_CLOSURE_REF,
+ idx1,
+ cv->var_name, FALSE, FALSE,
+ JS_VAR_NORMAL);
} else {
idx = idx1;
}
@@ -32206,19 +32467,67 @@ static int resolve_scope_var(JSContext *ctx,
JSFunctionDef *s,
var_object_test(ctx, s, var_name, op, bc, &label_done,
is_with);
}
}
- }
- if (var_idx >= 0) {
+ /* not found: add a closure for a global variable access */
+ idx1 = add_closure_var(ctx, fd, JS_CLOSURE_GLOBAL, 0, var_name,
+ FALSE, FALSE, JS_VAR_NORMAL);
+ if (idx1 < 0)
+ return -1;
+ if (fd != s) {
+ idx = get_closure_var(ctx, s, fd,
+ JS_CLOSURE_GLOBAL_REF,
+ idx1,
+ var_name, FALSE, FALSE,
+ JS_VAR_NORMAL);
+ } else {
+ idx = idx1;
+ }
+ has_global_idx:
+ /* global variable access */
+ switch (op) {
+ case OP_scope_make_ref:
+ if (label_done == -1 && can_opt_put_global_ref_value(bc_buf,
ls->pos)) {
+ pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls,
+ pos_next,
+ OP_get_var, idx);
+ } else {
+ dbuf_putc(bc, OP_make_var_ref);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ }
+ break;
+ case OP_scope_get_ref:
+ /* XXX: should create a dummy object with a named slot that is
+ a reference to the global variable */
+ dbuf_putc(bc, OP_undefined);
+ dbuf_putc(bc, OP_get_var);
+ dbuf_put_u16(bc, idx);
+ break;
+ case OP_scope_get_var_undef:
+ case OP_scope_get_var:
+ case OP_scope_put_var:
+ dbuf_putc(bc, OP_get_var_undef + (op - OP_scope_get_var_undef));
+ dbuf_put_u16(bc, idx);
+ break;
+ case OP_scope_put_var_init:
+ dbuf_putc(bc, OP_put_var_init);
+ dbuf_put_u16(bc, idx);
+ break;
+ case OP_scope_delete_var:
+ dbuf_putc(bc, OP_delete_var);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ break;
+ }
+ } else {
/* find the corresponding closure variable */
if (var_idx & ARGUMENT_VAR_OFFSET) {
fd->args[var_idx - ARGUMENT_VAR_OFFSET].is_captured = 1;
idx = get_closure_var(ctx, s, fd,
- TRUE, var_idx - ARGUMENT_VAR_OFFSET,
+ JS_CLOSURE_ARG, var_idx -
ARGUMENT_VAR_OFFSET,
var_name, FALSE, FALSE, JS_VAR_NORMAL);
} else {
fd->vars[var_idx].is_captured = 1;
idx = get_closure_var(ctx, s, fd,
- FALSE, var_idx,
+ JS_CLOSURE_LOCAL, var_idx,
var_name,
fd->vars[var_idx].is_const,
fd->vars[var_idx].is_lexical,
@@ -32263,15 +32572,22 @@ static int resolve_scope_var(JSContext *ctx,
JSFunctionDef *s,
dbuf_put_u16(bc, idx);
}
break;
+ case OP_scope_put_var:
+ if (s->closure_var[idx].var_kind == JS_VAR_FUNCTION_NAME) {
+ /* in non strict mode, modifying the function name is
ignored */
+ dbuf_putc(bc, OP_drop);
+ goto done;
+ }
+ goto closure_scope_var;
case OP_scope_get_ref:
/* XXX: should create a dummy object with a named slot that is
a reference to the closure variable */
dbuf_putc(bc, OP_undefined);
- /* fall thru */
+ goto closure_scope_var;
case OP_scope_get_var_undef:
case OP_scope_get_var:
- case OP_scope_put_var:
case OP_scope_put_var_init:
+ closure_scope_var:
is_put = (op == OP_scope_put_var ||
op == OP_scope_put_var_init);
if (is_put) {
@@ -32305,40 +32621,6 @@ static int resolve_scope_var(JSContext *ctx,
JSFunctionDef *s,
}
}
- /* global variable access */
-
- switch (op) {
- case OP_scope_make_ref:
- if (label_done == -1 && can_opt_put_global_ref_value(bc_buf, ls->pos))
{
- pos_next = optimize_scope_make_global_ref(ctx, s, bc, bc_buf, ls,
- pos_next, var_name);
- } else {
- dbuf_putc(bc, OP_make_var_ref);
- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
- }
- break;
- case OP_scope_get_ref:
- /* XXX: should create a dummy object with a named slot that is
- a reference to the global variable */
- dbuf_putc(bc, OP_undefined);
- dbuf_putc(bc, OP_get_var);
- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
- break;
- case OP_scope_get_var_undef:
- case OP_scope_get_var:
- case OP_scope_put_var:
- dbuf_putc(bc, OP_get_var_undef + (op - OP_scope_get_var_undef));
- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
- break;
- case OP_scope_put_var_init:
- dbuf_putc(bc, OP_put_var_init);
- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
- break;
- case OP_scope_delete_var:
- dbuf_putc(bc, OP_delete_var);
- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
- break;
- }
done:
if (label_done >= 0) {
dbuf_putc(bc, OP_label);
@@ -32390,7 +32672,7 @@ static int resolve_scope_private_field1(JSContext *ctx,
if (idx >= 0) {
var_kind = fd->vars[idx].var_kind;
if (is_ref) {
- idx = get_closure_var(ctx, s, fd, FALSE, idx, var_name,
+ idx = get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL, idx,
var_name,
TRUE, TRUE, JS_VAR_NORMAL);
if (idx < 0)
return -1;
@@ -32407,12 +32689,12 @@ static int resolve_scope_private_field1(JSContext
*ctx,
var_kind = cv->var_kind;
is_ref = TRUE;
if (fd != s) {
- idx = get_closure_var2(ctx, s, fd,
- FALSE,
- cv->is_arg, idx,
- cv->var_name, cv->is_const,
- cv->is_lexical,
- cv->var_kind);
+ idx = get_closure_var(ctx, s, fd,
+ JS_CLOSURE_REF,
+ idx,
+ cv->var_name, cv->is_const,
+ cv->is_lexical,
+ cv->var_kind);
if (idx < 0)
return -1;
}
@@ -32641,7 +32923,7 @@ static void add_eval_variables(JSContext *ctx,
JSFunctionDef *s)
while (scope_idx >= 0) {
vd = &fd->vars[scope_idx];
vd->is_captured = 1;
- get_closure_var(ctx, s, fd, FALSE, scope_idx,
+ get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL, scope_idx,
vd->var_name, vd->is_const, vd->is_lexical,
vd->var_kind);
scope_idx = vd->scope_next;
}
@@ -32653,7 +32935,7 @@ static void add_eval_variables(JSContext *ctx,
JSFunctionDef *s)
vd = &fd->args[i];
if (vd->var_name != JS_ATOM_NULL) {
get_closure_var(ctx, s, fd,
- TRUE, i, vd->var_name, FALSE,
+ JS_CLOSURE_ARG, i, vd->var_name, FALSE,
vd->is_lexical, JS_VAR_NORMAL);
}
}
@@ -32664,7 +32946,7 @@ static void add_eval_variables(JSContext *ctx,
JSFunctionDef *s)
vd->var_name != JS_ATOM__ret_ &&
vd->var_name != JS_ATOM_NULL) {
get_closure_var(ctx, s, fd,
- FALSE, i, vd->var_name, FALSE,
+ JS_CLOSURE_LOCAL, i, vd->var_name, FALSE,
vd->is_lexical, JS_VAR_NORMAL);
}
}
@@ -32674,7 +32956,7 @@ static void add_eval_variables(JSContext *ctx,
JSFunctionDef *s)
/* do not close top level last result */
if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) {
get_closure_var(ctx, s, fd,
- FALSE, i, vd->var_name, FALSE,
+ JS_CLOSURE_LOCAL, i, vd->var_name, FALSE,
vd->is_lexical, JS_VAR_NORMAL);
}
}
@@ -32682,13 +32964,19 @@ static void add_eval_variables(JSContext *ctx,
JSFunctionDef *s)
if (fd->is_eval) {
int idx;
/* add direct eval variables (we are necessarily at the
- top level) */
+ top level). */
for (idx = 0; idx < fd->closure_var_count; idx++) {
JSClosureVar *cv = &fd->closure_var[idx];
- get_closure_var2(ctx, s, fd,
- FALSE, cv->is_arg,
- idx, cv->var_name, cv->is_const,
- cv->is_lexical, cv->var_kind);
+ /* Global variables are removed but module
+ definitions are kept. */
+ if (cv->closure_type != JS_CLOSURE_GLOBAL_REF &&
+ cv->closure_type != JS_CLOSURE_GLOBAL_DECL &&
+ cv->closure_type != JS_CLOSURE_GLOBAL) {
+ get_closure_var(ctx, s, fd,
+ JS_CLOSURE_REF,
+ idx, cv->var_name, cv->is_const,
+ cv->is_lexical, cv->var_kind);
+ }
}
}
}
@@ -32697,8 +32985,7 @@ static void add_eval_variables(JSContext *ctx,
JSFunctionDef *s)
static void set_closure_from_var(JSContext *ctx, JSClosureVar *cv,
JSVarDef *vd, int var_idx)
{
- cv->is_local = TRUE;
- cv->is_arg = FALSE;
+ cv->closure_type = JS_CLOSURE_LOCAL;
cv->is_const = vd->is_const;
cv->is_lexical = vd->is_lexical;
cv->var_kind = vd->var_kind;
@@ -32739,8 +33026,7 @@ static __exception int add_closure_variables(JSContext
*ctx, JSFunctionDef *s,
for(i = 0; i < b->arg_count; i++) {
JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
vd = &b->vardefs[i];
- cv->is_local = TRUE;
- cv->is_arg = TRUE;
+ cv->closure_type = JS_CLOSURE_ARG;
cv->is_const = FALSE;
cv->is_lexical = FALSE;
cv->var_kind = JS_VAR_NORMAL;
@@ -32767,9 +33053,24 @@ static __exception int add_closure_variables(JSContext
*ctx, JSFunctionDef *s,
}
for(i = 0; i < b->closure_var_count; i++) {
JSClosureVar *cv0 = &b->closure_var[i];
- JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
- cv->is_local = FALSE;
- cv->is_arg = cv0->is_arg;
+ JSClosureVar *cv;
+
+ switch(cv0->closure_type) {
+ case JS_CLOSURE_LOCAL:
+ case JS_CLOSURE_ARG:
+ case JS_CLOSURE_REF:
+ case JS_CLOSURE_MODULE_DECL:
+ case JS_CLOSURE_MODULE_IMPORT:
+ break;
+ case JS_CLOSURE_GLOBAL_REF:
+ case JS_CLOSURE_GLOBAL_DECL:
+ case JS_CLOSURE_GLOBAL:
+ continue; /* not necessary to add global variables */
+ default:
+ abort();
+ }
+ cv = &s->closure_var[s->closure_var_count++];
+ cv->closure_type = JS_CLOSURE_REF;
cv->is_const = cv0->is_const;
cv->is_lexical = cv0->is_lexical;
cv->var_kind = cv0->var_kind;
@@ -32962,9 +33263,10 @@ static void instantiate_hoisted_definitions(JSContext
*ctx, JSFunctionDef *s, Dy
/* add the global variables (only happens if s->is_global_var is
true) */
+ /* XXX: inefficient, add a closure index in JSGlobalVar */
for(i = 0; i < s->global_var_count; i++) {
JSGlobalVar *hf = &s->global_vars[i];
- int has_closure = 0;
+ BOOL has_var_obj = FALSE;
BOOL force_init = hf->force_init;
/* we are in an eval, so the closure contains all the
enclosing variables */
@@ -32973,46 +33275,20 @@ static void instantiate_hoisted_definitions(JSContext
*ctx, JSFunctionDef *s, Dy
for(idx = 0; idx < s->closure_var_count; idx++) {
JSClosureVar *cv = &s->closure_var[idx];
if (cv->var_name == hf->var_name) {
- has_closure = 2;
force_init = FALSE;
- break;
+ goto closure_found;
}
if (cv->var_name == JS_ATOM__var_ ||
cv->var_name == JS_ATOM__arg_var_) {
dbuf_putc(bc, OP_get_var_ref);
dbuf_put_u16(bc, idx);
- has_closure = 1;
+ has_var_obj = TRUE;
force_init = TRUE;
- break;
- }
- }
- if (!has_closure) {
- int flags;
-
- flags = 0;
- if (s->eval_type != JS_EVAL_TYPE_GLOBAL)
- flags |= JS_PROP_CONFIGURABLE;
- if (hf->cpool_idx >= 0 && !hf->is_lexical) {
- /* global function definitions need a specific handling */
- dbuf_putc(bc, OP_fclosure);
- dbuf_put_u32(bc, hf->cpool_idx);
-
- dbuf_putc(bc, OP_define_func);
- dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
- dbuf_putc(bc, flags);
-
- goto done_global_var;
- } else {
- if (hf->is_lexical) {
- flags |= DEFINE_GLOBAL_LEX_VAR;
- if (!hf->is_const)
- flags |= JS_PROP_WRITABLE;
- }
- dbuf_putc(bc, OP_define_var);
- dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
- dbuf_putc(bc, flags);
+ goto closure_found;
}
}
+ abort();
+ closure_found:
if (hf->cpool_idx >= 0 || force_init) {
if (hf->cpool_idx >= 0) {
dbuf_putc(bc, OP_fclosure);
@@ -33025,20 +33301,15 @@ static void instantiate_hoisted_definitions(JSContext
*ctx, JSFunctionDef *s, Dy
} else {
dbuf_putc(bc, OP_undefined);
}
- if (has_closure == 2) {
+ if (!has_var_obj) {
dbuf_putc(bc, OP_put_var_ref);
dbuf_put_u16(bc, idx);
- } else if (has_closure == 1) {
+ } else {
dbuf_putc(bc, OP_define_field);
dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
dbuf_putc(bc, OP_drop);
- } else {
- /* XXX: Check if variable is writable and enumerable */
- dbuf_putc(bc, OP_put_var);
- dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
}
}
- done_global_var:
JS_FreeAtom(ctx, hf->var_name);
}
@@ -33133,7 +33404,7 @@ static int get_label_pos(JSFunctionDef *s, int label)
variables when necessary */
static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s)
{
- int pos, pos_next, bc_len, op, len, i, idx, line_num;
+ int pos, pos_next, bc_len, op, len, line_num, i, idx;
uint8_t *bc_buf;
JSAtom var_name;
DynBuf bc_out;
@@ -33146,13 +33417,19 @@ static __exception int resolve_variables(JSContext
*ctx, JSFunctionDef *s)
/* first pass for runtime checks (must be done before the
variables are created) */
+ /* XXX: inefficient */
for(i = 0; i < s->global_var_count; i++) {
JSGlobalVar *hf = &s->global_vars[i];
- int flags;
/* check if global variable (XXX: simplify) */
for(idx = 0; idx < s->closure_var_count; idx++) {
JSClosureVar *cv = &s->closure_var[idx];
+ if (cv->closure_type == JS_CLOSURE_GLOBAL_REF ||
+ cv->closure_type == JS_CLOSURE_GLOBAL_DECL ||
+ cv->closure_type == JS_CLOSURE_GLOBAL ||
+ cv->closure_type == JS_CLOSURE_MODULE_DECL ||
+ cv->closure_type == JS_CLOSURE_MODULE_IMPORT)
+ goto next; /* don't look at global variables (they are at the
end) */
if (cv->var_name == hf->var_name) {
if (s->eval_type == JS_EVAL_TYPE_DIRECT &&
cv->is_lexical) {
@@ -33171,15 +33448,6 @@ static __exception int resolve_variables(JSContext
*ctx, JSFunctionDef *s)
cv->var_name == JS_ATOM__arg_var_)
goto next;
}
-
- dbuf_putc(&bc_out, OP_check_define_var);
- dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name));
- flags = 0;
- if (hf->is_lexical)
- flags |= DEFINE_GLOBAL_LEX_VAR;
- if (hf->cpool_idx >= 0)
- flags |= DEFINE_GLOBAL_FUNC_VAR;
- dbuf_putc(&bc_out, flags);
next: ;
}
@@ -34895,35 +35163,68 @@ static __exception int compute_stack_size(JSContext
*ctx,
return -1;
}
-static int add_module_variables(JSContext *ctx, JSFunctionDef *fd)
+static int add_global_variables(JSContext *ctx, JSFunctionDef *fd)
{
int i, idx;
JSModuleDef *m = fd->module;
JSExportEntry *me;
JSGlobalVar *hf;
+ BOOL need_global_closures;
+
+ /* Script: add the defined global variables. In the non strict
+ direct eval not in global scope, the global variables are
+ created in the enclosing scope so they are not created as
+ variable references.
- /* The imported global variables were added as closure variables
- in js_parse_import(). We add here the module global
- variables. */
-
- for(i = 0; i < fd->global_var_count; i++) {
- hf = &fd->global_vars[i];
- if (add_closure_var(ctx, fd, TRUE, FALSE, i, hf->var_name,
hf->is_const,
- hf->is_lexical, FALSE) < 0)
- return -1;
+ In modules, the imported global variables were added as closure
+ global variables in js_parse_import().
+ */
+ need_global_closures = TRUE;
+ if (fd->eval_type == JS_EVAL_TYPE_DIRECT && !(fd->js_mode &
JS_MODE_STRICT)) {
+ /* XXX: add a flag ? */
+ for(idx = 0; idx < fd->closure_var_count; idx++) {
+ JSClosureVar *cv = &fd->closure_var[idx];
+ if (cv->var_name == JS_ATOM__var_ ||
+ cv->var_name == JS_ATOM__arg_var_) {
+ need_global_closures = FALSE;
+ break;
+ }
+ }
}
- /* resolve the variable names of the local exports */
- for(i = 0; i < m->export_entries_count; i++) {
- me = &m->export_entries[i];
- if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
- idx = find_closure_var(ctx, fd, me->local_name);
- if (idx < 0) {
- JS_ThrowSyntaxErrorAtom(ctx, "exported variable '%s' does not
exist",
- me->local_name);
+ if (need_global_closures) {
+ JSClosureTypeEnum closure_type;
+ if (fd->module)
+ closure_type = JS_CLOSURE_MODULE_DECL;
+ else
+ closure_type = JS_CLOSURE_GLOBAL_DECL;
+ for(i = 0; i < fd->global_var_count; i++) {
+ JSVarKindEnum var_kind;
+ hf = &fd->global_vars[i];
+ if (hf->cpool_idx >= 0 && !hf->is_lexical) {
+ var_kind = JS_VAR_GLOBAL_FUNCTION_DECL;
+ } else {
+ var_kind = JS_VAR_NORMAL;
+ }
+ if (add_closure_var(ctx, fd, closure_type, i, hf->var_name,
hf->is_const,
+ hf->is_lexical, var_kind) < 0)
return -1;
+ }
+ }
+
+ if (fd->module) {
+ /* resolve the variable names of the local exports */
+ for(i = 0; i < m->export_entries_count; i++) {
+ me = &m->export_entries[i];
+ if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
+ idx = find_closure_var(ctx, fd, me->local_name);
+ if (idx < 0) {
+ JS_ThrowSyntaxErrorAtom(ctx, "exported variable '%s' does
not exist",
+ me->local_name);
+ return -1;
+ }
+ me->u.local.var_idx = idx;
}
- me->u.local.var_idx = idx;
}
}
return 0;
@@ -34975,10 +35276,10 @@ static JSValue js_create_function(JSContext *ctx,
JSFunctionDef *fd)
add_eval_variables(ctx, fd);
/* add the module global variables in the closure */
- if (fd->module) {
- if (add_module_variables(ctx, fd))
+ if (fd->is_eval) {
+ if (add_global_variables(ctx, fd))
goto fail;
- }
+ }
/* first create all the child functions */
list_for_each_safe(el, el1, &fd->child_list) {
@@ -36031,7 +36332,9 @@ static JSValue JS_EvalFunctionInternal(JSContext *ctx,
JSValue fun_obj,
tag = JS_VALUE_GET_TAG(fun_obj);
if (tag == JS_TAG_FUNCTION_BYTECODE) {
- fun_obj = js_closure(ctx, fun_obj, var_refs, sf);
+ fun_obj = js_closure(ctx, fun_obj, var_refs, sf, TRUE);
+ if (JS_IsException(fun_obj))
+ return JS_EXCEPTION;
ret_val = JS_CallFree(ctx, fun_obj, this_obj, 0, NULL);
} else if (tag == JS_TAG_MODULE) {
JSModuleDef *m;
@@ -36755,13 +37058,12 @@ static int JS_WriteFunctionTag(BCWriterState *s,
JSValueConst obj)
bc_put_atom(s, cv->var_name);
bc_put_leb128(s, cv->var_idx);
flags = idx = 0;
- bc_set_flags(&flags, &idx, cv->is_local, 1);
- bc_set_flags(&flags, &idx, cv->is_arg, 1);
+ bc_set_flags(&flags, &idx, cv->closure_type, 3);
bc_set_flags(&flags, &idx, cv->is_const, 1);
bc_set_flags(&flags, &idx, cv->is_lexical, 1);
bc_set_flags(&flags, &idx, cv->var_kind, 4);
- assert(idx <= 8);
- bc_put_u8(s, flags);
+ assert(idx <= 16);
+ bc_put_u16(s, flags);
}
if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len))
@@ -37709,14 +38011,13 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s)
if (bc_get_leb128_int(s, &var_idx))
goto fail;
cv->var_idx = var_idx;
- if (bc_get_u8(s, &v8))
+ if (bc_get_u16(s, &v16))
goto fail;
idx = 0;
- cv->is_local = bc_get_flags(v8, &idx, 1);
- cv->is_arg = bc_get_flags(v8, &idx, 1);
- cv->is_const = bc_get_flags(v8, &idx, 1);
- cv->is_lexical = bc_get_flags(v8, &idx, 1);
- cv->var_kind = bc_get_flags(v8, &idx, 4);
+ cv->closure_type = bc_get_flags(v16, &idx, 3);
+ cv->is_const = bc_get_flags(v16, &idx, 1);
+ cv->is_lexical = bc_get_flags(v16, &idx, 1);
+ cv->var_kind = bc_get_flags(v16, &idx, 4);
#ifdef DUMP_READ_OBJECT
bc_read_trace(s, "name: "); print_atom(s->ctx, cv->var_name);
printf("\n");
#endif
@@ -39676,8 +39977,9 @@ static JSValue JS_SpeciesConstructor(JSContext *ctx,
JSValueConst obj,
if (JS_IsUndefined(species) || JS_IsNull(species))
return JS_DupValue(ctx, defaultConstructor);
if (!JS_IsConstructor(ctx, species)) {
+ JS_ThrowTypeErrorNotAConstructor(ctx, species);
JS_FreeValue(ctx, species);
- return JS_ThrowTypeError(ctx, "not a constructor");
+ return JS_EXCEPTION;
}
return species;
}
@@ -48520,7 +48822,7 @@ static JSValue js_reflect_construct(JSContext *ctx,
JSValueConst this_val,
if (argc > 2) {
new_target = argv[2];
if (!JS_IsConstructor(ctx, new_target))
- return JS_ThrowTypeError(ctx, "not a constructor");
+ return JS_ThrowTypeErrorNotAConstructor(ctx, new_target);
} else {
new_target = func;
}
@@ -49439,7 +49741,7 @@ static JSValue js_proxy_call_constructor(JSContext
*ctx, JSValueConst func_obj,
if (!s)
return JS_EXCEPTION;
if (!JS_IsConstructor(ctx, s->target))
- return JS_ThrowTypeError(ctx, "not a constructor");
+ return JS_ThrowTypeErrorNotAConstructor(ctx, s->target);
if (JS_IsUndefined(method))
return JS_CallConstructor2(ctx, s->target, new_target, argc, argv);
arg_array = js_create_array(ctx, argc, argv);
@@ -49665,7 +49967,7 @@ static JSValue js_symbol_constructor(JSContext *ctx,
JSValueConst new_target,
JSString *p;
if (!JS_IsUndefined(new_target))
- return JS_ThrowTypeError(ctx, "not a constructor");
+ return JS_ThrowTypeErrorNotAConstructor(ctx, new_target);
if (argc == 0 || JS_IsUndefined(argv[0])) {
p = NULL;
} else {
@@ -54327,7 +54629,7 @@ static JSValue js_bigint_constructor(JSContext *ctx,
int argc, JSValueConst *argv)
{
if (!JS_IsUndefined(new_target))
- return JS_ThrowTypeError(ctx, "not a constructor");
+ return JS_ThrowTypeErrorNotAConstructor(ctx, new_target);
return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0]));
}
@@ -54493,9 +54795,15 @@ static int JS_AddIntrinsicBasicObjects(JSContext *ctx)
ctx->class_proto[JS_CLASS_BYTECODE_FUNCTION] = JS_DupValue(ctx,
ctx->function_proto);
ctx->global_obj = JS_NewObjectProtoClassAlloc(ctx,
ctx->class_proto[JS_CLASS_OBJECT],
- JS_CLASS_OBJECT, 64);
+ JS_CLASS_GLOBAL_OBJECT, 64);
if (JS_IsException(ctx->global_obj))
return -1;
+ {
+ JSObject *p;
+ obj = JS_NewObjectProtoClassAlloc(ctx, JS_NULL, JS_CLASS_OBJECT, 4);
+ p = JS_VALUE_GET_OBJ(ctx->global_obj);
+ p->u.global_object.uninitialized_vars = obj;
+ }
ctx->global_var_obj = JS_NewObjectProtoClassAlloc(ctx, JS_NULL,
JS_CLASS_OBJECT, 16);
if (JS_IsException(ctx->global_var_obj))
@@ -55067,10 +55375,52 @@ static JSValue
js_array_buffer_get_resizable(JSContext *ctx,
return JS_NewBool(ctx, array_buffer_is_resizable(abuf));
}
+static void js_array_buffer_update_typed_arrays(JSArrayBuffer *abuf)
+{
+ uint32_t size_log2, size_elem;
+ struct list_head *el;
+ JSTypedArray *ta;
+ JSObject *p;
+ uint8_t *data;
+ int64_t len;
+
+ len = abuf->byte_length;
+ data = abuf->data;
+ // update lengths of all typed arrays backed by this array buffer
+ list_for_each(el, &abuf->array_list) {
+ ta = list_entry(el, JSTypedArray, link);
+ p = ta->obj;
+ if (p->class_id == JS_CLASS_DATAVIEW) {
+ if (ta->track_rab) {
+ if (ta->offset < len)
+ ta->length = len - ta->offset;
+ else
+ ta->length = 0;
+ }
+ } else {
+ p->u.array.count = 0;
+ p->u.array.u.ptr = NULL;
+ size_log2 = typed_array_size_log2(p->class_id);
+ size_elem = 1 << size_log2;
+ if (ta->track_rab) {
+ if (len >= (int64_t)ta->offset + size_elem) {
+ p->u.array.count = (len - ta->offset) >> size_log2;
+ p->u.array.u.ptr = &data[ta->offset];
+ }
+ } else {
+ if (len >= (int64_t)ta->offset + ta->length) {
+ p->u.array.count = ta->length >> size_log2;
+ p->u.array.u.ptr = &data[ta->offset];
+ }
+ }
+ }
+ }
+
+}
+
void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj)
{
JSArrayBuffer *abuf = JS_GetOpaque(obj, JS_CLASS_ARRAY_BUFFER);
- struct list_head *el;
if (!abuf || abuf->detached)
return;
@@ -55079,19 +55429,7 @@ void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst
obj)
abuf->data = NULL;
abuf->byte_length = 0;
abuf->detached = TRUE;
-
- list_for_each(el, &abuf->array_list) {
- JSTypedArray *ta;
- JSObject *p;
-
- ta = list_entry(el, JSTypedArray, link);
- p = ta->obj;
- /* Note: the typed array length and offset fields are not modified */
- if (p->class_id != JS_CLASS_DATAVIEW) {
- p->u.array.count = 0;
- p->u.array.u.ptr = NULL;
- }
- }
+ js_array_buffer_update_typed_arrays(abuf);
}
/* get an ArrayBuffer or SharedArrayBuffer */
@@ -55206,6 +55544,7 @@ static JSValue js_array_buffer_transfer(JSContext *ctx,
abuf->data = NULL;
abuf->byte_length = 0;
abuf->detached = TRUE;
+ js_array_buffer_update_typed_arrays(abuf);
return js_array_buffer_constructor3(ctx, JS_UNDEFINED, new_len,
pmax_len,
JS_CLASS_ARRAY_BUFFER,
bs, free_func,
@@ -55216,11 +55555,7 @@ static JSValue js_array_buffer_transfer(JSContext *ctx,
static JSValue js_array_buffer_resize(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int
class_id)
{
- uint32_t size_log2, size_elem;
- struct list_head *el;
JSArrayBuffer *abuf;
- JSTypedArray *ta;
- JSObject *p;
uint8_t *data;
int64_t len;
@@ -55263,29 +55598,7 @@ static JSValue js_array_buffer_resize(JSContext *ctx,
JSValueConst this_val,
abuf->byte_length = len;
abuf->data = data;
}
- data = abuf->data;
- // update lengths of all typed arrays backed by this array buffer
- list_for_each(el, &abuf->array_list) {
- ta = list_entry(el, JSTypedArray, link);
- p = ta->obj;
- if (p->class_id == JS_CLASS_DATAVIEW)
- continue;
- p->u.array.count = 0;
- p->u.array.u.ptr = NULL;
- size_log2 = typed_array_size_log2(p->class_id);
- size_elem = 1 << size_log2;
- if (ta->track_rab) {
- if (len >= (int64_t)ta->offset + size_elem) {
- p->u.array.count = (len - ta->offset) >> size_log2;
- p->u.array.u.ptr = &data[ta->offset];
- }
- } else {
- if (len >= (int64_t)ta->offset + ta->length) {
- p->u.array.count = ta->length >> size_log2;
- p->u.array.u.ptr = &data[ta->offset];
- }
- }
- }
+ js_array_buffer_update_typed_arrays(abuf);
return JS_UNDEFINED;
}
@@ -55344,7 +55657,7 @@ static JSValue js_array_buffer_slice(JSContext *ctx,
goto fail;
}
/* must test again because of side effects */
- if (abuf->detached) {
+ if (abuf->detached || abuf->byte_length < start + new_len) {
JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
goto fail;
}
diff --git a/src/couch_quickjs/quickjs/test262_errors.txt
b/src/couch_quickjs/quickjs/test262_errors.txt
index 247ae4470..cc49b2f32 100644
--- a/src/couch_quickjs/quickjs/test262_errors.txt
+++ b/src/couch_quickjs/quickjs/test262_errors.txt
@@ -5,6 +5,19 @@
test262/test/annexB/language/expressions/assignmenttargettype/callexpression-in-
test262/test/annexB/language/expressions/assignmenttargettype/callexpression-in-prefix-update.js:27:
SyntaxError: invalid increment/decrement operand
test262/test/annexB/language/expressions/assignmenttargettype/callexpression.js:33:
SyntaxError: invalid assignment left-hand side
test262/test/annexB/language/expressions/assignmenttargettype/cover-callexpression-and-asyncarrowhead.js:20:
SyntaxError: invalid assignment left-hand side
+test262/test/language/expressions/assignment/S11.13.1_A6_T1.js:23:
Test262Error: #1: innerX === undefined. Actual: 1
+test262/test/language/expressions/assignment/S11.13.1_A6_T2.js:23:
Test262Error: #1: innerX === 2. Actual: 1
+test262/test/language/expressions/compound-assignment/S11.13.2_A6.1_T1.js:24:
Test262Error: #1: innerX === 2. Actual: 12
+test262/test/language/expressions/compound-assignment/S11.13.2_A6.2_T1.js:24:
Test262Error: #1: innerX === 2. Actual: 5
+test262/test/language/expressions/compound-assignment/S11.13.2_A6.3_T1.js:24:
Test262Error: #1: innerX === 2. Actual: 3
+test262/test/language/expressions/compound-assignment/S11.13.2_A6.4_T1.js:24:
Test262Error: #1: innerX === 2. Actual: 4
+test262/test/language/expressions/compound-assignment/S11.13.2_A6.5_T1.js:24:
Test262Error: #1: innerX === 2. Actual: 4
+test262/test/language/expressions/compound-assignment/S11.13.2_A6.6_T1.js:24:
Test262Error: #1: innerX === 2. Actual: 8
+test262/test/language/expressions/compound-assignment/S11.13.2_A6.7_T1.js:24:
Test262Error: #1: innerX === 2. Actual: 4
+test262/test/language/expressions/compound-assignment/S11.13.2_A6.8_T1.js:24:
Test262Error: #1: innerX === 2. Actual: 4
+test262/test/language/expressions/compound-assignment/S11.13.2_A6.9_T1.js:24:
Test262Error: #1: innerX === 2. Actual: 1
+test262/test/language/expressions/compound-assignment/S11.13.2_A6.10_T1.js:24:
Test262Error: #1: innerX === 2. Actual: 5
+test262/test/language/expressions/compound-assignment/S11.13.2_A6.11_T1.js:24:
Test262Error: #1: innerX === 2. Actual: 5
test262/test/language/identifier-resolution/assign-to-global-undefined.js:20:
strict mode: expected error
test262/test/language/statements/expression/S12.4_A1.js:15: unexpected error
type: Test262: This statement should not be evaluated.
test262/test/language/statements/expression/S12.4_A1.js:15: strict mode:
unexpected error type: Test262: This statement should not be evaluated.
@@ -37,9 +50,6 @@ test262/test/staging/sm/class/boundFunctionSubclassing.js:9:
strict mode: Test26
test262/test/staging/sm/class/strictExecution.js:13: Test262Error: Expected a
TypeError to be thrown but no exception was thrown at all
test262/test/staging/sm/class/superPropOrdering.js:17: Test262Error: Expected
a TypeError to be thrown but no exception was thrown at all
test262/test/staging/sm/class/superPropOrdering.js:17: strict mode:
Test262Error: Expected a TypeError to be thrown but no exception was thrown at
all
-test262/test/staging/sm/expressions/short-circuit-compound-assignment-const.js:96:
TypeError: 'a' is read-only
-test262/test/staging/sm/expressions/short-circuit-compound-assignment-tdz.js:18:
Test262Error: Expected a ReferenceError but got a TypeError
-test262/test/staging/sm/expressions/short-circuit-compound-assignment-tdz.js:18:
strict mode: Test262Error: Expected a ReferenceError but got a TypeError
test262/test/staging/sm/generators/syntax.js:50: Test262Error: Expected a
SyntaxError to be thrown but no exception was thrown at all
test262/test/staging/sm/lexical-environment/block-scoped-functions-annex-b-arguments.js:13:
Test262Error: Expected SameValue(«"object"», «"function"») to be true
test262/test/staging/sm/lexical-environment/block-scoped-functions-annex-b-eval.js:11:
Test262Error: Expected SameValue(«"outer-gouter-geval-gtruefalseq"»,
«"outer-geval-gwith-gtruefalseq"») to be true