no more global obj; eval w/ env
This commit is contained in:
@@ -102,7 +102,6 @@ DEF( return, 1, 1, 0, none)
|
|||||||
DEF( return_undef, 1, 0, 0, none)
|
DEF( return_undef, 1, 0, 0, none)
|
||||||
DEF( throw, 1, 1, 0, none)
|
DEF( throw, 1, 1, 0, none)
|
||||||
DEF( throw_error, 6, 0, 0, key_u8)
|
DEF( throw_error, 6, 0, 0, key_u8)
|
||||||
DEF( eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */
|
|
||||||
DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a
|
DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a
|
||||||
bytecode string */
|
bytecode string */
|
||||||
|
|
||||||
|
|||||||
604
source/quickjs.c
604
source/quickjs.c
@@ -825,8 +825,8 @@ struct JSContext {
|
|||||||
JSValue throw_type_error;
|
JSValue throw_type_error;
|
||||||
JSValue eval_obj;
|
JSValue eval_obj;
|
||||||
|
|
||||||
JSValue global_obj; /* global object */
|
JSValue global_obj; /* global object (immutable intrinsics) */
|
||||||
JSValue global_var_obj; /* contains the global let/const definitions */
|
JSValue eval_env; /* environment record for eval (stone record) */
|
||||||
|
|
||||||
uint64_t random_state;
|
uint64_t random_state;
|
||||||
|
|
||||||
@@ -1030,7 +1030,9 @@ static int st_text_resize (JSContext *ctx) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Realloc with slack reporting (for bump allocator) */
|
/* Realloc with slack reporting (for bump allocator)
|
||||||
|
WARNING: This function is NOT GC-safe! The caller must protect the source
|
||||||
|
object with a GC ref before calling, and re-chase the pointer after. */
|
||||||
void *js_realloc (JSContext *ctx, void *ptr, size_t size) {
|
void *js_realloc (JSContext *ctx, void *ptr, size_t size) {
|
||||||
void *new_ptr;
|
void *new_ptr;
|
||||||
|
|
||||||
@@ -1044,13 +1046,9 @@ void *js_realloc (JSContext *ctx, void *ptr, size_t size) {
|
|||||||
return new_ptr;
|
return new_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Bump allocator: allocate new space and copy.
|
/* Bump allocator: just allocate new space.
|
||||||
For simplicity, we allocate new space and copy. */
|
Caller is responsible for protecting ptr and copying data. */
|
||||||
new_ptr = js_malloc (ctx, size);
|
new_ptr = js_malloc (ctx, size);
|
||||||
if (!new_ptr) return NULL;
|
|
||||||
|
|
||||||
/* Copy old data (caller ensures safety) */
|
|
||||||
memcpy (new_ptr, ptr, size);
|
|
||||||
return new_ptr;
|
return new_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1831,6 +1829,7 @@ static JSValue js_cell_text (JSContext *ctx, JSValue this_val, int argc, JSValue
|
|||||||
static JSValue js_cell_push (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
|
static JSValue js_cell_push (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
|
||||||
static JSValue js_cell_pop (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
|
static JSValue js_cell_pop (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
|
||||||
static JSValue js_cell_array_find (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
|
static JSValue js_cell_array_find (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
|
||||||
|
static JSValue js_cell_eval (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
|
||||||
static JSValue js_cell_stone (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
|
static JSValue js_cell_stone (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
|
||||||
static JSValue js_cell_length (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
|
static JSValue js_cell_length (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
|
||||||
static JSValue js_cell_reverse (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
|
static JSValue js_cell_reverse (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
|
||||||
@@ -2004,10 +2003,32 @@ static inline int pjs_resize_array (void **parray, int elem_size, int *psize, in
|
|||||||
static no_inline int js_realloc_array (JSContext *ctx, void **parray, int elem_size, int *psize, int req_size) {
|
static no_inline int js_realloc_array (JSContext *ctx, void **parray, int elem_size, int *psize, int req_size) {
|
||||||
int new_size;
|
int new_size;
|
||||||
void *new_array;
|
void *new_array;
|
||||||
|
void *old_array = *parray;
|
||||||
|
int old_size = *psize;
|
||||||
|
|
||||||
/* XXX: potential arithmetic overflow */
|
/* XXX: potential arithmetic overflow */
|
||||||
new_size = max_int (req_size, *psize * 3 / 2);
|
new_size = max_int (req_size, old_size * 3 / 2);
|
||||||
new_array = js_realloc (ctx, *parray, new_size * elem_size);
|
|
||||||
if (!new_array) return -1;
|
/* Protect source object with a GC ref before allocating (GC may move it) */
|
||||||
|
JSGCRef src_ref;
|
||||||
|
JS_PushGCRef (ctx, &src_ref);
|
||||||
|
if (old_array) {
|
||||||
|
src_ref.val = JS_MKPTR (old_array);
|
||||||
|
}
|
||||||
|
|
||||||
|
new_array = js_malloc (ctx, new_size * elem_size);
|
||||||
|
if (!new_array) {
|
||||||
|
JS_PopGCRef (ctx, &src_ref);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get possibly-moved source pointer after GC */
|
||||||
|
if (old_array) {
|
||||||
|
old_array = (void *)chase (src_ref.val);
|
||||||
|
memcpy (new_array, old_array, old_size * elem_size);
|
||||||
|
}
|
||||||
|
JS_PopGCRef (ctx, &src_ref);
|
||||||
|
|
||||||
*psize = new_size;
|
*psize = new_size;
|
||||||
*parray = new_array;
|
*parray = new_array;
|
||||||
return 0;
|
return 0;
|
||||||
@@ -2597,9 +2618,9 @@ static int ctx_gc (JSContext *ctx, int allow_grow) {
|
|||||||
printf(" after copy: global_obj = 0x%llx\n", (unsigned long long)ctx->global_obj); fflush(stdout);
|
printf(" after copy: global_obj = 0x%llx\n", (unsigned long long)ctx->global_obj); fflush(stdout);
|
||||||
#endif
|
#endif
|
||||||
#ifdef DUMP_GC_DETAIL
|
#ifdef DUMP_GC_DETAIL
|
||||||
printf(" roots: global_var_obj\n"); fflush(stdout);
|
printf(" roots: eval_env\n"); fflush(stdout);
|
||||||
#endif
|
#endif
|
||||||
ctx->global_var_obj = gc_copy_value (ctx, ctx->global_var_obj, from_base, from_end, to_base, &to_free, to_end);
|
ctx->eval_env = gc_copy_value (ctx, ctx->eval_env, from_base, from_end, to_base, &to_free, to_end);
|
||||||
#ifdef DUMP_GC_DETAIL
|
#ifdef DUMP_GC_DETAIL
|
||||||
printf(" roots: regexp_ctor\n"); fflush(stdout);
|
printf(" roots: regexp_ctor\n"); fflush(stdout);
|
||||||
#endif
|
#endif
|
||||||
@@ -3015,7 +3036,7 @@ static void JS_MarkContext (JSRuntime *rt, JSContext *ctx, JS_MarkFunc *mark_fun
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
JS_MarkValue (rt, ctx->global_obj, mark_func);
|
JS_MarkValue (rt, ctx->global_obj, mark_func);
|
||||||
JS_MarkValue (rt, ctx->global_var_obj, mark_func);
|
JS_MarkValue (rt, ctx->eval_env, mark_func);
|
||||||
|
|
||||||
JS_MarkValue (rt, ctx->throw_type_error, mark_func);
|
JS_MarkValue (rt, ctx->throw_type_error, mark_func);
|
||||||
JS_MarkValue (rt, ctx->eval_obj, mark_func);
|
JS_MarkValue (rt, ctx->eval_obj, mark_func);
|
||||||
@@ -3287,14 +3308,32 @@ static no_inline JSText *pretext_realloc (JSContext *ctx, JSText *s, int new_len
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
int old_cap = (int)objhdr_cap56 (s->hdr);
|
int old_cap = (int)objhdr_cap56 (s->hdr);
|
||||||
int new_cap = min_int (max_int (new_len, old_cap * 3 / 2), JS_STRING_LEN_MAX);
|
int old_len = (int)s->length;
|
||||||
|
/* Grow by 50%, ensuring we have at least new_len capacity */
|
||||||
|
int new_cap = max_int (new_len, old_cap * 3 / 2);
|
||||||
|
|
||||||
|
/* Protect source object with a GC ref before allocating (GC may move it) */
|
||||||
|
JSGCRef src_ref;
|
||||||
|
JS_PushGCRef (ctx, &src_ref);
|
||||||
|
src_ref.val = JS_MKPTR (s);
|
||||||
|
|
||||||
|
/* Allocate new string - this may trigger GC */
|
||||||
|
JSText *new_str = js_alloc_string (ctx, new_cap);
|
||||||
|
if (!new_str) {
|
||||||
|
JS_PopGCRef (ctx, &src_ref);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get possibly-moved source pointer after GC */
|
||||||
|
s = (JSText *)chase (src_ref.val);
|
||||||
|
JS_PopGCRef (ctx, &src_ref);
|
||||||
|
|
||||||
|
/* Copy data from old string to new */
|
||||||
|
new_str->length = old_len;
|
||||||
|
for (int i = 0; i < old_len; i++) {
|
||||||
|
string_put (new_str, i, string_get (s, i));
|
||||||
|
}
|
||||||
|
|
||||||
/* JSText stores UTF-32 packed into uint64_t (2 chars per word). */
|
|
||||||
size_t new_size_bytes = sizeof (JSText) + ((new_cap + 1) / 2) * sizeof (uint64_t);
|
|
||||||
JSText *new_str = js_realloc (ctx, s, new_size_bytes);
|
|
||||||
if (!new_str) return NULL;
|
|
||||||
new_cap = min_int (new_cap, JS_STRING_LEN_MAX);
|
|
||||||
new_str->hdr = objhdr_set_cap56 (new_str->hdr, new_cap);
|
|
||||||
return new_str;
|
return new_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3727,10 +3766,26 @@ static JSValue JS_ConcatString (JSContext *ctx, JSValue op1, JSValue op2) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (JS_IsNull (ret_val)) {
|
if (JS_IsNull (ret_val)) {
|
||||||
|
/* Protect op1 and op2 from GC during allocation */
|
||||||
|
JSGCRef op1_ref, op2_ref;
|
||||||
|
JS_PushGCRef (ctx, &op1_ref);
|
||||||
|
op1_ref.val = op1;
|
||||||
|
JS_PushGCRef (ctx, &op2_ref);
|
||||||
|
op2_ref.val = op2;
|
||||||
|
|
||||||
JSText *p = js_alloc_string (ctx, new_len);
|
JSText *p = js_alloc_string (ctx, new_len);
|
||||||
if (!p) {
|
if (!p) {
|
||||||
|
JS_PopGCRef (ctx, &op2_ref);
|
||||||
|
JS_PopGCRef (ctx, &op1_ref);
|
||||||
return JS_EXCEPTION;
|
return JS_EXCEPTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Get possibly-moved values after GC */
|
||||||
|
op1 = op1_ref.val;
|
||||||
|
op2 = op2_ref.val;
|
||||||
|
JS_PopGCRef (ctx, &op2_ref);
|
||||||
|
JS_PopGCRef (ctx, &op1_ref);
|
||||||
|
|
||||||
/* Copy characters using string_put/get */
|
/* Copy characters using string_put/get */
|
||||||
for (int i = 0; i < len1; i++) {
|
for (int i = 0; i < len1; i++) {
|
||||||
string_put (p, i, js_string_value_get (op1, i));
|
string_put (p, i, js_string_value_get (op1, i));
|
||||||
@@ -3738,6 +3793,7 @@ static JSValue JS_ConcatString (JSContext *ctx, JSValue op1, JSValue op2) {
|
|||||||
for (int i = 0; i < len2; i++) {
|
for (int i = 0; i < len2; i++) {
|
||||||
string_put (p, len1 + i, js_string_value_get (op2, i));
|
string_put (p, len1 + i, js_string_value_get (op2, i));
|
||||||
}
|
}
|
||||||
|
p->length = new_len;
|
||||||
ret_val = JS_MKPTR (p);
|
ret_val = JS_MKPTR (p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7216,29 +7272,6 @@ restart:
|
|||||||
}
|
}
|
||||||
goto exception;
|
goto exception;
|
||||||
|
|
||||||
CASE (OP_eval) : {
|
|
||||||
JSValue obj;
|
|
||||||
int scope_idx;
|
|
||||||
call_argc = get_u16 (pc);
|
|
||||||
scope_idx = get_u16 (pc + 2) + ARG_SCOPE_END;
|
|
||||||
pc += 4;
|
|
||||||
call_argv = sp - call_argc;
|
|
||||||
sf->cur_pc = pc;
|
|
||||||
if (js_strict_eq (ctx, call_argv[-1], ctx->eval_obj)) {
|
|
||||||
if (call_argc >= 1)
|
|
||||||
obj = call_argv[0];
|
|
||||||
else
|
|
||||||
obj = JS_NULL;
|
|
||||||
ret_val = JS_EvalObject (ctx, JS_NULL, obj, JS_EVAL_TYPE_DIRECT, scope_idx);
|
|
||||||
} else {
|
|
||||||
ret_val = JS_CallInternal (ctx, call_argv[-1], JS_NULL, call_argc, call_argv, 0);
|
|
||||||
}
|
|
||||||
if (unlikely (JS_IsException (ret_val))) goto exception;
|
|
||||||
for (i = -1; i < call_argc; i++)
|
|
||||||
sp -= call_argc + 1;
|
|
||||||
*sp++ = ret_val;
|
|
||||||
}
|
|
||||||
BREAK;
|
|
||||||
CASE (OP_regexp) : {
|
CASE (OP_regexp) : {
|
||||||
sp[-2] = js_regexp_constructor_internal (ctx, sp[-2], sp[-1]);
|
sp[-2] = js_regexp_constructor_internal (ctx, sp[-2], sp[-1]);
|
||||||
sp--;
|
sp--;
|
||||||
@@ -8468,9 +8501,8 @@ restart:
|
|||||||
|
|
||||||
CASE (OP_get_env_slot) : {
|
CASE (OP_get_env_slot) : {
|
||||||
int slot = get_u16 (pc); pc += 2;
|
int slot = get_u16 (pc); pc += 2;
|
||||||
(void)slot;
|
JSRecord *env = (JSRecord *)JS_VALUE_GET_OBJ (ctx->eval_env);
|
||||||
JS_ThrowInternalError (ctx, "OP_get_env_slot not yet implemented");
|
*sp++ = env->slots[slot].val;
|
||||||
goto exception;
|
|
||||||
}
|
}
|
||||||
BREAK;
|
BREAK;
|
||||||
|
|
||||||
@@ -8764,7 +8796,6 @@ typedef struct JSFunctionDef {
|
|||||||
BOOL has_simple_parameter_list;
|
BOOL has_simple_parameter_list;
|
||||||
BOOL has_parameter_expressions; /* if true, an argument scope is created */
|
BOOL has_parameter_expressions; /* if true, an argument scope is created */
|
||||||
BOOL has_use_strict; /* to reject directive in special cases */
|
BOOL has_use_strict; /* to reject directive in special cases */
|
||||||
BOOL has_eval_call; /* true if the function contains a call to eval() */
|
|
||||||
BOOL has_this_binding; /* true if the 'this' binding is available in the
|
BOOL has_this_binding; /* true if the 'this' binding is available in the
|
||||||
function */
|
function */
|
||||||
BOOL in_function_body;
|
BOOL in_function_body;
|
||||||
@@ -8951,7 +8982,8 @@ static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = {
|
|||||||
Returns new bytecode on success, NULL on link error.
|
Returns new bytecode on success, NULL on link error.
|
||||||
The linked bytecode is a separate allocation that can be modified. */
|
The linked bytecode is a separate allocation that can be modified. */
|
||||||
static JSFunctionBytecode *js_link_bytecode (JSContext *ctx,
|
static JSFunctionBytecode *js_link_bytecode (JSContext *ctx,
|
||||||
JSFunctionBytecode *tpl) {
|
JSFunctionBytecode *tpl,
|
||||||
|
JSValue env) {
|
||||||
/* Calculate total size of bytecode allocation */
|
/* Calculate total size of bytecode allocation */
|
||||||
int function_size;
|
int function_size;
|
||||||
int cpool_offset, vardefs_offset, closure_var_offset, byte_code_offset;
|
int cpool_offset, vardefs_offset, closure_var_offset, byte_code_offset;
|
||||||
@@ -9014,30 +9046,38 @@ static JSFunctionBytecode *js_link_bytecode (JSContext *ctx,
|
|||||||
/* Walk bytecode and patch global variable access opcodes */
|
/* Walk bytecode and patch global variable access opcodes */
|
||||||
uint8_t *bc = linked->byte_code_buf;
|
uint8_t *bc = linked->byte_code_buf;
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
|
|
||||||
|
/* Get env record if provided */
|
||||||
|
JSRecord *env_rec = NULL;
|
||||||
|
if (!JS_IsNull (env) && JS_IsRecord (env)) {
|
||||||
|
env_rec = (JSRecord *)JS_VALUE_GET_OBJ (env);
|
||||||
|
}
|
||||||
|
|
||||||
while (pos < linked->byte_code_len) {
|
while (pos < linked->byte_code_len) {
|
||||||
uint8_t op = bc[pos];
|
uint8_t op = bc[pos];
|
||||||
int len = short_opcode_info (op).size;
|
int len = short_opcode_info (op).size;
|
||||||
|
|
||||||
/* Patch OP_get_var -> OP_get_global_slot */
|
/* Patch OP_get_var -> OP_get_global_slot or OP_get_env_slot */
|
||||||
if (op == OP_get_var || op == OP_get_var_undef) {
|
if (op == OP_get_var || op == OP_get_var_undef) {
|
||||||
uint32_t cpool_idx = get_u32 (bc + pos + 1);
|
uint32_t cpool_idx = get_u32 (bc + pos + 1);
|
||||||
JSValue name = linked->cpool[cpool_idx];
|
JSValue name = linked->cpool[cpool_idx];
|
||||||
|
|
||||||
/* Try global_obj first (for built-ins like 'print') */
|
/* Try env first (if provided) */
|
||||||
JSRecord *global = (JSRecord *)JS_VALUE_GET_OBJ (ctx->global_obj);
|
if (env_rec) {
|
||||||
int slot = rec_find_slot (global, name);
|
int slot = rec_find_slot (env_rec, name);
|
||||||
if (slot > 0) {
|
if (slot > 0) {
|
||||||
bc[pos] = OP_get_global_slot;
|
bc[pos] = OP_get_env_slot;
|
||||||
put_u16 (bc + pos + 1, (uint16_t)slot);
|
put_u16 (bc + pos + 1, (uint16_t)slot);
|
||||||
bc[pos + 3] = OP_nop;
|
bc[pos + 3] = OP_nop;
|
||||||
bc[pos + 4] = OP_nop;
|
bc[pos + 4] = OP_nop;
|
||||||
pos += len;
|
pos += len;
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Try global_var_obj (let/const declarations) */
|
/* Try global_obj (intrinsics like 'print') */
|
||||||
JSRecord *global_var = (JSRecord *)JS_VALUE_GET_OBJ (ctx->global_var_obj);
|
JSRecord *global = (JSRecord *)JS_VALUE_GET_OBJ (ctx->global_obj);
|
||||||
slot = rec_find_slot (global_var, name);
|
int slot = rec_find_slot (global, name);
|
||||||
if (slot > 0) {
|
if (slot > 0) {
|
||||||
bc[pos] = OP_get_global_slot;
|
bc[pos] = OP_get_global_slot;
|
||||||
put_u16 (bc + pos + 1, (uint16_t)slot);
|
put_u16 (bc + pos + 1, (uint16_t)slot);
|
||||||
@@ -9058,44 +9098,17 @@ static JSFunctionBytecode *js_link_bytecode (JSContext *ctx,
|
|||||||
/* OP_get_var_undef is ok - leaves as is for runtime check */
|
/* OP_get_var_undef is ok - leaves as is for runtime check */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Patch OP_put_var family -> OP_set_global_slot */
|
/* Patch OP_put_var family -> error (global is immutable) */
|
||||||
if (op == OP_put_var || op == OP_put_var_init || op == OP_put_var_strict) {
|
if (op == OP_put_var || op == OP_put_var_init || op == OP_put_var_strict) {
|
||||||
uint32_t cpool_idx = get_u32 (bc + pos + 1);
|
uint32_t cpool_idx = get_u32 (bc + pos + 1);
|
||||||
JSValue name = linked->cpool[cpool_idx];
|
JSValue name = linked->cpool[cpool_idx];
|
||||||
|
|
||||||
/* Try global_obj first */
|
/* Global object is immutable - can't write to intrinsics */
|
||||||
JSRecord *global = (JSRecord *)JS_VALUE_GET_OBJ (ctx->global_obj);
|
char buf[64];
|
||||||
int slot = rec_find_slot (global, name);
|
JS_ThrowReferenceError (ctx, "cannot assign to '%s' - global object is immutable",
|
||||||
if (slot > 0) {
|
JS_KeyGetStr (ctx, buf, sizeof (buf), name));
|
||||||
bc[pos] = OP_set_global_slot;
|
pjs_free (linked);
|
||||||
put_u16 (bc + pos + 1, (uint16_t)slot);
|
return NULL;
|
||||||
bc[pos + 3] = OP_nop;
|
|
||||||
bc[pos + 4] = OP_nop;
|
|
||||||
pos += len;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try global_var_obj */
|
|
||||||
JSRecord *global_var = (JSRecord *)JS_VALUE_GET_OBJ (ctx->global_var_obj);
|
|
||||||
slot = rec_find_slot (global_var, name);
|
|
||||||
if (slot > 0) {
|
|
||||||
bc[pos] = OP_set_global_slot;
|
|
||||||
put_u16 (bc + pos + 1, (uint16_t)slot);
|
|
||||||
bc[pos + 3] = OP_nop;
|
|
||||||
bc[pos + 4] = OP_nop;
|
|
||||||
pos += len;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* For put_var_strict, error if not found */
|
|
||||||
if (op == OP_put_var_strict) {
|
|
||||||
char buf[64];
|
|
||||||
JS_ThrowReferenceError (ctx, "'%s' is not defined",
|
|
||||||
JS_KeyGetStr (ctx, buf, sizeof (buf), name));
|
|
||||||
pjs_free (linked);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
/* OP_put_var and OP_put_var_init - leave for runtime (implicit global) */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Patch OP_check_var -> OP_nop (if variable exists) */
|
/* Patch OP_check_var -> OP_nop (if variable exists) */
|
||||||
@@ -9103,10 +9116,18 @@ static JSFunctionBytecode *js_link_bytecode (JSContext *ctx,
|
|||||||
uint32_t cpool_idx = get_u32 (bc + pos + 1);
|
uint32_t cpool_idx = get_u32 (bc + pos + 1);
|
||||||
JSValue name = linked->cpool[cpool_idx];
|
JSValue name = linked->cpool[cpool_idx];
|
||||||
|
|
||||||
JSRecord *global = (JSRecord *)JS_VALUE_GET_OBJ (ctx->global_obj);
|
BOOL found = FALSE;
|
||||||
JSRecord *global_var = (JSRecord *)JS_VALUE_GET_OBJ (ctx->global_var_obj);
|
if (env_rec && rec_find_slot (env_rec, name) > 0) {
|
||||||
|
found = TRUE;
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
JSRecord *global = (JSRecord *)JS_VALUE_GET_OBJ (ctx->global_obj);
|
||||||
|
if (rec_find_slot (global, name) > 0) {
|
||||||
|
found = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (rec_find_slot (global, name) > 0 || rec_find_slot (global_var, name) > 0) {
|
if (found) {
|
||||||
/* Variable exists, replace with NOPs */
|
/* Variable exists, replace with NOPs */
|
||||||
bc[pos] = OP_nop;
|
bc[pos] = OP_nop;
|
||||||
bc[pos + 1] = OP_nop;
|
bc[pos + 1] = OP_nop;
|
||||||
@@ -9114,7 +9135,7 @@ static JSFunctionBytecode *js_link_bytecode (JSContext *ctx,
|
|||||||
bc[pos + 3] = OP_nop;
|
bc[pos + 3] = OP_nop;
|
||||||
bc[pos + 4] = OP_nop;
|
bc[pos + 4] = OP_nop;
|
||||||
}
|
}
|
||||||
/* else leave check_var for runtime - might be implicit global */
|
/* else leave check_var for runtime */
|
||||||
}
|
}
|
||||||
|
|
||||||
pos += len;
|
pos += len;
|
||||||
@@ -12412,17 +12433,11 @@ static __exception int js_parse_postfix_expr (JSParseState *s,
|
|||||||
uint32_t idx = get_u32 (fd->byte_code.buf + fd->last_opcode_pos + 1);
|
uint32_t idx = get_u32 (fd->byte_code.buf + fd->last_opcode_pos + 1);
|
||||||
name = fd->cpool[idx];
|
name = fd->cpool[idx];
|
||||||
scope = get_u16 (fd->byte_code.buf + fd->last_opcode_pos + 5);
|
scope = get_u16 (fd->byte_code.buf + fd->last_opcode_pos + 5);
|
||||||
if (js_key_equal_str (name, "eval") && call_type == FUNC_CALL_NORMAL
|
/* verify if function name resolves to a simple
|
||||||
&& !has_optional_chain) {
|
get_loc/get_arg: a function call inside a `with`
|
||||||
/* direct 'eval' */
|
statement can resolve to a method call of the
|
||||||
opcode = OP_eval;
|
`with` context object
|
||||||
} else {
|
*/
|
||||||
/* verify if function name resolves to a simple
|
|
||||||
get_loc/get_arg: a function call inside a `with`
|
|
||||||
statement can resolve to a method call of the
|
|
||||||
`with` context object
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
drop_count = 1;
|
drop_count = 1;
|
||||||
} break;
|
} break;
|
||||||
default:
|
default:
|
||||||
@@ -12463,12 +12478,6 @@ static __exception int js_parse_postfix_expr (JSParseState *s,
|
|||||||
emit_op (s, OP_call_method);
|
emit_op (s, OP_call_method);
|
||||||
emit_u16 (s, arg_count);
|
emit_u16 (s, arg_count);
|
||||||
break;
|
break;
|
||||||
case OP_eval:
|
|
||||||
emit_op (s, OP_eval);
|
|
||||||
emit_u16 (s, arg_count);
|
|
||||||
emit_u16 (s, fd->scope_level);
|
|
||||||
fd->has_eval_call = TRUE;
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
emit_op (s, OP_call);
|
emit_op (s, OP_call);
|
||||||
emit_u16 (s, arg_count);
|
emit_u16 (s, arg_count);
|
||||||
@@ -14801,187 +14810,6 @@ done:
|
|||||||
return pos_next;
|
return pos_next;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mark_eval_captured_variables (JSContext *ctx, JSFunctionDef *s, int scope_level) {
|
|
||||||
int idx;
|
|
||||||
JSVarDef *vd;
|
|
||||||
|
|
||||||
for (idx = s->scopes[scope_level].first; idx >= 0;) {
|
|
||||||
vd = &s->vars[idx];
|
|
||||||
vd->is_captured = 1;
|
|
||||||
idx = vd->scope_next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* XXX: should handle the argument scope generically */
|
|
||||||
static BOOL is_var_in_arg_scope (const JSVarDef *vd) {
|
|
||||||
return (js_key_equal_str (vd->var_name, "home_object")
|
|
||||||
|| js_key_equal_str (vd->var_name, "this_active_func")
|
|
||||||
|| js_key_equal_str (vd->var_name, "new_target")
|
|
||||||
|| js_key_equal (vd->var_name, JS_KEY_this)
|
|
||||||
|| js_key_equal_str (vd->var_name, "_arg_var_")
|
|
||||||
|| vd->var_kind == JS_VAR_FUNCTION_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void add_eval_variables (JSContext *ctx, JSFunctionDef *s) {
|
|
||||||
JSFunctionDef *fd;
|
|
||||||
JSVarDef *vd;
|
|
||||||
int i, scope_level, scope_idx;
|
|
||||||
BOOL has_this_binding, is_arg_scope;
|
|
||||||
|
|
||||||
has_this_binding = s->has_this_binding;
|
|
||||||
if (has_this_binding) {
|
|
||||||
if (s->this_var_idx < 0) s->this_var_idx = add_var_this (ctx, s);
|
|
||||||
}
|
|
||||||
if (s->is_func_expr && !JS_IsNull (s->func_name))
|
|
||||||
add_func_var (ctx, s, s->func_name);
|
|
||||||
|
|
||||||
/* eval can use all the variables of the enclosing functions, so
|
|
||||||
they must be all put in the closure. The closure variables are
|
|
||||||
ordered by scope. It works only because no closure are created
|
|
||||||
before. */
|
|
||||||
assert (s->is_eval || s->closure_var_count == 0);
|
|
||||||
|
|
||||||
/* XXX: inefficient, but eval performance is less critical */
|
|
||||||
fd = s;
|
|
||||||
for (;;) {
|
|
||||||
scope_level = fd->parent_scope_level;
|
|
||||||
fd = fd->parent;
|
|
||||||
if (!fd) break;
|
|
||||||
/* add 'this' if it was not previously added */
|
|
||||||
if (!has_this_binding && fd->has_this_binding) {
|
|
||||||
if (fd->this_var_idx < 0) fd->this_var_idx = add_var_this (ctx, fd);
|
|
||||||
has_this_binding = TRUE;
|
|
||||||
}
|
|
||||||
/* add function name */
|
|
||||||
if (fd->is_func_expr && !JS_IsNull (fd->func_name))
|
|
||||||
add_func_var (ctx, fd, fd->func_name);
|
|
||||||
|
|
||||||
/* add lexical variables */
|
|
||||||
scope_idx = fd->scopes[scope_level].first;
|
|
||||||
while (scope_idx >= 0) {
|
|
||||||
vd = &fd->vars[scope_idx];
|
|
||||||
vd->is_captured = 1;
|
|
||||||
get_closure_var (ctx, s, fd, FALSE, scope_idx, vd->var_name, vd->is_const, vd->is_lexical, vd->var_kind);
|
|
||||||
scope_idx = vd->scope_next;
|
|
||||||
}
|
|
||||||
is_arg_scope = (scope_idx == ARG_SCOPE_END);
|
|
||||||
if (!is_arg_scope) {
|
|
||||||
/* add unscoped variables */
|
|
||||||
/* XXX: propagate is_const and var_kind too ? */
|
|
||||||
for (i = 0; i < fd->arg_count; i++) {
|
|
||||||
vd = &fd->args[i];
|
|
||||||
if (!JS_IsNull (vd->var_name)) {
|
|
||||||
get_closure_var (ctx, s, fd, TRUE, i, vd->var_name, FALSE, vd->is_lexical, JS_VAR_NORMAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (i = 0; i < fd->var_count; i++) {
|
|
||||||
vd = &fd->vars[i];
|
|
||||||
/* do not close top level last result */
|
|
||||||
if (vd->scope_level == 0 && !js_key_equal (vd->var_name, JS_KEY__ret_)
|
|
||||||
&& !JS_IsNull (vd->var_name)) {
|
|
||||||
get_closure_var (ctx, s, fd, FALSE, i, vd->var_name, FALSE, vd->is_lexical, JS_VAR_NORMAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (i = 0; i < fd->var_count; i++) {
|
|
||||||
vd = &fd->vars[i];
|
|
||||||
/* do not close top level last result */
|
|
||||||
if (vd->scope_level == 0 && is_var_in_arg_scope (vd)) {
|
|
||||||
get_closure_var (ctx, s, fd, FALSE, i, vd->var_name, FALSE, vd->is_lexical, JS_VAR_NORMAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (fd->is_eval) {
|
|
||||||
int idx;
|
|
||||||
/* add direct eval variables (we are necessarily at the
|
|
||||||
top level) */
|
|
||||||
for (idx = 0; idx < fd->closure_var_count; idx++) {
|
|
||||||
JSClosureVar *cv = &fd->closure_var[idx];
|
|
||||||
get_closure_var2 (ctx, s, fd, FALSE, cv->is_arg, idx, cv->var_name, cv->is_const, cv->is_lexical, cv->var_kind);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void set_closure_from_var (JSContext *ctx, JSClosureVar *cv, JSVarDef *vd, int var_idx) {
|
|
||||||
cv->is_local = TRUE;
|
|
||||||
cv->is_arg = FALSE;
|
|
||||||
cv->is_const = vd->is_const;
|
|
||||||
cv->is_lexical = vd->is_lexical;
|
|
||||||
cv->var_kind = vd->var_kind;
|
|
||||||
cv->var_idx = var_idx;
|
|
||||||
cv->var_name = vd->var_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* for direct eval compilation: add references to the variables of the
|
|
||||||
calling function */
|
|
||||||
static __exception int add_closure_variables (JSContext *ctx, JSFunctionDef *s, JSFunctionBytecode *b, int scope_idx) {
|
|
||||||
int i, count;
|
|
||||||
JSVarDef *vd;
|
|
||||||
BOOL is_arg_scope;
|
|
||||||
|
|
||||||
count = b->arg_count + b->var_count + b->closure_var_count;
|
|
||||||
s->closure_var = NULL;
|
|
||||||
s->closure_var_count = 0;
|
|
||||||
s->closure_var_size = count;
|
|
||||||
if (count == 0) return 0;
|
|
||||||
s->closure_var = js_malloc (ctx, sizeof (s->closure_var[0]) * count);
|
|
||||||
if (!s->closure_var) return -1;
|
|
||||||
/* Add lexical variables in scope at the point of evaluation */
|
|
||||||
for (i = scope_idx; i >= 0;) {
|
|
||||||
vd = &b->vardefs[b->arg_count + i];
|
|
||||||
if (vd->scope_level > 0) {
|
|
||||||
JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
|
|
||||||
set_closure_from_var (ctx, cv, vd, i);
|
|
||||||
}
|
|
||||||
i = vd->scope_next;
|
|
||||||
}
|
|
||||||
is_arg_scope = (i == ARG_SCOPE_END);
|
|
||||||
if (!is_arg_scope) {
|
|
||||||
/* Add argument variables */
|
|
||||||
for (i = 0; i < b->arg_count; i++) {
|
|
||||||
JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
|
|
||||||
vd = &b->vardefs[i];
|
|
||||||
cv->is_local = TRUE;
|
|
||||||
cv->is_arg = TRUE;
|
|
||||||
cv->is_const = FALSE;
|
|
||||||
cv->is_lexical = FALSE;
|
|
||||||
cv->var_kind = JS_VAR_NORMAL;
|
|
||||||
cv->var_idx = i;
|
|
||||||
cv->var_name = vd->var_name;
|
|
||||||
}
|
|
||||||
/* Add local non lexical variables */
|
|
||||||
for (i = 0; i < b->var_count; i++) {
|
|
||||||
vd = &b->vardefs[b->arg_count + i];
|
|
||||||
if (vd->scope_level == 0 && !js_key_equal (vd->var_name, JS_KEY__ret_)) {
|
|
||||||
JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
|
|
||||||
set_closure_from_var (ctx, cv, vd, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* only add pseudo variables */
|
|
||||||
for (i = 0; i < b->var_count; i++) {
|
|
||||||
vd = &b->vardefs[b->arg_count + i];
|
|
||||||
if (vd->scope_level == 0 && is_var_in_arg_scope (vd)) {
|
|
||||||
JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
|
|
||||||
set_closure_from_var (ctx, cv, vd, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (i = 0; i < b->closure_var_count; i++) {
|
|
||||||
JSClosureVar *cv0 = &b->closure_var[i];
|
|
||||||
JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
|
|
||||||
cv->is_local = FALSE;
|
|
||||||
cv->is_arg = cv0->is_arg;
|
|
||||||
cv->is_const = cv0->is_const;
|
|
||||||
cv->is_lexical = cv0->is_lexical;
|
|
||||||
cv->var_kind = cv0->var_kind;
|
|
||||||
cv->var_idx = i;
|
|
||||||
cv->var_name = cv0->var_name;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct CodeContext {
|
typedef struct CodeContext {
|
||||||
const uint8_t *bc_buf; /* code buffer */
|
const uint8_t *bc_buf; /* code buffer */
|
||||||
int bc_len; /* length of the code buffer */
|
int bc_len; /* length of the code buffer */
|
||||||
@@ -15346,15 +15174,6 @@ static __exception int resolve_variables (JSContext *ctx, JSFunctionDef *s) {
|
|||||||
s->line_number_size++;
|
s->line_number_size++;
|
||||||
goto no_change;
|
goto no_change;
|
||||||
|
|
||||||
case OP_eval: /* convert scope index to adjusted variable index */
|
|
||||||
{
|
|
||||||
int call_argc = get_u16 (bc_buf + pos + 1);
|
|
||||||
scope = get_u16 (bc_buf + pos + 1 + 2);
|
|
||||||
mark_eval_captured_variables (ctx, s, scope);
|
|
||||||
dbuf_putc (&bc_out, op);
|
|
||||||
dbuf_put_u16 (&bc_out, call_argc);
|
|
||||||
dbuf_put_u16 (&bc_out, s->scopes[scope].first - ARG_SCOPE_END);
|
|
||||||
} break;
|
|
||||||
case OP_scope_get_var_checkthis:
|
case OP_scope_get_var_checkthis:
|
||||||
case OP_scope_get_var_undef:
|
case OP_scope_get_var_undef:
|
||||||
case OP_scope_get_var:
|
case OP_scope_get_var:
|
||||||
@@ -16725,12 +16544,6 @@ static JSValue js_create_function (JSContext *ctx, JSFunctionDef *fd) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if the function contains an eval call, the closure variables
|
|
||||||
are used to compile the eval and they must be ordered by scope,
|
|
||||||
so it is necessary to create the closure variables before any
|
|
||||||
other variable lookup is done. */
|
|
||||||
if (fd->has_eval_call) add_eval_variables (ctx, fd);
|
|
||||||
|
|
||||||
/* first create all the child functions */
|
/* first create all the child functions */
|
||||||
list_for_each_safe (el, el1, &fd->child_list) {
|
list_for_each_safe (el, el1, &fd->child_list) {
|
||||||
JSFunctionDef *fd1;
|
JSFunctionDef *fd1;
|
||||||
@@ -16775,7 +16588,7 @@ static JSValue js_create_function (JSContext *ctx, JSFunctionDef *fd) {
|
|||||||
cpool_offset = function_size;
|
cpool_offset = function_size;
|
||||||
function_size += fd->cpool_count * sizeof (*fd->cpool);
|
function_size += fd->cpool_count * sizeof (*fd->cpool);
|
||||||
vardefs_offset = function_size;
|
vardefs_offset = function_size;
|
||||||
if (!fd->strip_debug || fd->has_eval_call) {
|
if (!fd->strip_debug) {
|
||||||
function_size += (fd->arg_count + fd->var_count) * sizeof (*b->vardefs);
|
function_size += (fd->arg_count + fd->var_count) * sizeof (*b->vardefs);
|
||||||
}
|
}
|
||||||
closure_var_offset = function_size;
|
closure_var_offset = function_size;
|
||||||
@@ -16796,7 +16609,7 @@ static JSValue js_create_function (JSContext *ctx, JSFunctionDef *fd) {
|
|||||||
|
|
||||||
b->func_name = fd->func_name;
|
b->func_name = fd->func_name;
|
||||||
if (fd->arg_count + fd->var_count > 0) {
|
if (fd->arg_count + fd->var_count > 0) {
|
||||||
if (fd->strip_debug && !fd->has_eval_call) {
|
if (fd->strip_debug) {
|
||||||
/* Strip variable definitions not needed at runtime */
|
/* Strip variable definitions not needed at runtime */
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < fd->var_count; i++) {
|
for (i = 0; i < fd->var_count; i++) {
|
||||||
@@ -17445,10 +17258,65 @@ JSValue JS_EvalFunction (JSContext *ctx, JSValue fun_obj) {
|
|||||||
return JS_EvalFunctionInternal (ctx, fun_obj, ctx->global_obj, NULL);
|
return JS_EvalFunctionInternal (ctx, fun_obj, ctx->global_obj, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Eval function with environment record for variable resolution.
|
||||||
|
The env must be a stoned record. Variables are resolved env first,
|
||||||
|
then global intrinsics. */
|
||||||
|
JSValue JS_EvalFunctionEnv (JSContext *ctx, JSValue fun_obj, JSValue env) {
|
||||||
|
JSValue ret_val;
|
||||||
|
JSValue saved_env;
|
||||||
|
uint32_t tag;
|
||||||
|
JSGCRef env_ref, fun_ref;
|
||||||
|
|
||||||
|
tag = JS_VALUE_GET_TAG (fun_obj);
|
||||||
|
/* JSFunctionBytecode uses OBJ_CODE type with JS_TAG_PTR */
|
||||||
|
if (tag != JS_TAG_PTR || objhdr_type (*(objhdr_t *)JS_VALUE_GET_PTR (fun_obj)) != OBJ_CODE) {
|
||||||
|
return JS_ThrowTypeError (ctx, "bytecode function expected");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Protect env and fun_obj from GC during linking */
|
||||||
|
JS_AddGCRef (ctx, &env_ref);
|
||||||
|
env_ref.val = env;
|
||||||
|
JS_AddGCRef (ctx, &fun_ref);
|
||||||
|
fun_ref.val = fun_obj;
|
||||||
|
|
||||||
|
/* Link with environment */
|
||||||
|
JSValue linked = JS_LinkFunctionEnv (ctx, fun_ref.val, env_ref.val);
|
||||||
|
if (JS_IsException (linked)) {
|
||||||
|
JS_DeleteGCRef (ctx, &fun_ref);
|
||||||
|
JS_DeleteGCRef (ctx, &env_ref);
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update env from GC ref (may have moved) */
|
||||||
|
env = env_ref.val;
|
||||||
|
JS_DeleteGCRef (ctx, &fun_ref);
|
||||||
|
JS_DeleteGCRef (ctx, &env_ref);
|
||||||
|
|
||||||
|
/* Save and set eval environment for OP_get_env_slot */
|
||||||
|
saved_env = ctx->eval_env;
|
||||||
|
ctx->eval_env = env;
|
||||||
|
|
||||||
|
/* Create closure and execute */
|
||||||
|
linked = js_closure (ctx, linked, NULL);
|
||||||
|
ret_val = JS_Call (ctx, linked, ctx->global_obj, 0, NULL);
|
||||||
|
|
||||||
|
/* Restore env */
|
||||||
|
ctx->eval_env = saved_env;
|
||||||
|
|
||||||
|
return ret_val;
|
||||||
|
}
|
||||||
|
|
||||||
/* Link compiled bytecode to context - resolves global references.
|
/* Link compiled bytecode to context - resolves global references.
|
||||||
Returns linked bytecode on success, JS_EXCEPTION on link error.
|
Returns linked bytecode on success, JS_EXCEPTION on link error.
|
||||||
The linked bytecode is a separate copy that can be modified. */
|
The linked bytecode is a separate copy that can be modified. */
|
||||||
JSValue JS_LinkFunction (JSContext *ctx, JSValue fun_obj) {
|
JSValue JS_LinkFunction (JSContext *ctx, JSValue fun_obj) {
|
||||||
|
return JS_LinkFunctionEnv (ctx, fun_obj, JS_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Link compiled bytecode with environment record for variable resolution.
|
||||||
|
Variables are resolved: env first, then global intrinsics.
|
||||||
|
Returns linked bytecode on success, JS_EXCEPTION on link error. */
|
||||||
|
JSValue JS_LinkFunctionEnv (JSContext *ctx, JSValue fun_obj, JSValue env) {
|
||||||
if (JS_VALUE_GET_TAG (fun_obj) != JS_TAG_PTR)
|
if (JS_VALUE_GET_TAG (fun_obj) != JS_TAG_PTR)
|
||||||
return JS_ThrowTypeError (ctx, "bytecode function expected");
|
return JS_ThrowTypeError (ctx, "bytecode function expected");
|
||||||
|
|
||||||
@@ -17457,7 +17325,7 @@ JSValue JS_LinkFunction (JSContext *ctx, JSValue fun_obj) {
|
|||||||
return JS_ThrowTypeError (ctx, "bytecode function expected");
|
return JS_ThrowTypeError (ctx, "bytecode function expected");
|
||||||
|
|
||||||
JSFunctionBytecode *tpl = (JSFunctionBytecode *)hdr;
|
JSFunctionBytecode *tpl = (JSFunctionBytecode *)hdr;
|
||||||
JSFunctionBytecode *linked = js_link_bytecode (ctx, tpl);
|
JSFunctionBytecode *linked = js_link_bytecode (ctx, tpl, env);
|
||||||
if (!linked)
|
if (!linked)
|
||||||
return JS_EXCEPTION;
|
return JS_EXCEPTION;
|
||||||
|
|
||||||
@@ -17467,40 +17335,21 @@ JSValue JS_LinkFunction (JSContext *ctx, JSValue fun_obj) {
|
|||||||
/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */
|
/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */
|
||||||
static JSValue __JS_EvalInternal (JSContext *ctx, JSValue this_obj, const char *input, size_t input_len, const char *filename, int flags, int scope_idx) {
|
static JSValue __JS_EvalInternal (JSContext *ctx, JSValue this_obj, const char *input, size_t input_len, const char *filename, int flags, int scope_idx) {
|
||||||
JSParseState s1, *s = &s1;
|
JSParseState s1, *s = &s1;
|
||||||
int err, js_mode, eval_type;
|
int err;
|
||||||
JSValue fun_obj, ret_val;
|
JSValue fun_obj, ret_val;
|
||||||
JSStackFrame *sf;
|
|
||||||
JSFunctionBytecode *b;
|
|
||||||
JSFunctionDef *fd;
|
JSFunctionDef *fd;
|
||||||
|
(void)scope_idx; /* unused - direct eval no longer supported */
|
||||||
|
|
||||||
js_parse_init (ctx, s, input, input_len, filename);
|
js_parse_init (ctx, s, input, input_len, filename);
|
||||||
|
|
||||||
eval_type = flags & JS_EVAL_TYPE_MASK;
|
|
||||||
if (eval_type == JS_EVAL_TYPE_DIRECT) {
|
|
||||||
JSFunction *fn;
|
|
||||||
sf = ctx->rt->current_stack_frame;
|
|
||||||
assert (sf != NULL);
|
|
||||||
assert (JS_VALUE_GET_TAG (sf->cur_func) == JS_TAG_FUNCTION);
|
|
||||||
fn = JS_VALUE_GET_FUNCTION (sf->cur_func);
|
|
||||||
assert (fn->kind == JS_FUNC_KIND_BYTECODE);
|
|
||||||
b = fn->u.func.function_bytecode;
|
|
||||||
js_mode = b->js_mode;
|
|
||||||
} else {
|
|
||||||
sf = NULL;
|
|
||||||
b = NULL;
|
|
||||||
js_mode = 0;
|
|
||||||
}
|
|
||||||
fd = js_new_function_def (ctx, NULL, TRUE, FALSE, filename, s->buf_start, &s->get_line_col_cache);
|
fd = js_new_function_def (ctx, NULL, TRUE, FALSE, filename, s->buf_start, &s->get_line_col_cache);
|
||||||
if (!fd) goto fail1;
|
if (!fd) goto fail1;
|
||||||
s->cur_func = fd;
|
s->cur_func = fd;
|
||||||
ctx->current_parse_fd = fd; /* Set GC root for parser's cpool */
|
ctx->current_parse_fd = fd; /* Set GC root for parser's cpool */
|
||||||
fd->eval_type = eval_type;
|
fd->eval_type = JS_EVAL_TYPE_GLOBAL;
|
||||||
fd->has_this_binding = (eval_type != JS_EVAL_TYPE_DIRECT);
|
fd->has_this_binding = TRUE;
|
||||||
fd->js_mode = js_mode;
|
fd->js_mode = 0;
|
||||||
fd->func_name = JS_KEY__eval_;
|
fd->func_name = JS_KEY__eval_;
|
||||||
if (b) {
|
|
||||||
if (add_closure_variables (ctx, fd, b, scope_idx)) goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
push_scope (s); /* body scope */
|
push_scope (s); /* body scope */
|
||||||
fd->body_scope = fd->scope_level;
|
fd->body_scope = fd->scope_level;
|
||||||
@@ -17522,7 +17371,7 @@ static JSValue __JS_EvalInternal (JSContext *ctx, JSValue this_obj, const char *
|
|||||||
if (flags & JS_EVAL_FLAG_COMPILE_ONLY) {
|
if (flags & JS_EVAL_FLAG_COMPILE_ONLY) {
|
||||||
ret_val = fun_obj;
|
ret_val = fun_obj;
|
||||||
} else {
|
} else {
|
||||||
ret_val = JS_EvalFunctionInternal (ctx, fun_obj, this_obj, sf);
|
ret_val = JS_EvalFunctionInternal (ctx, fun_obj, this_obj, NULL);
|
||||||
}
|
}
|
||||||
return ret_val;
|
return ret_val;
|
||||||
fail1:
|
fail1:
|
||||||
@@ -24318,6 +24167,66 @@ int js_is_blob (JSContext *js, JSValue v) {
|
|||||||
return js_get_blob (js, v) != NULL;
|
return js_get_blob (js, v) != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* eval() function - compile and execute code with environment
|
||||||
|
* ============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* eval(text, env) - evaluate code with optional environment record
|
||||||
|
* text: string to compile and execute
|
||||||
|
* env: optional stone record for variable bindings (checked first before intrinsics)
|
||||||
|
*/
|
||||||
|
static JSValue js_cell_eval (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||||
|
const char *str;
|
||||||
|
size_t len;
|
||||||
|
JSValue env = JS_NULL;
|
||||||
|
JSValue result;
|
||||||
|
JSGCRef env_ref;
|
||||||
|
|
||||||
|
if (argc < 1 || !JS_IsText (argv[0])) {
|
||||||
|
return JS_ThrowTypeError (ctx, "eval requires a text argument");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get optional environment record (must be stone if provided) */
|
||||||
|
if (argc > 1 && !JS_IsNull (argv[1])) {
|
||||||
|
if (!JS_IsRecord (argv[1])) {
|
||||||
|
return JS_ThrowTypeError (ctx, "eval environment must be an object");
|
||||||
|
}
|
||||||
|
JSRecord *rec = (JSRecord *)JS_VALUE_GET_OBJ (argv[1]);
|
||||||
|
if (!objhdr_s (rec->mist_hdr)) {
|
||||||
|
return JS_ThrowTypeError (ctx, "eval environment must be stoned");
|
||||||
|
}
|
||||||
|
env = argv[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Protect env from GC during compilation */
|
||||||
|
JS_AddGCRef (ctx, &env_ref);
|
||||||
|
env_ref.val = env;
|
||||||
|
|
||||||
|
/* Get text string */
|
||||||
|
str = JS_ToCStringLen (ctx, &len, argv[0]);
|
||||||
|
if (!str) {
|
||||||
|
JS_DeleteGCRef (ctx, &env_ref);
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compile the text */
|
||||||
|
JSValue fun = JS_Eval (ctx, str, len, "<eval>", JS_EVAL_FLAG_COMPILE_ONLY);
|
||||||
|
JS_FreeCString (ctx, str);
|
||||||
|
if (JS_IsException (fun)) {
|
||||||
|
JS_DeleteGCRef (ctx, &env_ref);
|
||||||
|
return fun;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update env from GC ref (may have moved) */
|
||||||
|
env = env_ref.val;
|
||||||
|
JS_DeleteGCRef (ctx, &env_ref);
|
||||||
|
|
||||||
|
/* Eval with environment */
|
||||||
|
result = JS_EvalFunctionEnv (ctx, fun, env);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
* stone() function - deep freeze with blob support
|
* stone() function - deep freeze with blob support
|
||||||
* ============================================================================
|
* ============================================================================
|
||||||
@@ -25294,7 +25203,7 @@ void JS_AddIntrinsicBaseObjects (JSContext *ctx) {
|
|||||||
|
|
||||||
ctx->throw_type_error = JS_NewCFunction (ctx, js_throw_type_error, NULL, 0);
|
ctx->throw_type_error = JS_NewCFunction (ctx, js_throw_type_error, NULL, 0);
|
||||||
ctx->global_obj = JS_NewObject (ctx);
|
ctx->global_obj = JS_NewObject (ctx);
|
||||||
ctx->global_var_obj = JS_NewObjectProto (ctx, JS_NULL);
|
ctx->eval_env = JS_NULL; /* no eval environment by default */
|
||||||
|
|
||||||
/* Error */
|
/* Error */
|
||||||
obj1 = JS_NewCFunctionMagic (ctx, js_error_constructor, "Error", 1, JS_CFUNC_generic_magic, -1);
|
obj1 = JS_NewCFunctionMagic (ctx, js_error_constructor, "Error", 1, JS_CFUNC_generic_magic, -1);
|
||||||
@@ -25314,12 +25223,6 @@ void JS_AddIntrinsicBaseObjects (JSContext *ctx) {
|
|||||||
REGISTER_ERROR(7, "AggregateError");
|
REGISTER_ERROR(7, "AggregateError");
|
||||||
#undef REGISTER_ERROR
|
#undef REGISTER_ERROR
|
||||||
|
|
||||||
/* global properties */
|
|
||||||
{
|
|
||||||
JSValue key = JS_KEY_STR (ctx, "globalThis");
|
|
||||||
JS_SetPropertyInternal (ctx, ctx->global_obj, key, ctx->global_obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cell Script global functions: text, number, array, object, fn */
|
/* Cell Script global functions: text, number, array, object, fn */
|
||||||
{
|
{
|
||||||
JSValue text_func = JS_NewCFunction (ctx, js_cell_text, "text", 3);
|
JSValue text_func = JS_NewCFunction (ctx, js_cell_text, "text", 3);
|
||||||
@@ -25349,6 +25252,7 @@ void JS_AddIntrinsicBaseObjects (JSContext *ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Core functions - using GC-safe helper */
|
/* Core functions - using GC-safe helper */
|
||||||
|
js_set_global_cfunc(ctx, "eval", js_cell_eval, 2);
|
||||||
js_set_global_cfunc(ctx, "stone", js_cell_stone, 1);
|
js_set_global_cfunc(ctx, "stone", js_cell_stone, 1);
|
||||||
js_set_global_cfunc(ctx, "length", js_cell_length, 1);
|
js_set_global_cfunc(ctx, "length", js_cell_length, 1);
|
||||||
js_set_global_cfunc(ctx, "call", js_cell_call, 3);
|
js_set_global_cfunc(ctx, "call", js_cell_call, 3);
|
||||||
|
|||||||
@@ -783,6 +783,11 @@ JSValue JS_ReadObject (JSContext *ctx, const uint8_t *buf, size_t buf_len,
|
|||||||
reading a script or module with JS_ReadObject() */
|
reading a script or module with JS_ReadObject() */
|
||||||
JSValue JS_EvalFunction (JSContext *ctx, JSValue fun_obj);
|
JSValue JS_EvalFunction (JSContext *ctx, JSValue fun_obj);
|
||||||
|
|
||||||
|
/* Eval function with environment record for variable resolution.
|
||||||
|
The env must be a stoned record. Variables are resolved env first,
|
||||||
|
then global intrinsics. */
|
||||||
|
JSValue JS_EvalFunctionEnv (JSContext *ctx, JSValue fun_obj, JSValue env);
|
||||||
|
|
||||||
/* Dump bytecode of a compiled function (for debugging) */
|
/* Dump bytecode of a compiled function (for debugging) */
|
||||||
void JS_DumpFunctionBytecode (JSContext *ctx, JSValue func_val);
|
void JS_DumpFunctionBytecode (JSContext *ctx, JSValue func_val);
|
||||||
|
|
||||||
@@ -790,6 +795,11 @@ void JS_DumpFunctionBytecode (JSContext *ctx, JSValue func_val);
|
|||||||
Returns linked bytecode on success, JS_EXCEPTION on link error. */
|
Returns linked bytecode on success, JS_EXCEPTION on link error. */
|
||||||
JSValue JS_LinkFunction (JSContext *ctx, JSValue func_val);
|
JSValue JS_LinkFunction (JSContext *ctx, JSValue func_val);
|
||||||
|
|
||||||
|
/* Link compiled bytecode with environment record for variable resolution.
|
||||||
|
Variables are resolved: env first, then global intrinsics.
|
||||||
|
Returns linked bytecode on success, JS_EXCEPTION on link error. */
|
||||||
|
JSValue JS_LinkFunctionEnv (JSContext *ctx, JSValue func_val, JSValue env);
|
||||||
|
|
||||||
/* C function definition */
|
/* C function definition */
|
||||||
typedef enum JSCFunctionEnum {
|
typedef enum JSCFunctionEnum {
|
||||||
JS_CFUNC_generic,
|
JS_CFUNC_generic,
|
||||||
|
|||||||
Reference in New Issue
Block a user