- Closure optimization (go from quadratic to linear time when the number

of closure variables is large)
- Separated JSVarDef and JSBytecodeVarDef to simplify the code and save memory
- fixed debug info stripping with global variables
This commit is contained in:
Fabrice Bellard
2025-11-15 12:01:20 +01:00
parent 9688007ccb
commit ae7219b1a1

435
quickjs.c
View File

@@ -328,7 +328,7 @@ typedef struct JSStackFrame {
JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */ JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */
JSValue *arg_buf; /* arguments */ JSValue *arg_buf; /* arguments */
JSValue *var_buf; /* variables */ JSValue *var_buf; /* variables */
struct list_head var_ref_list; /* list of JSVarRef.var_ref_link */ struct JSVarRef **var_refs; /* references to arguments or local variables */
const uint8_t *cur_pc; /* only used in bytecode functions : PC of the const uint8_t *cur_pc; /* only used in bytecode functions : PC of the
instruction after the call */ instruction after the call */
int arg_count; int arg_count;
@@ -388,8 +388,8 @@ typedef struct JSVarRef {
union { union {
JSValue value; /* used when is_detached = TRUE */ JSValue value; /* used when is_detached = TRUE */
struct { struct {
struct list_head var_ref_link; /* JSStackFrame.var_ref_list list */ uint16_t var_ref_idx; /* index in JSStackFrame.var_refs[] */
struct JSAsyncFunctionState *async_func; /* != NULL if async stack frame */ JSStackFrame *stack_frame;
}; /* used when is_detached = FALSE */ }; /* used when is_detached = FALSE */
}; };
} JSVarRef; } JSVarRef;
@@ -563,7 +563,6 @@ typedef struct JSClosureVar {
uint8_t is_lexical : 1; /* lexical variable */ uint8_t is_lexical : 1; /* lexical variable */
uint8_t is_const : 1; /* const variable (is_lexical = 1 if is_const = 1 */ uint8_t is_const : 1; /* const variable (is_lexical = 1 if is_const = 1 */
uint8_t var_kind : 4; /* see JSVarKindEnum */ uint8_t var_kind : 4; /* see JSVarKindEnum */
/* 8 bits available */
uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the
parent function. otherwise: index to a closure parent function. otherwise: index to a closure
variable of the parent function */ variable of the parent function */
@@ -573,11 +572,6 @@ typedef struct JSClosureVar {
#define ARG_SCOPE_INDEX 1 #define ARG_SCOPE_INDEX 1
#define ARG_SCOPE_END (-2) #define ARG_SCOPE_END (-2)
typedef struct JSVarScope {
int parent; /* index into fd->scopes of the enclosing scope */
int first; /* index into fd->vars of the last variable in this scope */
} JSVarScope;
typedef enum { typedef enum {
/* XXX: add more variable kinds here instead of using bit fields */ /* XXX: add more variable kinds here instead of using bit fields */
JS_VAR_NORMAL, JS_VAR_NORMAL,
@@ -594,35 +588,23 @@ typedef enum {
JS_VAR_GLOBAL_FUNCTION_DECL, /* global function definition, only in JSVarDef */ JS_VAR_GLOBAL_FUNCTION_DECL, /* global function definition, only in JSVarDef */
} JSVarKindEnum; } JSVarKindEnum;
/* XXX: could use a different structure in bytecode functions to save typedef struct JSBytecodeVarDef {
memory */
typedef struct JSVarDef {
JSAtom var_name; JSAtom var_name;
/* index into fd->scopes of this variable lexical scope */ /* index into JSFunctionBytecode.vars of the next variable in the same or
int scope_level; enclosing lexical scope
/* during compilation:
- if scope_level = 0: scope in which the variable is defined
- if scope_level != 0: index into fd->vars of the next
variable in the same or enclosing lexical scope
in a bytecode function:
index into fd->vars of the next
variable in the same or enclosing lexical scope
*/ */
int scope_next; int scope_next; /* XXX: store on 16 bits */
uint8_t is_const : 1; uint8_t is_const : 1;
uint8_t is_lexical : 1; uint8_t is_lexical : 1;
uint8_t is_captured : 1; uint8_t is_captured : 1; /* XXX: could remove and use a var_ref_idx value */
uint8_t is_static_private : 1; /* only used during private class field parsing */ uint8_t has_scope: 1; /* true if JSVarDef.scope_level != 0 */
uint8_t var_kind : 4; /* see JSVarKindEnum */ uint8_t var_kind : 4; /* see JSVarKindEnum */
/* only used during compilation: function pool index for lexical /* If is_captured = TRUE, provides, the index of the corresponding
variables with var_kind = JSVarRef on stack. It would be more compact to have a separate
JS_VAR_FUNCTION_DECL/JS_VAR_NEW_FUNCTION_DECL or scope level of table with the corresponding inverted table but it requires
the definition of the 'var' variables (they have scope_level = more modifications in the code. */
0) */ uint16_t var_ref_idx;
int func_pool_idx : 24; /* only used during compilation : index in } JSBytecodeVarDef;
the constant pool for hoisted function
definition */
} JSVarDef;
/* for the encoding of the pc2line table */ /* for the encoding of the pc2line table */
#define PC2LINE_BASE (-1) #define PC2LINE_BASE (-1)
@@ -657,12 +639,13 @@ typedef struct JSFunctionBytecode {
uint8_t *byte_code_buf; /* (self pointer) */ uint8_t *byte_code_buf; /* (self pointer) */
int byte_code_len; int byte_code_len;
JSAtom func_name; JSAtom func_name;
JSVarDef *vardefs; /* arguments + local variables (arg_count + var_count) (self pointer) */ JSBytecodeVarDef *vardefs; /* arguments + local variables (arg_count + var_count) (self pointer) */
JSClosureVar *closure_var; /* list of variables in the closure (self pointer) */ JSClosureVar *closure_var; /* list of variables in the closure (self pointer) */
uint16_t arg_count; uint16_t arg_count;
uint16_t var_count; uint16_t var_count;
uint16_t defined_arg_count; /* for length function property */ uint16_t defined_arg_count; /* for length function property */
uint16_t stack_size; /* maximum stack size */ uint16_t stack_size; /* maximum stack size */
uint16_t var_ref_count; /* number of local variable references */
JSContext *realm; /* function realm */ JSContext *realm; /* function realm */
JSValue *cpool; /* constant pool (self pointer) */ JSValue *cpool; /* constant pool (self pointer) */
int cpool_count; int cpool_count;
@@ -744,6 +727,7 @@ typedef struct JSAsyncFunctionState {
frame is no longer valid */ frame is no longer valid */
JSValue resolving_funcs[2]; /* only used in JS async functions */ JSValue resolving_funcs[2]; /* only used in JS async functions */
JSStackFrame frame; JSStackFrame frame;
/* arg_buf, var_buf, stack_buf and var_refs follow */
} JSAsyncFunctionState; } JSAsyncFunctionState;
typedef enum { typedef enum {
@@ -5721,9 +5705,13 @@ static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref)
if (var_ref->is_detached) { if (var_ref->is_detached) {
JS_FreeValueRT(rt, var_ref->value); JS_FreeValueRT(rt, var_ref->value);
} else { } else {
list_del(&var_ref->var_ref_link); /* still on the stack */ JSStackFrame *sf = var_ref->stack_frame;
if (var_ref->async_func) assert(sf->var_refs[var_ref->var_ref_idx] == var_ref);
async_func_free(rt, var_ref->async_func); sf->var_refs[var_ref->var_ref_idx] = NULL;
if (sf->js_mode & JS_MODE_ASYNC) {
JSAsyncFunctionState *async_func = container_of(sf, JSAsyncFunctionState, frame);
async_func_free(rt, async_func);
}
} }
remove_gc_object(&var_ref->header); remove_gc_object(&var_ref->header);
js_free_rt(rt, var_ref); js_free_rt(rt, var_ref);
@@ -6175,8 +6163,12 @@ static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp,
JSVarRef *var_ref = (JSVarRef *)gp; JSVarRef *var_ref = (JSVarRef *)gp;
if (var_ref->is_detached) { if (var_ref->is_detached) {
JS_MarkValue(rt, *var_ref->pvalue, mark_func); JS_MarkValue(rt, *var_ref->pvalue, mark_func);
} else if (var_ref->async_func) { } else {
mark_func(rt, &var_ref->async_func->header); JSStackFrame *sf = var_ref->stack_frame;
if (sf->js_mode & JS_MODE_ASYNC) {
JSAsyncFunctionState *async_func = container_of(sf, JSAsyncFunctionState, frame);
mark_func(rt, &async_func->header);
}
} }
} }
break; break;
@@ -16513,25 +16505,37 @@ static JSVarRef *js_create_var_ref(JSContext *ctx, BOOL is_lexical)
return var_ref; return var_ref;
} }
static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx,
int var_idx, BOOL is_arg) BOOL is_arg)
{ {
JSObject *p;
JSFunctionBytecode *b;
JSVarRef *var_ref; JSVarRef *var_ref;
struct list_head *el;
JSValue *pvalue; JSValue *pvalue;
int var_ref_idx;
JSBytecodeVarDef *vd;
if (is_arg) p = JS_VALUE_GET_OBJ(sf->cur_func);
b = p->u.func.function_bytecode;
if (is_arg) {
vd = &b->vardefs[var_idx];
pvalue = &sf->arg_buf[var_idx]; pvalue = &sf->arg_buf[var_idx];
else } else {
vd = &b->vardefs[b->arg_count + var_idx];
pvalue = &sf->var_buf[var_idx]; pvalue = &sf->var_buf[var_idx];
list_for_each(el, &sf->var_ref_list) {
var_ref = list_entry(el, JSVarRef, var_ref_link);
if (var_ref->pvalue == pvalue) {
var_ref->header.ref_count++;
return var_ref;
}
} }
assert(vd->is_captured);
var_ref_idx = vd->var_ref_idx;
assert(var_ref_idx < b->var_ref_count);
var_ref = sf->var_refs[var_ref_idx];
if (var_ref) {
/* reference to the already created local variable */
assert(var_ref->pvalue == pvalue);
var_ref->header.ref_count++;
return var_ref;
}
/* create a new one */ /* create a new one */
var_ref = js_malloc(ctx, sizeof(JSVarRef)); var_ref = js_malloc(ctx, sizeof(JSVarRef));
if (!var_ref) if (!var_ref)
@@ -16541,8 +16545,11 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf,
var_ref->is_detached = FALSE; var_ref->is_detached = FALSE;
var_ref->is_lexical = FALSE; var_ref->is_lexical = FALSE;
var_ref->is_const = FALSE; var_ref->is_const = FALSE;
list_add_tail(&var_ref->var_ref_link, &sf->var_ref_list); var_ref->var_ref_idx = var_ref_idx;
var_ref->stack_frame = sf;
sf->var_refs[var_ref_idx] = var_ref;
if (sf->js_mode & JS_MODE_ASYNC) { if (sf->js_mode & JS_MODE_ASYNC) {
JSAsyncFunctionState *async_func = container_of(sf, JSAsyncFunctionState, frame);
/* The stack frame is detached and may be destroyed at any /* The stack frame is detached and may be destroyed at any
time so its reference count must be increased. Calling time so its reference count must be increased. Calling
close_var_refs() when destroying the stack frame is not close_var_refs() when destroying the stack frame is not
@@ -16551,10 +16558,7 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf,
the JSVarRef of async functions during the GC. It would the JSVarRef of async functions during the GC. It would
have the advantage of allowing the release of unused stack have the advantage of allowing the release of unused stack
frames in a cycle. */ frames in a cycle. */
var_ref->async_func = container_of(sf, JSAsyncFunctionState, frame); async_func->header.ref_count++;
var_ref->async_func->header.ref_count++;
} else {
var_ref->async_func = NULL;
} }
var_ref->pvalue = pvalue; var_ref->pvalue = pvalue;
return var_ref; return var_ref;
@@ -17025,41 +17029,41 @@ static int js_op_define_class(JSContext *ctx, JSValue *sp,
return -1; return -1;
} }
static void close_var_refs(JSRuntime *rt, JSStackFrame *sf) static void close_var_ref(JSRuntime *rt, JSStackFrame *sf, JSVarRef *var_ref)
{ {
struct list_head *el, *el1; if (sf->js_mode & JS_MODE_ASYNC) {
JSVarRef *var_ref; JSAsyncFunctionState *async_func = container_of(sf, JSAsyncFunctionState, frame);
async_func_free(rt, async_func);
}
var_ref->value = JS_DupValueRT(rt, *var_ref->pvalue);
var_ref->pvalue = &var_ref->value;
/* the reference is no longer to a local variable */
var_ref->is_detached = TRUE;
}
list_for_each_safe(el, el1, &sf->var_ref_list) { static void close_var_refs(JSRuntime *rt, JSFunctionBytecode *b, JSStackFrame *sf)
var_ref = list_entry(el, JSVarRef, var_ref_link); {
/* no need to unlink var_ref->var_ref_link as the list is never used afterwards */ JSVarRef *var_ref;
if (var_ref->async_func) int i;
async_func_free(rt, var_ref->async_func);
var_ref->value = JS_DupValueRT(rt, *var_ref->pvalue); for(i = 0; i < b->var_ref_count; i++) {
var_ref->pvalue = &var_ref->value; var_ref = sf->var_refs[i];
/* the reference is no longer to a local variable */ if (var_ref)
var_ref->is_detached = TRUE; close_var_ref(rt, sf, var_ref);
} }
} }
static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int var_idx) static void close_lexical_var(JSContext *ctx, JSFunctionBytecode *b,
JSStackFrame *sf, int var_idx)
{ {
JSValue *pvalue;
struct list_head *el, *el1;
JSVarRef *var_ref; JSVarRef *var_ref;
int var_ref_idx;
pvalue = &sf->var_buf[var_idx]; var_ref_idx = b->vardefs[b->arg_count + var_idx].var_ref_idx;
list_for_each_safe(el, el1, &sf->var_ref_list) { var_ref = sf->var_refs[var_ref_idx];
var_ref = list_entry(el, JSVarRef, var_ref_link); if (var_ref) {
if (var_ref->pvalue == pvalue) { close_var_ref(ctx->rt, sf, var_ref);
list_del(&var_ref->var_ref_link); sf->var_refs[var_ref_idx] = NULL;
if (var_ref->async_func)
async_func_free(ctx->rt, var_ref->async_func);
var_ref->value = JS_DupValue(ctx, *var_ref->pvalue);
var_ref->pvalue = &var_ref->value;
/* the reference is no longer to a local variable */
var_ref->is_detached = TRUE;
}
} }
} }
@@ -17330,7 +17334,8 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
} }
alloca_size = sizeof(JSValue) * (arg_allocated_size + b->var_count + alloca_size = sizeof(JSValue) * (arg_allocated_size + b->var_count +
b->stack_size); b->stack_size) +
sizeof(JSVarRef *) * b->var_ref_count;
if (js_check_stack_overflow(rt, alloca_size)) if (js_check_stack_overflow(rt, alloca_size))
return JS_ThrowStackOverflow(caller_ctx); return JS_ThrowStackOverflow(caller_ctx);
@@ -17338,7 +17343,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
arg_buf = argv; arg_buf = argv;
sf->arg_count = argc; sf->arg_count = argc;
sf->cur_func = (JSValue)func_obj; sf->cur_func = (JSValue)func_obj;
init_list_head(&sf->var_ref_list);
var_refs = p->u.func.var_refs; var_refs = p->u.func.var_refs;
local_buf = alloca(alloca_size); local_buf = alloca(alloca_size);
@@ -17359,6 +17363,9 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
var_buf[i] = JS_UNDEFINED; var_buf[i] = JS_UNDEFINED;
stack_buf = var_buf + b->var_count; stack_buf = var_buf + b->var_count;
sf->var_refs = (JSVarRef **)(stack_buf + b->stack_size);
for(i = 0; i < b->var_ref_count; i++)
sf->var_refs[i] = NULL;
sp = stack_buf; sp = stack_buf;
pc = b->byte_code_buf; pc = b->byte_code_buf;
sf->prev_frame = rt->current_stack_frame; sf->prev_frame = rt->current_stack_frame;
@@ -18257,7 +18264,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
int idx; int idx;
idx = get_u16(pc); idx = get_u16(pc);
pc += 2; pc += 2;
close_lexical_var(ctx, sf, idx); close_lexical_var(ctx, b, sf, idx);
} }
BREAK; BREAK;
@@ -19988,9 +19995,9 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
sf->cur_sp = sp; sf->cur_sp = sp;
} else { } else {
done: done:
if (unlikely(!list_empty(&sf->var_ref_list))) { if (unlikely(b->var_ref_count != 0)) {
/* variable references reference the stack: must close them */ /* variable references reference the stack: must close them */
close_var_refs(rt, sf); close_var_refs(rt, b, sf);
} }
/* free the local variables and stack */ /* free the local variables and stack */
for(pval = local_buf; pval < sp; pval++) { for(pval = local_buf; pval < sp; pval++) {
@@ -20185,33 +20192,31 @@ static JSAsyncFunctionState *async_func_init(JSContext *ctx,
JSObject *p; JSObject *p;
JSFunctionBytecode *b; JSFunctionBytecode *b;
JSStackFrame *sf; JSStackFrame *sf;
int local_count, i, arg_buf_len, n; int i, arg_buf_len, n;
s = js_mallocz(ctx, sizeof(*s)); p = JS_VALUE_GET_OBJ(func_obj);
b = p->u.func.function_bytecode;
arg_buf_len = max_int(b->arg_count, argc);
s = js_malloc(ctx, sizeof(*s) + sizeof(JSValue) * (arg_buf_len + b->var_count + b->stack_size) + sizeof(JSVarRef *) * b->var_ref_count);
if (!s) if (!s)
return NULL; return NULL;
memset(s, 0, sizeof(*s));
s->header.ref_count = 1; s->header.ref_count = 1;
add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION); add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION);
sf = &s->frame; sf = &s->frame;
init_list_head(&sf->var_ref_list);
p = JS_VALUE_GET_OBJ(func_obj);
b = p->u.func.function_bytecode;
sf->js_mode = b->js_mode | JS_MODE_ASYNC; sf->js_mode = b->js_mode | JS_MODE_ASYNC;
sf->cur_pc = b->byte_code_buf; sf->cur_pc = b->byte_code_buf;
arg_buf_len = max_int(b->arg_count, argc); sf->arg_buf = (JSValue *)(s + 1);
local_count = arg_buf_len + b->var_count + b->stack_size;
sf->arg_buf = js_malloc(ctx, sizeof(JSValue) * max_int(local_count, 1));
if (!sf->arg_buf) {
js_free(ctx, s);
return NULL;
}
sf->cur_func = JS_DupValue(ctx, func_obj); sf->cur_func = JS_DupValue(ctx, func_obj);
s->this_val = JS_DupValue(ctx, this_obj); s->this_val = JS_DupValue(ctx, this_obj);
s->argc = argc; s->argc = argc;
sf->arg_count = arg_buf_len; sf->arg_count = arg_buf_len;
sf->var_buf = sf->arg_buf + arg_buf_len; sf->var_buf = sf->arg_buf + arg_buf_len;
sf->cur_sp = sf->var_buf + b->var_count; sf->cur_sp = sf->var_buf + b->var_count;
sf->var_refs = (JSVarRef **)(sf->cur_sp + b->stack_size);
for(i = 0; i < b->var_ref_count; i++)
sf->var_refs[i] = NULL;
for(i = 0; i < argc; i++) for(i = 0; i < argc; i++)
sf->arg_buf[i] = JS_DupValue(ctx, argv[i]); sf->arg_buf[i] = JS_DupValue(ctx, argv[i]);
n = arg_buf_len + b->var_count; n = arg_buf_len + b->var_count;
@@ -20228,14 +20233,10 @@ static void async_func_free_frame(JSRuntime *rt, JSAsyncFunctionState *s)
JSStackFrame *sf = &s->frame; JSStackFrame *sf = &s->frame;
JSValue *sp; JSValue *sp;
if (sf->arg_buf) { /* cannot free the function if it is running */
/* cannot free the function if it is running */ assert(sf->cur_sp != NULL);
assert(sf->cur_sp != NULL); for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) {
for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) { JS_FreeValueRT(rt, *sp);
JS_FreeValueRT(rt, *sp);
}
js_free_rt(rt, sf->arg_buf);
sf->arg_buf = NULL;
} }
JS_FreeValueRT(rt, sf->cur_func); JS_FreeValueRT(rt, sf->cur_func);
JS_FreeValueRT(rt, s->this_val); JS_FreeValueRT(rt, s->this_val);
@@ -20257,6 +20258,12 @@ static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s)
s->argc, sf->arg_buf, JS_CALL_FLAG_GENERATOR); s->argc, sf->arg_buf, JS_CALL_FLAG_GENERATOR);
} }
if (JS_IsException(ret) || JS_IsUndefined(ret)) { if (JS_IsException(ret) || JS_IsUndefined(ret)) {
JSObject *p;
JSFunctionBytecode *b;
p = JS_VALUE_GET_OBJ(sf->cur_func);
b = p->u.func.function_bytecode;
if (JS_IsUndefined(ret)) { if (JS_IsUndefined(ret)) {
ret = sf->cur_sp[-1]; ret = sf->cur_sp[-1];
sf->cur_sp[-1] = JS_UNDEFINED; sf->cur_sp[-1] = JS_UNDEFINED;
@@ -20265,7 +20272,7 @@ static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s)
s->is_completed = TRUE; s->is_completed = TRUE;
/* close the closure variables. */ /* close the closure variables. */
close_var_refs(rt, sf); close_var_refs(rt, b, sf);
async_func_free_frame(rt, s); async_func_free_frame(rt, s);
} }
@@ -21252,6 +21259,35 @@ typedef enum JSParseExportEnum {
JS_PARSE_EXPORT_DEFAULT, JS_PARSE_EXPORT_DEFAULT,
} JSParseExportEnum; } JSParseExportEnum;
typedef struct JSVarScope {
int parent; /* index into fd->scopes of the enclosing scope */
int first; /* index into fd->vars of the last variable in this scope */
} JSVarScope;
typedef struct JSVarDef {
JSAtom var_name;
/* index into fd->scopes of this variable lexical scope */
int scope_level;
/* - if scope_level = 0: scope in which the variable is defined
- if scope_level != 0: index into fd->vars of the next
variable in the same or enclosing lexical scope
*/
int scope_next;
uint8_t is_const : 1;
uint8_t is_lexical : 1;
uint8_t is_captured : 1; /* XXX: could remove and use a var_ref_idx value */
uint8_t is_static_private : 1; /* only used during private class field parsing */
uint8_t var_kind : 4; /* see JSVarKindEnum */
/* if is_captured = TRUE, provides, the index of the corresponding
JSVarRef on stack */
uint16_t var_ref_idx;
/* function pool index for lexical variables with var_kind =
JS_VAR_FUNCTION_DECL/JS_VAR_NEW_FUNCTION_DECL or scope level of
the definition of the 'var' variables (they have scope_level =
0) */
int func_pool_idx;
} JSVarDef;
typedef struct JSFunctionDef { typedef struct JSFunctionDef {
JSContext *ctx; JSContext *ctx;
struct JSFunctionDef *parent; struct JSFunctionDef *parent;
@@ -21295,6 +21331,7 @@ typedef struct JSFunctionDef {
int arg_size; /* allocated size for args[] */ int arg_size; /* allocated size for args[] */
int arg_count; /* number of arguments */ int arg_count; /* number of arguments */
int defined_arg_count; int defined_arg_count;
int var_ref_count; /* number of local/arg variable references */
int var_object_idx; /* -1 if none */ int var_object_idx; /* -1 if none */
int arg_var_object_idx; /* -1 if none (var object for the argument scope) */ int arg_var_object_idx; /* -1 if none (var object for the argument scope) */
int arguments_var_idx; /* -1 if none */ int arguments_var_idx; /* -1 if none */
@@ -31494,6 +31531,7 @@ static void print_lines(const char *source, int line, int line1) {
static void dump_byte_code(JSContext *ctx, int pass, static void dump_byte_code(JSContext *ctx, int pass,
const uint8_t *tab, int len, const uint8_t *tab, int len,
const JSBytecodeVarDef *vardefs,
const JSVarDef *args, int arg_count, const JSVarDef *args, int arg_count,
const JSVarDef *vars, int var_count, const JSVarDef *vars, int var_count,
const JSClosureVar *closure_var, int closure_var_count, const JSClosureVar *closure_var, int closure_var_count,
@@ -31730,7 +31768,7 @@ static void dump_byte_code(JSContext *ctx, int pass,
has_loc: has_loc:
printf(" %d: ", idx); printf(" %d: ", idx);
if (idx < var_count) { if (idx < var_count) {
print_atom(ctx, vars[idx].var_name); print_atom(ctx, vars ? vars[idx].var_name : vardefs[arg_count + idx].var_name);
} }
break; break;
case OP_FMT_none_arg: case OP_FMT_none_arg:
@@ -31741,7 +31779,7 @@ static void dump_byte_code(JSContext *ctx, int pass,
has_arg: has_arg:
printf(" %d: ", idx); printf(" %d: ", idx);
if (idx < arg_count) { if (idx < arg_count) {
print_atom(ctx, args[idx].var_name); print_atom(ctx, args ? args[idx].var_name : vardefs[idx].var_name);
} }
break; break;
case OP_FMT_none_var_ref: case OP_FMT_none_var_ref:
@@ -31861,7 +31899,7 @@ static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionB
if (b->var_count && b->vardefs) { if (b->var_count && b->vardefs) {
printf(" locals:\n"); printf(" locals:\n");
for(i = 0; i < b->var_count; i++) { for(i = 0; i < b->var_count; i++) {
JSVarDef *vd = &b->vardefs[b->arg_count + i]; JSBytecodeVarDef *vd = &b->vardefs[b->arg_count + i];
printf("%5d: %s %s", i, printf("%5d: %s %s", i,
vd->var_kind == JS_VAR_CATCH ? "catch" : vd->var_kind == JS_VAR_CATCH ? "catch" :
(vd->var_kind == JS_VAR_FUNCTION_DECL || (vd->var_kind == JS_VAR_FUNCTION_DECL ||
@@ -31869,8 +31907,8 @@ static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionB
vd->is_const ? "const" : vd->is_const ? "const" :
vd->is_lexical ? "let" : "var", vd->is_lexical ? "let" : "var",
JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), vd->var_name)); JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), vd->var_name));
if (vd->scope_level) if (vd->has_scope)
printf(" [level:%d next:%d]", vd->scope_level, vd->scope_next); printf(" [next:%d]", vd->scope_next);
printf("\n"); printf("\n");
} }
} }
@@ -31914,10 +31952,12 @@ static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionB
} }
} }
printf(" stack_size: %d\n", b->stack_size); printf(" stack_size: %d\n", b->stack_size);
printf(" var_ref_count: %d\n", b->var_ref_count);
printf(" opcodes:\n"); printf(" opcodes:\n");
dump_byte_code(ctx, 3, b->byte_code_buf, b->byte_code_len, dump_byte_code(ctx, 3, b->byte_code_buf, b->byte_code_len,
b->vardefs, b->arg_count, b->vardefs,
b->vardefs ? b->vardefs + b->arg_count : NULL, b->var_count, NULL, b->arg_count,
NULL, b->var_count,
b->closure_var, b->closure_var_count, b->closure_var, b->closure_var_count,
b->cpool, b->cpool_count, b->cpool, b->cpool_count,
b->has_debug ? b->debug.source : NULL, b->has_debug ? b->debug.source : NULL,
@@ -32143,6 +32183,14 @@ static void var_object_test(JSContext *ctx, JSFunctionDef *s,
s->jump_size++; s->jump_size++;
} }
static inline void capture_var(JSFunctionDef *s, JSVarDef *vd)
{
if (!vd->is_captured) {
vd->is_captured = 1;
vd->var_ref_idx = s->var_ref_count++;
}
}
/* return the position of the next opcode or -1 if error */ /* return the position of the next opcode or -1 if error */
static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s,
JSAtom var_name, int scope_level, int op, JSAtom var_name, int scope_level, int op,
@@ -32252,10 +32300,12 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s,
/* Create a dummy object with a named slot that is /* Create a dummy object with a named slot that is
a reference to the local variable */ a reference to the local variable */
if (var_idx & ARGUMENT_VAR_OFFSET) { if (var_idx & ARGUMENT_VAR_OFFSET) {
capture_var(s, &s->args[var_idx - ARGUMENT_VAR_OFFSET]);
dbuf_putc(bc, OP_make_arg_ref); dbuf_putc(bc, OP_make_arg_ref);
dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET); dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET);
} else { } else {
capture_var(s, &s->vars[var_idx]);
dbuf_putc(bc, OP_make_loc_ref); dbuf_putc(bc, OP_make_loc_ref);
dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
dbuf_put_u16(bc, var_idx); dbuf_put_u16(bc, var_idx);
@@ -32349,7 +32399,7 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s,
var_idx = idx; var_idx = idx;
break; break;
} else if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) { } else if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) {
vd->is_captured = 1; capture_var(fd, vd);
idx = get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL, 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) { if (idx >= 0) {
dbuf_putc(bc, OP_get_var_ref); dbuf_putc(bc, OP_get_var_ref);
@@ -32386,7 +32436,7 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s,
/* check eval object */ /* check eval object */
if (!is_arg_scope && fd->var_object_idx >= 0 && !is_pseudo_var) { if (!is_arg_scope && fd->var_object_idx >= 0 && !is_pseudo_var) {
vd = &fd->vars[fd->var_object_idx]; vd = &fd->vars[fd->var_object_idx];
vd->is_captured = 1; capture_var(fd, vd);
idx = get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL, idx = get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL,
fd->var_object_idx, vd->var_name, fd->var_object_idx, vd->var_name,
FALSE, FALSE, JS_VAR_NORMAL); FALSE, FALSE, JS_VAR_NORMAL);
@@ -32398,7 +32448,7 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s,
/* check eval object in argument scope */ /* check eval object in argument scope */
if (fd->arg_var_object_idx >= 0 && !is_pseudo_var) { if (fd->arg_var_object_idx >= 0 && !is_pseudo_var) {
vd = &fd->vars[fd->arg_var_object_idx]; vd = &fd->vars[fd->arg_var_object_idx];
vd->is_captured = 1; capture_var(fd, vd);
idx = get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL, idx = get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL,
fd->arg_var_object_idx, vd->var_name, fd->arg_var_object_idx, vd->var_name,
FALSE, FALSE, JS_VAR_NORMAL); FALSE, FALSE, JS_VAR_NORMAL);
@@ -32513,12 +32563,12 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s,
} else { } else {
/* find the corresponding closure variable */ /* find the corresponding closure variable */
if (var_idx & ARGUMENT_VAR_OFFSET) { if (var_idx & ARGUMENT_VAR_OFFSET) {
fd->args[var_idx - ARGUMENT_VAR_OFFSET].is_captured = 1; capture_var(fd, &fd->args[var_idx - ARGUMENT_VAR_OFFSET]);
idx = get_closure_var(ctx, s, fd, idx = get_closure_var(ctx, s, fd,
JS_CLOSURE_ARG, var_idx - ARGUMENT_VAR_OFFSET, JS_CLOSURE_ARG, var_idx - ARGUMENT_VAR_OFFSET,
var_name, FALSE, FALSE, JS_VAR_NORMAL); var_name, FALSE, FALSE, JS_VAR_NORMAL);
} else { } else {
fd->vars[var_idx].is_captured = 1; capture_var(fd, &fd->vars[var_idx]);
idx = get_closure_var(ctx, s, fd, idx = get_closure_var(ctx, s, fd,
JS_CLOSURE_LOCAL, var_idx, JS_CLOSURE_LOCAL, var_idx,
var_name, var_name,
@@ -32665,6 +32715,7 @@ static int resolve_scope_private_field1(JSContext *ctx,
if (idx >= 0) { if (idx >= 0) {
var_kind = fd->vars[idx].var_kind; var_kind = fd->vars[idx].var_kind;
if (is_ref) { if (is_ref) {
capture_var(fd, &fd->vars[idx]);
idx = get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL, idx, var_name, idx = get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL, idx, var_name,
TRUE, TRUE, JS_VAR_NORMAL); TRUE, TRUE, JS_VAR_NORMAL);
if (idx < 0) if (idx < 0)
@@ -32819,20 +32870,20 @@ static void mark_eval_captured_variables(JSContext *ctx, JSFunctionDef *s,
for (idx = s->scopes[scope_level].first; idx >= 0;) { for (idx = s->scopes[scope_level].first; idx >= 0;) {
vd = &s->vars[idx]; vd = &s->vars[idx];
vd->is_captured = 1; capture_var(s, vd);
idx = vd->scope_next; idx = vd->scope_next;
} }
} }
/* XXX: should handle the argument scope generically */ /* XXX: should handle the argument scope generically */
static BOOL is_var_in_arg_scope(const JSVarDef *vd) static BOOL is_var_in_arg_scope(JSAtom var_name, JSVarKindEnum var_kind)
{ {
return (vd->var_name == JS_ATOM_home_object || return (var_name == JS_ATOM_home_object ||
vd->var_name == JS_ATOM_this_active_func || var_name == JS_ATOM_this_active_func ||
vd->var_name == JS_ATOM_new_target || var_name == JS_ATOM_new_target ||
vd->var_name == JS_ATOM_this || var_name == JS_ATOM_this ||
vd->var_name == JS_ATOM__arg_var_ || var_name == JS_ATOM__arg_var_ ||
vd->var_kind == JS_VAR_FUNCTION_NAME); var_kind == JS_VAR_FUNCTION_NAME);
} }
static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) static void add_eval_variables(JSContext *ctx, JSFunctionDef *s)
@@ -32877,6 +32928,20 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s)
if (s->is_func_expr && s->func_name != JS_ATOM_NULL) if (s->is_func_expr && s->func_name != JS_ATOM_NULL)
add_func_var(ctx, s, s->func_name); add_func_var(ctx, s, s->func_name);
for(i = 0; i < s->arg_count; i++) {
vd = &s->args[i];
capture_var(s, vd);
}
for(i = 0; i < s->var_count; i++) {
vd = &s->vars[i];
/* do not close top level last result */
if (vd->scope_level == 0 &&
vd->var_name != JS_ATOM__ret_ &&
vd->var_name != JS_ATOM_NULL) {
capture_var(s, vd);
}
}
/* eval can use all the variables of the enclosing functions, so /* eval can use all the variables of the enclosing functions, so
they must be all put in the closure. The closure variables are they must be all put in the closure. The closure variables are
ordered by scope. It works only because no closure are created ordered by scope. It works only because no closure are created
@@ -32915,7 +32980,7 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s)
scope_idx = fd->scopes[scope_level].first; scope_idx = fd->scopes[scope_level].first;
while (scope_idx >= 0) { while (scope_idx >= 0) {
vd = &fd->vars[scope_idx]; vd = &fd->vars[scope_idx];
vd->is_captured = 1; capture_var(fd, vd);
get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL, 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); vd->var_name, vd->is_const, vd->is_lexical, vd->var_kind);
scope_idx = vd->scope_next; scope_idx = vd->scope_next;
@@ -32927,6 +32992,7 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s)
for(i = 0; i < fd->arg_count; i++) { for(i = 0; i < fd->arg_count; i++) {
vd = &fd->args[i]; vd = &fd->args[i];
if (vd->var_name != JS_ATOM_NULL) { if (vd->var_name != JS_ATOM_NULL) {
capture_var(fd, vd);
get_closure_var(ctx, s, fd, get_closure_var(ctx, s, fd,
JS_CLOSURE_ARG, i, vd->var_name, FALSE, JS_CLOSURE_ARG, i, vd->var_name, FALSE,
vd->is_lexical, JS_VAR_NORMAL); vd->is_lexical, JS_VAR_NORMAL);
@@ -32938,6 +33004,7 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s)
if (vd->scope_level == 0 && if (vd->scope_level == 0 &&
vd->var_name != JS_ATOM__ret_ && vd->var_name != JS_ATOM__ret_ &&
vd->var_name != JS_ATOM_NULL) { vd->var_name != JS_ATOM_NULL) {
capture_var(fd, vd);
get_closure_var(ctx, s, fd, get_closure_var(ctx, s, fd,
JS_CLOSURE_LOCAL, i, vd->var_name, FALSE, JS_CLOSURE_LOCAL, i, vd->var_name, FALSE,
vd->is_lexical, JS_VAR_NORMAL); vd->is_lexical, JS_VAR_NORMAL);
@@ -32947,7 +33014,8 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s)
for(i = 0; i < fd->var_count; i++) { for(i = 0; i < fd->var_count; i++) {
vd = &fd->vars[i]; vd = &fd->vars[i];
/* do not close top level last result */ /* do not close top level last result */
if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) { if (vd->scope_level == 0 && is_var_in_arg_scope(vd->var_name, vd->var_kind)) {
capture_var(fd, vd);
get_closure_var(ctx, s, fd, get_closure_var(ctx, s, fd,
JS_CLOSURE_LOCAL, i, vd->var_name, FALSE, JS_CLOSURE_LOCAL, i, vd->var_name, FALSE,
vd->is_lexical, JS_VAR_NORMAL); vd->is_lexical, JS_VAR_NORMAL);
@@ -32976,7 +33044,7 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s)
} }
static void set_closure_from_var(JSContext *ctx, JSClosureVar *cv, static void set_closure_from_var(JSContext *ctx, JSClosureVar *cv,
JSVarDef *vd, int var_idx) JSBytecodeVarDef *vd, int var_idx)
{ {
cv->closure_type = JS_CLOSURE_LOCAL; cv->closure_type = JS_CLOSURE_LOCAL;
cv->is_const = vd->is_const; cv->is_const = vd->is_const;
@@ -32992,7 +33060,7 @@ static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s,
JSFunctionBytecode *b, int scope_idx) JSFunctionBytecode *b, int scope_idx)
{ {
int i, count; int i, count;
JSVarDef *vd; JSBytecodeVarDef *vd;
BOOL is_arg_scope; BOOL is_arg_scope;
count = b->arg_count + b->var_count + b->closure_var_count; count = b->arg_count + b->var_count + b->closure_var_count;
@@ -33007,7 +33075,7 @@ static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s,
/* Add lexical variables in scope at the point of evaluation */ /* Add lexical variables in scope at the point of evaluation */
for (i = scope_idx; i >= 0;) { for (i = scope_idx; i >= 0;) {
vd = &b->vardefs[b->arg_count + i]; vd = &b->vardefs[b->arg_count + i];
if (vd->scope_level > 0) { if (vd->has_scope) {
JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
set_closure_from_var(ctx, cv, vd, i); set_closure_from_var(ctx, cv, vd, i);
} }
@@ -33029,7 +33097,7 @@ static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s,
/* Add local non lexical variables */ /* Add local non lexical variables */
for(i = 0; i < b->var_count; i++) { for(i = 0; i < b->var_count; i++) {
vd = &b->vardefs[b->arg_count + i]; vd = &b->vardefs[b->arg_count + i];
if (vd->scope_level == 0 && vd->var_name != JS_ATOM__ret_) { if (!vd->has_scope && vd->var_name != JS_ATOM__ret_) {
JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
set_closure_from_var(ctx, cv, vd, i); set_closure_from_var(ctx, cv, vd, i);
} }
@@ -33038,7 +33106,7 @@ static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s,
/* only add pseudo variables */ /* only add pseudo variables */
for(i = 0; i < b->var_count; i++) { for(i = 0; i < b->var_count; i++) {
vd = &b->vardefs[b->arg_count + i]; vd = &b->vardefs[b->arg_count + i];
if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) { if (!vd->has_scope && is_var_in_arg_scope(vd->var_name, vd->var_kind)) {
JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
set_closure_from_var(ctx, cv, vd, i); set_closure_from_var(ctx, cv, vd, i);
} }
@@ -34078,6 +34146,11 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
} else { } else {
dbuf_putc(&bc_out, OP_special_object); dbuf_putc(&bc_out, OP_special_object);
dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS); dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS);
/* the arguments are implicitly captured because
references to them are created with the 'argument'
object */
for(i = 0; i < s->arg_count; i++)
capture_var(s, &s->args[i]);
} }
if (s->arguments_arg_idx >= 0) if (s->arguments_arg_idx >= 0)
put_short_code(&bc_out, OP_set_loc, s->arguments_arg_idx); put_short_code(&bc_out, OP_set_loc, s->arguments_arg_idx);
@@ -35234,6 +35307,7 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
int stack_size, scope, idx; int stack_size, scope, idx;
int function_size, byte_code_offset, cpool_offset; int function_size, byte_code_offset, cpool_offset;
int closure_var_offset, vardefs_offset; int closure_var_offset, vardefs_offset;
BOOL strip_var_debug;
/* recompute scope linkage */ /* recompute scope linkage */
for (scope = 0; scope < fd->scope_count; scope++) { for (scope = 0; scope < fd->scope_count; scope++) {
@@ -35293,7 +35367,7 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
if (!fd->strip_debug) { if (!fd->strip_debug) {
printf("pass 1\n"); printf("pass 1\n");
dump_byte_code(ctx, 1, fd->byte_code.buf, fd->byte_code.size, dump_byte_code(ctx, 1, fd->byte_code.buf, fd->byte_code.size,
fd->args, fd->arg_count, fd->vars, fd->var_count, NULL, fd->args, fd->arg_count, fd->vars, fd->var_count,
fd->closure_var, fd->closure_var_count, fd->closure_var, fd->closure_var_count,
fd->cpool, fd->cpool_count, fd->source, fd->cpool, fd->cpool_count, fd->source,
fd->label_slots, NULL); fd->label_slots, NULL);
@@ -35308,7 +35382,7 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
if (!fd->strip_debug) { if (!fd->strip_debug) {
printf("pass 2\n"); printf("pass 2\n");
dump_byte_code(ctx, 2, fd->byte_code.buf, fd->byte_code.size, dump_byte_code(ctx, 2, fd->byte_code.buf, fd->byte_code.size,
fd->args, fd->arg_count, fd->vars, fd->var_count, NULL, fd->args, fd->arg_count, fd->vars, fd->var_count,
fd->closure_var, fd->closure_var_count, fd->closure_var, fd->closure_var_count,
fd->cpool, fd->cpool_count, fd->source, fd->cpool, fd->cpool_count, fd->source,
fd->label_slots, NULL); fd->label_slots, NULL);
@@ -35330,9 +35404,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) { 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;
function_size += fd->closure_var_count * sizeof(*fd->closure_var); function_size += fd->closure_var_count * sizeof(*fd->closure_var);
byte_code_offset = function_size; byte_code_offset = function_size;
@@ -35349,29 +35421,50 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
js_free(ctx, fd->byte_code.buf); js_free(ctx, fd->byte_code.buf);
fd->byte_code.buf = NULL; fd->byte_code.buf = NULL;
strip_var_debug = fd->strip_debug && !fd->has_eval_call; /* XXX: check */
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) { int i;
/* Strip variable definitions not needed at runtime */ b->vardefs = (void *)((uint8_t*)b + vardefs_offset);
int i; for(i = 0; i < fd->arg_count; i++) {
for(i = 0; i < fd->var_count; i++) { JSVarDef *vd = &fd->args[i];
JS_FreeAtom(ctx, fd->vars[i].var_name); JSBytecodeVarDef *vd1 = &b->vardefs[i];
if (strip_var_debug) {
JS_FreeAtom(ctx, vd->var_name);
vd1->var_name = JS_ATOM_NULL;
} else {
vd1->var_name = vd->var_name;
} }
for(i = 0; i < fd->arg_count; i++) { vd1->has_scope = (vd->scope_level != 0);
JS_FreeAtom(ctx, fd->args[i].var_name); vd1->scope_next = vd->scope_next;
vd1->is_const = vd->is_const;
vd1->is_lexical = vd->is_lexical;
vd1->is_captured = vd->is_captured;
vd1->var_kind = vd->var_kind;
vd1->var_ref_idx = vd->var_ref_idx;
}
for(i = 0; i < fd->var_count; i++) {
JSVarDef *vd = &fd->vars[i];
JSBytecodeVarDef *vd1 = &b->vardefs[i + fd->arg_count];
if (strip_var_debug) {
JS_FreeAtom(ctx, vd->var_name);
vd1->var_name = JS_ATOM_NULL;
} else {
vd1->var_name = vd->var_name;
} }
for(i = 0; i < fd->closure_var_count; i++) { vd1->has_scope = (vd->scope_level != 0);
JS_FreeAtom(ctx, fd->closure_var[i].var_name); vd1->scope_next = vd->scope_next;
fd->closure_var[i].var_name = JS_ATOM_NULL; vd1->is_const = vd->is_const;
} vd1->is_lexical = vd->is_lexical;
} else { vd1->is_captured = vd->is_captured;
b->vardefs = (void *)((uint8_t*)b + vardefs_offset); vd1->var_kind = vd->var_kind;
memcpy_no_ub(b->vardefs, fd->args, fd->arg_count * sizeof(fd->args[0])); vd1->var_ref_idx = vd->var_ref_idx;
memcpy_no_ub(b->vardefs + fd->arg_count, fd->vars, fd->var_count * sizeof(fd->vars[0]));
} }
b->var_count = fd->var_count; b->var_count = fd->var_count;
b->arg_count = fd->arg_count; b->arg_count = fd->arg_count;
b->defined_arg_count = fd->defined_arg_count; b->defined_arg_count = fd->defined_arg_count;
b->var_ref_count = fd->var_ref_count;
js_free(ctx, fd->args); js_free(ctx, fd->args);
js_free(ctx, fd->vars); js_free(ctx, fd->vars);
} }
@@ -35410,6 +35503,20 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
b->closure_var_count = fd->closure_var_count; b->closure_var_count = fd->closure_var_count;
if (b->closure_var_count) { if (b->closure_var_count) {
if (strip_var_debug) {
int i;
for(i = 0; i < fd->closure_var_count; i++) {
JSClosureVar *cv = &fd->closure_var[i];
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) {
JS_FreeAtom(ctx, cv->var_name);
cv->var_name = JS_ATOM_NULL;
}
}
}
b->closure_var = (void *)((uint8_t*)b + closure_var_offset); b->closure_var = (void *)((uint8_t*)b + closure_var_offset);
memcpy(b->closure_var, fd->closure_var, b->closure_var_count * sizeof(*b->closure_var)); memcpy(b->closure_var, fd->closure_var, b->closure_var_count * sizeof(*b->closure_var));
} }
@@ -37023,22 +37130,23 @@ static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj)
bc_put_leb128(s, b->var_count); bc_put_leb128(s, b->var_count);
bc_put_leb128(s, b->defined_arg_count); bc_put_leb128(s, b->defined_arg_count);
bc_put_leb128(s, b->stack_size); bc_put_leb128(s, b->stack_size);
bc_put_leb128(s, b->var_ref_count);
bc_put_leb128(s, b->closure_var_count); bc_put_leb128(s, b->closure_var_count);
bc_put_leb128(s, b->cpool_count); bc_put_leb128(s, b->cpool_count);
bc_put_leb128(s, b->byte_code_len); bc_put_leb128(s, b->byte_code_len);
if (b->vardefs) { if (b->vardefs) {
/* XXX: this field is redundant */
bc_put_leb128(s, b->arg_count + b->var_count); bc_put_leb128(s, b->arg_count + b->var_count);
for(i = 0; i < b->arg_count + b->var_count; i++) { for(i = 0; i < b->arg_count + b->var_count; i++) {
JSVarDef *vd = &b->vardefs[i]; JSBytecodeVarDef *vd = &b->vardefs[i];
bc_put_atom(s, vd->var_name); bc_put_atom(s, vd->var_name);
bc_put_leb128(s, vd->scope_level);
bc_put_leb128(s, vd->scope_next + 1); bc_put_leb128(s, vd->scope_next + 1);
bc_put_leb128(s, vd->var_ref_idx);
flags = idx = 0; flags = idx = 0;
bc_set_flags(&flags, &idx, vd->var_kind, 4); bc_set_flags(&flags, &idx, vd->var_kind, 4);
bc_set_flags(&flags, &idx, vd->is_const, 1); bc_set_flags(&flags, &idx, vd->is_const, 1);
bc_set_flags(&flags, &idx, vd->is_lexical, 1); bc_set_flags(&flags, &idx, vd->is_lexical, 1);
bc_set_flags(&flags, &idx, vd->is_captured, 1); bc_set_flags(&flags, &idx, vd->is_captured, 1);
bc_set_flags(&flags, &idx, vd->has_scope, 1);
assert(idx <= 8); assert(idx <= 8);
bc_put_u8(s, flags); bc_put_u8(s, flags);
} }
@@ -37916,6 +38024,8 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s)
goto fail; goto fail;
if (bc_get_leb128_u16(s, &bc.stack_size)) if (bc_get_leb128_u16(s, &bc.stack_size))
goto fail; goto fail;
if (bc_get_leb128_u16(s, &bc.var_ref_count))
goto fail;
if (bc_get_leb128_int(s, &bc.closure_var_count)) if (bc_get_leb128_int(s, &bc.closure_var_count))
goto fail; goto fail;
if (bc_get_leb128_int(s, &bc.cpool_count)) if (bc_get_leb128_int(s, &bc.cpool_count))
@@ -37973,14 +38083,14 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s)
if (local_count != 0) { if (local_count != 0) {
bc_read_trace(s, "vars {\n"); bc_read_trace(s, "vars {\n");
for(i = 0; i < local_count; i++) { for(i = 0; i < local_count; i++) {
JSVarDef *vd = &b->vardefs[i]; JSBytecodeVarDef *vd = &b->vardefs[i];
if (bc_get_atom(s, &vd->var_name)) if (bc_get_atom(s, &vd->var_name))
goto fail; goto fail;
if (bc_get_leb128_int(s, &vd->scope_level))
goto fail;
if (bc_get_leb128_int(s, &vd->scope_next)) if (bc_get_leb128_int(s, &vd->scope_next))
goto fail; goto fail;
vd->scope_next--; vd->scope_next--;
if (bc_get_leb128_u16(s, &vd->var_ref_idx))
goto fail;
if (bc_get_u8(s, &v8)) if (bc_get_u8(s, &v8))
goto fail; goto fail;
idx = 0; idx = 0;
@@ -37988,6 +38098,7 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s)
vd->is_const = bc_get_flags(v8, &idx, 1); vd->is_const = bc_get_flags(v8, &idx, 1);
vd->is_lexical = bc_get_flags(v8, &idx, 1); vd->is_lexical = bc_get_flags(v8, &idx, 1);
vd->is_captured = bc_get_flags(v8, &idx, 1); vd->is_captured = bc_get_flags(v8, &idx, 1);
vd->has_scope = bc_get_flags(v8, &idx, 1);
#ifdef DUMP_READ_OBJECT #ifdef DUMP_READ_OBJECT
bc_read_trace(s, "name: "); print_atom(s->ctx, vd->var_name); printf("\n"); bc_read_trace(s, "name: "); print_atom(s->ctx, vd->var_name); printf("\n");
#endif #endif