From: Gabriel Ivăncescu gabrielopcode@gmail.com
Rather than using the variable obj for it, unless necessary. Since it's an implementation detail, the scope's dispex object accesses them using index props (using same indices as the buffer's).
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/compile.c | 5 +- dlls/jscript/engine.c | 229 ++++++++++++++++++++++++++++++++++++---- dlls/jscript/engine.h | 10 ++ dlls/jscript/function.c | 42 +++----- 4 files changed, 234 insertions(+), 52 deletions(-)
diff --git a/dlls/jscript/compile.c b/dlls/jscript/compile.c index e2690935212..8ccd7cfaae1 100644 --- a/dlls/jscript/compile.c +++ b/dlls/jscript/compile.c @@ -2517,9 +2517,9 @@ static HRESULT init_code(compiler_ctx_t *compiler, const WCHAR *source, UINT64 s static HRESULT compile_function(compiler_ctx_t *ctx, statement_t *source, function_expression_t *func_expr, BOOL from_eval, function_code_t *func) { + unsigned off, i, scope, scope_var_offs; function_expression_t *iter; function_local_t *local; - unsigned off, i, scope; HRESULT hres;
TRACE("\n"); @@ -2594,15 +2594,18 @@ static HRESULT compile_function(compiler_ctx_t *ctx, statement_t *source, functi func->local_scopes[scope].locals_cnt = ctx->local_scopes[scope].locals_cnt;
i = 0; + scope_var_offs = 0; WINE_RB_FOR_EACH_ENTRY(local, &ctx->local_scopes[scope].locals, function_local_t, entry) { func->local_scopes[scope].locals[i].name = local->name; func->local_scopes[scope].locals[i].ref = local->ref; if(local->ref >= 0) { func->variables[local->ref].name = local->name; + func->variables[local->ref].scope_offs = scope_var_offs++; func->variables[local->ref].func_id = -1; } i++; } + func->local_scopes[scope].vars_cnt = scope_var_offs; assert(i == ctx->local_scopes[scope].locals_cnt); }
diff --git a/dlls/jscript/engine.c b/dlls/jscript/engine.c index 0b044076676..7149293c170 100644 --- a/dlls/jscript/engine.c +++ b/dlls/jscript/engine.c @@ -162,6 +162,31 @@ static inline BSTR local_name(call_frame_t *frame, int ref) return ref < 0 ? frame->function->params[-ref-1] : frame->function->variables[ref].name; }
+static jsval_t *get_detached_local_ref(scope_chain_t *scope, const WCHAR *name) +{ + struct locals_buffer *detached_locals = scope->detached_locals; + function_code_t *func; + local_ref_t *ref; + + if(!detached_locals) + return NULL; + func = detached_locals->func_code; + ref = lookup_local(func, name, scope->scope_index); + return ref ? &detached_locals->local[(ref->ref < 0) ? -ref->ref - 1 : detached_locals->var_offs + func->variables[ref->ref].scope_offs] : NULL; +} + +static HRESULT get_detached_local_dispid(scope_chain_t *scope, const WCHAR *name, DISPID *id) +{ + jsval_t *local_ref = get_detached_local_ref(scope, name); + WCHAR name_idx[11]; + + if(!local_ref) + return DISP_E_UNKNOWNNAME; + + swprintf(name_idx, ARRAY_SIZE(name_idx), L"%u", local_ref - scope->detached_locals->local); + return jsdisp_get_id(&scope->dispex, name_idx, 0, id); +} + /* Steals input reference even on failure. */ static HRESULT stack_push_exprval(script_ctx_t *ctx, exprval_t *val) { @@ -228,6 +253,18 @@ static BOOL stack_topn_exprval(script_ctx_t *ctx, unsigned n, exprval_t *r)
while (1) { + hres = get_detached_local_dispid(scope, name, &id); + if (hres != DISP_E_UNKNOWNNAME) + { + if (FAILED(hres)) + { + r->type = EXPRVAL_INVALID; + r->u.hres = hres; + return FALSE; + } + jsobj = &scope->dispex; + break; + } if ((jsobj = to_jsdisp(scope->obj)) && SUCCEEDED(hres = jsdisp_get_id(jsobj, name, 0, &id))) break; if (scope == frame->base_scope) @@ -423,9 +460,24 @@ static inline void clear_acc(script_ctx_t *ctx) ctx->acc = jsval_undefined(); }
+static inline scope_chain_t *scope_from_dispex(jsdisp_t *dispex) +{ + return CONTAINING_RECORD(dispex, scope_chain_t, dispex); +} + static void scope_destructor(jsdisp_t *dispex) { - scope_chain_t *scope = CONTAINING_RECORD(dispex, scope_chain_t, dispex); + scope_chain_t *scope = scope_from_dispex(dispex); + + if(scope->detached_locals) { + struct locals_buffer *locals = scope->detached_locals; + unsigned i, cnt = locals->local_cnt; + + release_bytecode(locals->func_code->bytecode); + for(i = 0; i < cnt; i++) + jsval_release(locals->local[i]); + free(locals); + }
if(scope->next) scope_release(scope->next); @@ -435,12 +487,53 @@ static void scope_destructor(jsdisp_t *dispex) free(scope); }
+static unsigned scope_idx_length(jsdisp_t *dispex) +{ + scope_chain_t *scope = scope_from_dispex(dispex); + + return scope->detached_locals->local_cnt; +} + +static HRESULT scope_idx_get(jsdisp_t *dispex, unsigned idx, jsval_t *r) +{ + scope_chain_t *scope = scope_from_dispex(dispex); + + return jsval_copy(scope->detached_locals->local[idx], r); +} + +static HRESULT scope_idx_put(jsdisp_t *dispex, unsigned idx, jsval_t val) +{ + scope_chain_t *scope = scope_from_dispex(dispex); + jsval_t copy, *ref; + HRESULT hres; + + hres = jsval_copy(val, ©); + if(FAILED(hres)) + return hres; + + ref = &scope->detached_locals->local[idx]; + jsval_release(*ref); + *ref = copy; + return S_OK; +} + static HRESULT scope_gc_traverse(struct gc_ctx *gc_ctx, enum gc_traverse_op op, jsdisp_t *dispex) { - scope_chain_t *scope = CONTAINING_RECORD(dispex, scope_chain_t, dispex); + scope_chain_t *scope = scope_from_dispex(dispex); jsdisp_t *jsobj; HRESULT hres;
+ if(scope->detached_locals) { + struct locals_buffer *locals = scope->detached_locals; + unsigned i, cnt = locals->local_cnt; + + for(i = 0; i < cnt; i++) { + hres = gc_process_linked_val(gc_ctx, op, dispex, &locals->local[i]); + if(FAILED(hres)) + return hres; + } + } + if(scope->next) { hres = gc_process_linked_obj(gc_ctx, op, dispex, &scope->next->dispex, (void**)&scope->next); if(FAILED(hres)) @@ -468,9 +561,9 @@ static const builtin_info_t scope_info = { NULL, scope_destructor, NULL, - NULL, - NULL, - NULL, + scope_idx_length, + scope_idx_get, + scope_idx_put, scope_gc_traverse };
@@ -624,10 +717,42 @@ HRESULT jsval_strict_equal(jsval_t lval, jsval_t rval, BOOL *ret) return S_OK; }
+static HRESULT alloc_detached_locals(script_ctx_t *ctx, call_frame_t *frame, scope_chain_t *scope) +{ + function_code_t *func = frame->function; + unsigned i, argc, local_cnt; + jsval_t *detached_local; + HRESULT hres; + + argc = (scope == frame->base_scope) ? max(frame->argc, func->param_cnt) : 0; + local_cnt = argc + func->local_scopes[scope->scope_index].vars_cnt; + + if(!(scope->detached_locals = malloc(FIELD_OFFSET(struct locals_buffer, local[local_cnt])))) + return E_OUTOFMEMORY; + detached_local = scope->detached_locals->local; + scope->detached_locals->local_cnt = local_cnt; + scope->detached_locals->var_offs = argc; + scope->detached_locals->func_code = func; + bytecode_addref(func->bytecode); + + for(i = argc; i < local_cnt; i++) + detached_local[i] = jsval_undefined(); + for(i = 0; i < argc; i++) { + hres = jsval_copy(ctx->stack[frame->arguments_off + i], &detached_local[i]); + if(FAILED(hres)) { + do detached_local[i++] = jsval_undefined(); while(i < argc); + return hres; + } + } + + return S_OK; +} + static HRESULT detach_scope(script_ctx_t *ctx, call_frame_t *frame, scope_chain_t *scope) { function_code_t *func = frame->function; - unsigned int i, index; + unsigned int i, index, var_offs; + jsval_t *detached_local; jsdisp_t *jsobj; HRESULT hres;
@@ -637,6 +762,12 @@ static HRESULT detach_scope(script_ctx_t *ctx, call_frame_t *frame, scope_chain_ assert(scope->frame == frame); scope->frame = NULL;
+ hres = alloc_detached_locals(ctx, frame, scope); + if (FAILED(hres)) + return hres; + detached_local = scope->detached_locals->local; + var_offs = scope->detached_locals->var_offs; + if (!scope->obj) { jsobj = &scope->dispex; @@ -650,16 +781,33 @@ static HRESULT detach_scope(script_ctx_t *ctx, call_frame_t *frame, scope_chain_ jsdisp_propput_name(jsobj, func->name, jsval_obj(jsdisp_addref(frame->function_instance)));
index = scope->scope_index; - for(i = 0; i < frame->function->local_scopes[index].locals_cnt; i++) + for (i = 0; i < func->local_scopes[index].locals_cnt; i++) { - WCHAR *name = frame->function->local_scopes[index].locals[i].name; - int ref = frame->function->local_scopes[index].locals[i].ref; + WCHAR *name = func->local_scopes[index].locals[i].name; + int ref = func->local_scopes[index].locals[i].ref;
- if (FAILED(hres = jsdisp_propput_name(jsobj, name, ctx->stack[local_off(frame, ref)]))) - return hres; - if (scope != frame->base_scope && frame->function->variables[ref].func_id != -1 - && FAILED(hres = jsdisp_propput_name(frame->variable_obj, name, ctx->stack[local_off(frame, ref)]))) + if (ref < 0) + continue; + if (FAILED(hres = jsval_copy(ctx->stack[local_off(frame, ref)], &detached_local[var_offs + func->variables[ref].scope_offs]))) return hres; + if (scope != frame->base_scope && func->variables[ref].func_id != -1) + { + jsval_t *base_ref = get_detached_local_ref(frame->base_scope, name); + if (base_ref) + { + jsval_t tmp; + hres = jsval_copy(ctx->stack[local_off(frame, ref)], &tmp); + if (SUCCEEDED(hres)) + { + jsval_release(*base_ref); + *base_ref = tmp; + } + } + else + hres = jsdisp_propput_name(frame->variable_obj, name, ctx->stack[local_off(frame, ref)]); + if (FAILED(hres)) + return hres; + } } return S_OK; } @@ -782,6 +930,10 @@ static HRESULT identifier_eval(script_ctx_t *ctx, BSTR identifier, exprval_t *re ret->u.val = jsval_obj(jsdisp_addref(scope->frame->function_instance)); return S_OK; } + }else if((hres = get_detached_local_dispid(scope, identifier, &id)) != DISP_E_UNKNOWNNAME) { + if(SUCCEEDED(hres)) + exprval_set_disp_ref(ret, to_disp(&scope->dispex), id); + return hres; }
if (!scope->obj) @@ -960,6 +1112,14 @@ static HRESULT scope_init_locals(script_ctx_t *ctx) else jsobj = as_jsdisp(scope->obj);
+ if (detached_vars) + { + assert(!scope->detached_locals); + hres = alloc_detached_locals(ctx, frame, scope); + if (FAILED(hres)) + return hres; + } + for(i = 0; i < frame->function->local_scopes[index].locals_cnt; i++) { WCHAR *name = frame->function->local_scopes[index].locals[i].name; @@ -975,10 +1135,20 @@ static HRESULT scope_init_locals(script_ctx_t *ctx) + frame->function->variables[ref].func_id, ctx->call_ctx->scope, &func_obj))) return hres; val = jsval_obj(func_obj); - if (detached_vars && FAILED(hres = jsdisp_propput_name(frame->variable_obj, name, jsval_obj(func_obj)))) + if (detached_vars) { - jsdisp_release(func_obj); - return hres; + jsval_t *base_ref; + + if (frame->base_scope && (base_ref = get_detached_local_ref(frame->base_scope, name))) + { + jsval_release(*base_ref); + *base_ref = jsval_obj(jsdisp_addref(func_obj)); + } + else if (FAILED(hres = jsdisp_propput_name(frame->variable_obj, name, jsval_obj(func_obj)))) + { + jsdisp_release(func_obj); + return hres; + } } } else @@ -988,7 +1158,10 @@ static HRESULT scope_init_locals(script_ctx_t *ctx)
if (detached_vars) { - hres = jsdisp_propput_name(jsobj, name, val); + if (scope->detached_locals) + hres = jsval_copy(val, &scope->detached_locals->local[scope->detached_locals->var_offs + frame->function->variables[ref].scope_offs]); + else + hres = jsdisp_propput_name(jsobj, name, val); jsval_release(val); if (FAILED(hres)) return hres; @@ -3297,6 +3470,7 @@ static HRESULT setup_scope(script_ctx_t *ctx, call_frame_t *frame, scope_chain_t HRESULT exec_source(script_ctx_t *ctx, DWORD flags, bytecode_t *bytecode, function_code_t *function, scope_chain_t *scope, IDispatch *this_obj, jsdisp_t *function_instance, unsigned argc, jsval_t *argv, jsval_t *r) { + scope_chain_t *locals_scope = NULL; jsdisp_t *variable_obj; call_frame_t *frame; unsigned i; @@ -3343,6 +3517,7 @@ HRESULT exec_source(script_ctx_t *ctx, DWORD flags, bytecode_t *bytecode, functi }
if((flags & EXEC_EVAL) && ctx->call_ctx) { + locals_scope = ctx->call_ctx->base_scope; variable_obj = ctx->call_ctx->variable_obj; hres = detach_variable_object(ctx, ctx->call_ctx, FALSE); if(FAILED(hres)) @@ -3364,6 +3539,7 @@ HRESULT exec_source(script_ctx_t *ctx, DWORD flags, bytecode_t *bytecode, functi TRACE("[%d] %s %d\n", i, debugstr_w(function->variables[i].name), function->variables[i].func_id); if(function->variables[i].func_id != -1) { jsdisp_t *func_obj; + jsval_t *local_ref;
if (function->funcs[function->variables[i].func_id].scope_index && flags & EXEC_EVAL) { @@ -3375,8 +3551,15 @@ HRESULT exec_source(script_ctx_t *ctx, DWORD flags, bytecode_t *bytecode, functi if(FAILED(hres)) goto fail;
- hres = jsdisp_propput_name(variable_obj, function->variables[i].name, jsval_obj(func_obj)); - jsdisp_release(func_obj); + if(locals_scope && (local_ref = get_detached_local_ref(locals_scope, function->variables[i].name))) { + jsval_release(*local_ref); + *local_ref = jsval_obj(func_obj); + }else { + hres = jsdisp_propput_name(variable_obj, function->variables[i].name, jsval_obj(func_obj)); + jsdisp_release(func_obj); + if(FAILED(hres)) + goto fail; + } continue; }
@@ -3387,9 +3570,11 @@ HRESULT exec_source(script_ctx_t *ctx, DWORD flags, bytecode_t *bytecode, functi if(!item && (flags & EXEC_GLOBAL) && lookup_global_members(ctx, function->variables[i].name, NULL)) continue;
- hres = jsdisp_get_id(variable_obj, function->variables[i].name, fdexNameEnsure, &id); - if(FAILED(hres)) - goto fail; + if(!locals_scope || !get_detached_local_ref(locals_scope, function->variables[i].name)) { + hres = jsdisp_get_id(variable_obj, function->variables[i].name, fdexNameEnsure, &id); + if(FAILED(hres)) + goto fail; + } } }
diff --git a/dlls/jscript/engine.h b/dlls/jscript/engine.h index b6f26232059..2454b0abdc3 100644 --- a/dlls/jscript/engine.h +++ b/dlls/jscript/engine.h @@ -151,6 +151,7 @@ typedef struct {
typedef struct { unsigned locals_cnt; + unsigned vars_cnt; local_ref_t *locals; } local_ref_scopes_t;
@@ -169,6 +170,7 @@ typedef struct _function_code_t { unsigned var_cnt; struct { BSTR name; + unsigned scope_offs; int func_id; /* -1 if not a function */ } *variables;
@@ -222,10 +224,18 @@ static inline bytecode_t *bytecode_addref(bytecode_t *code) return code; }
+struct locals_buffer { + function_code_t *func_code; + unsigned local_cnt; + unsigned var_offs; + jsval_t local[]; +}; + typedef struct _scope_chain_t { jsdisp_t dispex; IDispatch *obj; /* Does not hold ref when it's set to scope's dispex (prevents cyclic ref for a very common case) */ unsigned int scope_index; + struct locals_buffer *detached_locals; struct _call_frame_t *frame; struct _scope_chain_t *next; } scope_chain_t; diff --git a/dlls/jscript/function.c b/dlls/jscript/function.c index 7f53122001c..da89b7f2b3a 100644 --- a/dlls/jscript/function.c +++ b/dlls/jscript/function.c @@ -129,48 +129,36 @@ static jsval_t *get_argument_ref(ArgumentsInstance *arguments, unsigned idx) { if(arguments->buf) return arguments->buf + idx; - if(arguments->frame->base_scope->frame || idx >= arguments->frame->function->param_cnt) + if(!arguments->frame->base_scope->detached_locals) return arguments->jsdisp.ctx->stack + arguments->frame->arguments_off + idx; - return NULL; + return arguments->frame->base_scope->detached_locals->local + idx; }
static HRESULT Arguments_idx_get(jsdisp_t *jsdisp, unsigned idx, jsval_t *r) { ArgumentsInstance *arguments = arguments_from_jsdisp(jsdisp); - jsval_t *ref;
TRACE("%p[%u]\n", arguments, idx);
- if((ref = get_argument_ref(arguments, idx))) - return jsval_copy(*ref, r); - - /* FIXME: Accessing by name won't work for duplicated argument names */ - return jsdisp_propget_name(as_jsdisp(arguments->frame->base_scope->obj), - arguments->function->func_code->params[idx], r); + return jsval_copy(*get_argument_ref(arguments, idx), r); }
static HRESULT Arguments_idx_put(jsdisp_t *jsdisp, unsigned idx, jsval_t val) { ArgumentsInstance *arguments = arguments_from_jsdisp(jsdisp); - jsval_t *ref; + jsval_t copy, *ref; HRESULT hres;
TRACE("%p[%u] = %s\n", arguments, idx, debugstr_jsval(val));
- if((ref = get_argument_ref(arguments, idx))) { - jsval_t copy; - hres = jsval_copy(val, ©); - if(FAILED(hres)) - return hres; - - jsval_release(*ref); - *ref = copy; - return S_OK; - } + hres = jsval_copy(val, ©); + if(FAILED(hres)) + return hres;
- /* FIXME: Accessing by name won't work for duplicated argument names */ - return jsdisp_propput_name(as_jsdisp(arguments->frame->base_scope->obj), - arguments->function->func_code->params[idx], val); + ref = get_argument_ref(arguments, idx); + jsval_release(*ref); + *ref = copy; + return S_OK; }
static HRESULT Arguments_gc_traverse(struct gc_ctx *gc_ctx, enum gc_traverse_op op, jsdisp_t *jsdisp) @@ -243,7 +231,6 @@ void detach_arguments_object(jsdisp_t *args_disp) call_frame_t *frame = arguments->frame; const BOOL on_stack = frame->base_scope->frame == frame; jsdisp_t *jsobj = as_jsdisp(frame->base_scope->obj); - HRESULT hres;
/* Reset arguments value to cut the reference cycle. Note that since all activation contexts have * their own arguments property, it's impossible to use prototype's one during name lookup */ @@ -254,14 +241,11 @@ void detach_arguments_object(jsdisp_t *args_disp) if(arguments->jsdisp.ref > 1) { arguments->buf = malloc(arguments->argc * sizeof(*arguments->buf)); if(arguments->buf) { + const jsval_t *args = on_stack ? arguments->jsdisp.ctx->stack + frame->arguments_off : frame->base_scope->detached_locals->local; int i;
for(i = 0; i < arguments->argc ; i++) { - if(on_stack || i >= frame->function->param_cnt) - hres = jsval_copy(arguments->jsdisp.ctx->stack[frame->arguments_off + i], arguments->buf+i); - else - hres = jsdisp_propget_name(jsobj, frame->function->params[i], arguments->buf+i); - if(FAILED(hres)) + if(FAILED(jsval_copy(args[i], &arguments->buf[i]))) arguments->buf[i] = jsval_undefined(); } }else {