JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */
JSValue *arg_buf; /* arguments */
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
instruction after the call */
int arg_count;
union {
JSValue value; /* used when is_detached = TRUE */
struct {
- struct list_head var_ref_link; /* JSStackFrame.var_ref_list list */
- struct JSAsyncFunctionState *async_func; /* != NULL if async stack frame */
+ uint16_t var_ref_idx; /* index in JSStackFrame.var_refs[] */
+ JSStackFrame *stack_frame;
}; /* used when is_detached = FALSE */
};
} JSVarRef;
uint8_t is_lexical : 1; /* lexical variable */
uint8_t is_const : 1; /* const variable (is_lexical = 1 if is_const = 1 */
uint8_t var_kind : 4; /* see JSVarKindEnum */
- /* 8 bits available */
uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the
parent function. otherwise: index to a closure
variable of the parent function */
#define ARG_SCOPE_INDEX 1
#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 {
/* XXX: add more variable kinds here instead of using bit fields */
JS_VAR_NORMAL,
JS_VAR_GLOBAL_FUNCTION_DECL, /* global function definition, only in JSVarDef */
} JSVarKindEnum;
-/* XXX: could use a different structure in bytecode functions to save
- memory */
-typedef struct JSVarDef {
+typedef struct JSBytecodeVarDef {
JSAtom var_name;
- /* index into fd->scopes of this variable lexical scope */
- int scope_level;
- /* 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
+ /* index into JSFunctionBytecode.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_lexical : 1;
- uint8_t is_captured : 1;
- uint8_t is_static_private : 1; /* only used during private class field parsing */
+ uint8_t is_captured : 1; /* XXX: could remove and use a var_ref_idx value */
+ uint8_t has_scope: 1; /* true if JSVarDef.scope_level != 0 */
uint8_t var_kind : 4; /* see JSVarKindEnum */
- /* only used during compilation: 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 : 24; /* only used during compilation : index in
- the constant pool for hoisted function
- definition */
-} JSVarDef;
+ /* If is_captured = TRUE, provides, the index of the corresponding
+ JSVarRef on stack. It would be more compact to have a separate
+ table with the corresponding inverted table but it requires
+ more modifications in the code. */
+ uint16_t var_ref_idx;
+} JSBytecodeVarDef;
/* for the encoding of the pc2line table */
#define PC2LINE_BASE (-1)
uint8_t *byte_code_buf; /* (self pointer) */
int byte_code_len;
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) */
uint16_t arg_count;
uint16_t var_count;
uint16_t defined_arg_count; /* for length function property */
uint16_t stack_size; /* maximum stack size */
+ uint16_t var_ref_count; /* number of local variable references */
JSContext *realm; /* function realm */
JSValue *cpool; /* constant pool (self pointer) */
int cpool_count;
frame is no longer valid */
JSValue resolving_funcs[2]; /* only used in JS async functions */
JSStackFrame frame;
+ /* arg_buf, var_buf, stack_buf and var_refs follow */
} JSAsyncFunctionState;
typedef enum {
if (var_ref->is_detached) {
JS_FreeValueRT(rt, var_ref->value);
} else {
- list_del(&var_ref->var_ref_link); /* still on the stack */
- if (var_ref->async_func)
- async_func_free(rt, var_ref->async_func);
+ JSStackFrame *sf = var_ref->stack_frame;
+ assert(sf->var_refs[var_ref->var_ref_idx] == var_ref);
+ 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);
js_free_rt(rt, var_ref);
JSVarRef *var_ref = (JSVarRef *)gp;
if (var_ref->is_detached) {
JS_MarkValue(rt, *var_ref->pvalue, mark_func);
- } else if (var_ref->async_func) {
- mark_func(rt, &var_ref->async_func->header);
+ } else {
+ 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;
return var_ref;
}
-static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf,
- int var_idx, BOOL is_arg)
+static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx,
+ BOOL is_arg)
{
+ JSObject *p;
+ JSFunctionBytecode *b;
JSVarRef *var_ref;
- struct list_head *el;
JSValue *pvalue;
-
- if (is_arg)
+ int var_ref_idx;
+ JSBytecodeVarDef *vd;
+
+ 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];
- else
+ } else {
+ vd = &b->vardefs[b->arg_count + 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 */
var_ref = js_malloc(ctx, sizeof(JSVarRef));
if (!var_ref)
var_ref->is_detached = FALSE;
var_ref->is_lexical = FALSE;
var_ref->is_const = FALSE;
- list_add_tail(&var_ref->var_ref_link, &sf->var_ref_list);
+ 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) {
+ JSAsyncFunctionState *async_func = container_of(sf, JSAsyncFunctionState, frame);
/* The stack frame is detached and may be destroyed at any
time so its reference count must be increased. Calling
close_var_refs() when destroying the stack frame is not
the JSVarRef of async functions during the GC. It would
have the advantage of allowing the release of unused stack
frames in a cycle. */
- var_ref->async_func = container_of(sf, JSAsyncFunctionState, frame);
- var_ref->async_func->header.ref_count++;
- } else {
- var_ref->async_func = NULL;
+ async_func->header.ref_count++;
}
var_ref->pvalue = pvalue;
return var_ref;
return -1;
}
-static void close_var_refs(JSRuntime *rt, JSStackFrame *sf)
+static void close_var_ref(JSRuntime *rt, JSStackFrame *sf, JSVarRef *var_ref)
+{
+ if (sf->js_mode & JS_MODE_ASYNC) {
+ 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;
+}
+
+static void close_var_refs(JSRuntime *rt, JSFunctionBytecode *b, JSStackFrame *sf)
{
- struct list_head *el, *el1;
JSVarRef *var_ref;
+ int i;
- list_for_each_safe(el, el1, &sf->var_ref_list) {
- 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 */
- if (var_ref->async_func)
- async_func_free(rt, var_ref->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;
+ for(i = 0; i < b->var_ref_count; i++) {
+ var_ref = sf->var_refs[i];
+ if (var_ref)
+ 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;
-
- pvalue = &sf->var_buf[var_idx];
- list_for_each_safe(el, el1, &sf->var_ref_list) {
- var_ref = list_entry(el, JSVarRef, var_ref_link);
- if (var_ref->pvalue == pvalue) {
- list_del(&var_ref->var_ref_link);
- 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;
- }
+ int var_ref_idx;
+
+ var_ref_idx = b->vardefs[b->arg_count + var_idx].var_ref_idx;
+ var_ref = sf->var_refs[var_ref_idx];
+ if (var_ref) {
+ close_var_ref(ctx->rt, sf, var_ref);
+ sf->var_refs[var_ref_idx] = NULL;
}
}
}
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))
return JS_ThrowStackOverflow(caller_ctx);
arg_buf = argv;
sf->arg_count = argc;
sf->cur_func = (JSValue)func_obj;
- init_list_head(&sf->var_ref_list);
var_refs = p->u.func.var_refs;
local_buf = alloca(alloca_size);
var_buf[i] = JS_UNDEFINED;
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;
pc = b->byte_code_buf;
sf->prev_frame = rt->current_stack_frame;
int idx;
idx = get_u16(pc);
pc += 2;
- close_lexical_var(ctx, sf, idx);
+ close_lexical_var(ctx, b, sf, idx);
}
BREAK;
sf->cur_sp = sp;
} else {
done:
- if (unlikely(!list_empty(&sf->var_ref_list))) {
+ if (unlikely(b->var_ref_count != 0)) {
/* 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 */
for(pval = local_buf; pval < sp; pval++) {
JSObject *p;
JSFunctionBytecode *b;
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)
return NULL;
+ memset(s, 0, sizeof(*s));
s->header.ref_count = 1;
add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION);
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->cur_pc = b->byte_code_buf;
- arg_buf_len = max_int(b->arg_count, argc);
- 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->arg_buf = (JSValue *)(s + 1);
sf->cur_func = JS_DupValue(ctx, func_obj);
s->this_val = JS_DupValue(ctx, this_obj);
s->argc = argc;
sf->arg_count = arg_buf_len;
sf->var_buf = sf->arg_buf + arg_buf_len;
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++)
sf->arg_buf[i] = JS_DupValue(ctx, argv[i]);
n = arg_buf_len + b->var_count;
JSStackFrame *sf = &s->frame;
JSValue *sp;
- if (sf->arg_buf) {
- /* cannot free the function if it is running */
- assert(sf->cur_sp != NULL);
- for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) {
- JS_FreeValueRT(rt, *sp);
- }
- js_free_rt(rt, sf->arg_buf);
- sf->arg_buf = NULL;
+ /* cannot free the function if it is running */
+ assert(sf->cur_sp != NULL);
+ for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) {
+ JS_FreeValueRT(rt, *sp);
}
JS_FreeValueRT(rt, sf->cur_func);
JS_FreeValueRT(rt, s->this_val);
s->argc, sf->arg_buf, JS_CALL_FLAG_GENERATOR);
}
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)) {
ret = sf->cur_sp[-1];
sf->cur_sp[-1] = JS_UNDEFINED;
s->is_completed = TRUE;
/* close the closure variables. */
- close_var_refs(rt, sf);
-
+ close_var_refs(rt, b, sf);
+
async_func_free_frame(rt, s);
}
return ret;
JS_PARSE_EXPORT_DEFAULT,
} 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 {
JSContext *ctx;
struct JSFunctionDef *parent;
int arg_size; /* allocated size for args[] */
int arg_count; /* number of arguments */
int defined_arg_count;
+ int var_ref_count; /* number of local/arg variable references */
int var_object_idx; /* -1 if none */
int arg_var_object_idx; /* -1 if none (var object for the argument scope) */
int arguments_var_idx; /* -1 if none */
static void dump_byte_code(JSContext *ctx, int pass,
const uint8_t *tab, int len,
+ const JSBytecodeVarDef *vardefs,
const JSVarDef *args, int arg_count,
const JSVarDef *vars, int var_count,
const JSClosureVar *closure_var, int closure_var_count,
has_loc:
printf(" %d: ", idx);
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;
case OP_FMT_none_arg:
has_arg:
printf(" %d: ", idx);
if (idx < arg_count) {
- print_atom(ctx, args[idx].var_name);
+ print_atom(ctx, args ? args[idx].var_name : vardefs[idx].var_name);
}
break;
case OP_FMT_none_var_ref:
if (b->var_count && b->vardefs) {
printf(" locals:\n");
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,
vd->var_kind == JS_VAR_CATCH ? "catch" :
(vd->var_kind == JS_VAR_FUNCTION_DECL ||
vd->is_const ? "const" :
vd->is_lexical ? "let" : "var",
JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), vd->var_name));
- if (vd->scope_level)
- printf(" [level:%d next:%d]", vd->scope_level, vd->scope_next);
+ if (vd->has_scope)
+ printf(" [next:%d]", vd->scope_next);
printf("\n");
}
}
}
}
printf(" stack_size: %d\n", b->stack_size);
+ printf(" var_ref_count: %d\n", b->var_ref_count);
printf(" opcodes:\n");
dump_byte_code(ctx, 3, b->byte_code_buf, b->byte_code_len,
- b->vardefs, b->arg_count,
- b->vardefs ? b->vardefs + b->arg_count : NULL, b->var_count,
+ b->vardefs,
+ NULL, b->arg_count,
+ NULL, b->var_count,
b->closure_var, b->closure_var_count,
b->cpool, b->cpool_count,
b->has_debug ? b->debug.source : NULL,
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 */
static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s,
JSAtom var_name, int scope_level, int op,
/* Create a dummy object with a named slot that is
a reference to the local variable */
if (var_idx & ARGUMENT_VAR_OFFSET) {
+ capture_var(s, &s->args[var_idx - ARGUMENT_VAR_OFFSET]);
dbuf_putc(bc, OP_make_arg_ref);
dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET);
} else {
+ capture_var(s, &s->vars[var_idx]);
dbuf_putc(bc, OP_make_loc_ref);
dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
dbuf_put_u16(bc, var_idx);
var_idx = idx;
break;
} 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);
if (idx >= 0) {
dbuf_putc(bc, OP_get_var_ref);
/* check eval object */
if (!is_arg_scope && fd->var_object_idx >= 0 && !is_pseudo_var) {
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,
fd->var_object_idx, vd->var_name,
FALSE, FALSE, JS_VAR_NORMAL);
/* check eval object in argument scope */
if (fd->arg_var_object_idx >= 0 && !is_pseudo_var) {
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,
fd->arg_var_object_idx, vd->var_name,
FALSE, FALSE, JS_VAR_NORMAL);
} else {
/* find the corresponding closure variable */
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,
JS_CLOSURE_ARG, var_idx - ARGUMENT_VAR_OFFSET,
var_name, FALSE, FALSE, JS_VAR_NORMAL);
} else {
- fd->vars[var_idx].is_captured = 1;
+ capture_var(fd, &fd->vars[var_idx]);
idx = get_closure_var(ctx, s, fd,
JS_CLOSURE_LOCAL, var_idx,
var_name,
if (idx >= 0) {
var_kind = fd->vars[idx].var_kind;
if (is_ref) {
+ capture_var(fd, &fd->vars[idx]);
idx = get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL, idx, var_name,
TRUE, TRUE, JS_VAR_NORMAL);
if (idx < 0)
for (idx = s->scopes[scope_level].first; idx >= 0;) {
vd = &s->vars[idx];
- vd->is_captured = 1;
+ capture_var(s, vd);
idx = vd->scope_next;
}
}
/* 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 ||
- vd->var_name == JS_ATOM_this_active_func ||
- vd->var_name == JS_ATOM_new_target ||
- vd->var_name == JS_ATOM_this ||
- vd->var_name == JS_ATOM__arg_var_ ||
- vd->var_kind == JS_VAR_FUNCTION_NAME);
+ return (var_name == JS_ATOM_home_object ||
+ var_name == JS_ATOM_this_active_func ||
+ var_name == JS_ATOM_new_target ||
+ var_name == JS_ATOM_this ||
+ var_name == JS_ATOM__arg_var_ ||
+ var_kind == JS_VAR_FUNCTION_NAME);
}
static void add_eval_variables(JSContext *ctx, JSFunctionDef *s)
if (s->is_func_expr && s->func_name != JS_ATOM_NULL)
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
they must be all put in the closure. The closure variables are
ordered by scope. It works only because no closure are created
scope_idx = fd->scopes[scope_level].first;
while (scope_idx >= 0) {
vd = &fd->vars[scope_idx];
- vd->is_captured = 1;
+ capture_var(fd, vd);
get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL, scope_idx,
vd->var_name, vd->is_const, vd->is_lexical, vd->var_kind);
scope_idx = vd->scope_next;
for(i = 0; i < fd->arg_count; i++) {
vd = &fd->args[i];
if (vd->var_name != JS_ATOM_NULL) {
+ capture_var(fd, vd);
get_closure_var(ctx, s, fd,
JS_CLOSURE_ARG, i, vd->var_name, FALSE,
vd->is_lexical, JS_VAR_NORMAL);
if (vd->scope_level == 0 &&
vd->var_name != JS_ATOM__ret_ &&
vd->var_name != JS_ATOM_NULL) {
+ capture_var(fd, vd);
get_closure_var(ctx, s, fd,
JS_CLOSURE_LOCAL, 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 && 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,
JS_CLOSURE_LOCAL, i, vd->var_name, FALSE,
vd->is_lexical, JS_VAR_NORMAL);
}
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->is_const = vd->is_const;
JSFunctionBytecode *b, int scope_idx)
{
int i, count;
- JSVarDef *vd;
+ JSBytecodeVarDef *vd;
BOOL is_arg_scope;
count = b->arg_count + b->var_count + b->closure_var_count;
/* 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) {
+ if (vd->has_scope) {
JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
set_closure_from_var(ctx, cv, vd, i);
}
/* 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 && 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++];
set_closure_from_var(ctx, cv, vd, i);
}
/* 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)) {
+ if (!vd->has_scope && is_var_in_arg_scope(vd->var_name, vd->var_kind)) {
JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
set_closure_from_var(ctx, cv, vd, i);
}
} else {
dbuf_putc(&bc_out, OP_special_object);
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)
put_short_code(&bc_out, OP_set_loc, s->arguments_arg_idx);
int stack_size, scope, idx;
int function_size, byte_code_offset, cpool_offset;
int closure_var_offset, vardefs_offset;
-
+ BOOL strip_var_debug;
+
/* recompute scope linkage */
for (scope = 0; scope < fd->scope_count; scope++) {
fd->scopes[scope].first = -1;
if (!fd->strip_debug) {
printf("pass 1\n");
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->cpool, fd->cpool_count, fd->source,
fd->label_slots, NULL);
if (!fd->strip_debug) {
printf("pass 2\n");
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->cpool, fd->cpool_count, fd->source,
fd->label_slots, NULL);
cpool_offset = function_size;
function_size += fd->cpool_count * sizeof(*fd->cpool);
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;
function_size += fd->closure_var_count * sizeof(*fd->closure_var);
byte_code_offset = function_size;
js_free(ctx, fd->byte_code.buf);
fd->byte_code.buf = NULL;
+ strip_var_debug = fd->strip_debug && !fd->has_eval_call; /* XXX: check */
b->func_name = fd->func_name;
if (fd->arg_count + fd->var_count > 0) {
- if (fd->strip_debug && !fd->has_eval_call) {
- /* Strip variable definitions not needed at runtime */
- int i;
- for(i = 0; i < fd->var_count; i++) {
- JS_FreeAtom(ctx, fd->vars[i].var_name);
- }
- for(i = 0; i < fd->arg_count; i++) {
- JS_FreeAtom(ctx, fd->args[i].var_name);
+ int i;
+ b->vardefs = (void *)((uint8_t*)b + vardefs_offset);
+ for(i = 0; i < fd->arg_count; i++) {
+ JSVarDef *vd = &fd->args[i];
+ 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->closure_var_count; i++) {
- JS_FreeAtom(ctx, fd->closure_var[i].var_name);
- fd->closure_var[i].var_name = JS_ATOM_NULL;
+ vd1->has_scope = (vd->scope_level != 0);
+ 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;
}
- } else {
- b->vardefs = (void *)((uint8_t*)b + vardefs_offset);
- memcpy_no_ub(b->vardefs, fd->args, fd->arg_count * sizeof(fd->args[0]));
- memcpy_no_ub(b->vardefs + fd->arg_count, fd->vars, fd->var_count * sizeof(fd->vars[0]));
+ vd1->has_scope = (vd->scope_level != 0);
+ 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;
}
b->var_count = fd->var_count;
b->arg_count = fd->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->vars);
}
b->closure_var_count = fd->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);
memcpy(b->closure_var, fd->closure_var, b->closure_var_count * sizeof(*b->closure_var));
}
bc_put_leb128(s, b->var_count);
bc_put_leb128(s, b->defined_arg_count);
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->cpool_count);
bc_put_leb128(s, b->byte_code_len);
if (b->vardefs) {
- /* XXX: this field is redundant */
bc_put_leb128(s, b->arg_count + b->var_count);
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_leb128(s, vd->scope_level);
bc_put_leb128(s, vd->scope_next + 1);
+ bc_put_leb128(s, vd->var_ref_idx);
flags = idx = 0;
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_lexical, 1);
bc_set_flags(&flags, &idx, vd->is_captured, 1);
+ bc_set_flags(&flags, &idx, vd->has_scope, 1);
assert(idx <= 8);
bc_put_u8(s, flags);
}
goto fail;
if (bc_get_leb128_u16(s, &bc.stack_size))
goto fail;
+ if (bc_get_leb128_u16(s, &bc.var_ref_count))
+ goto fail;
if (bc_get_leb128_int(s, &bc.closure_var_count))
goto fail;
if (bc_get_leb128_int(s, &bc.cpool_count))
if (local_count != 0) {
bc_read_trace(s, "vars {\n");
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))
goto fail;
- if (bc_get_leb128_int(s, &vd->scope_level))
- goto fail;
if (bc_get_leb128_int(s, &vd->scope_next))
goto fail;
vd->scope_next--;
+ if (bc_get_leb128_u16(s, &vd->var_ref_idx))
+ goto fail;
if (bc_get_u8(s, &v8))
goto fail;
idx = 0;
vd->is_const = 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->has_scope = bc_get_flags(v8, &idx, 1);
#ifdef DUMP_READ_OBJECT
bc_read_trace(s, "name: "); print_atom(s->ctx, vd->var_name); printf("\n");
#endif