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/dispex.c | 8 ++ dlls/jscript/engine.c | 227 ++++++++++++++++++++++++++++++++++------ dlls/jscript/engine.h | 12 ++- dlls/jscript/function.c | 42 +++----- dlls/jscript/jscript.h | 1 + 6 files changed, 231 insertions(+), 64 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/dispex.c b/dlls/jscript/dispex.c index e8f7d599463..b5fc3699212 100644 --- a/dlls/jscript/dispex.c +++ b/dlls/jscript/dispex.c @@ -2332,6 +2332,14 @@ HRESULT jsdisp_get_id(jsdisp_t *jsdisp, const WCHAR *name, DWORD flags, DISPID * return DISP_E_UNKNOWNNAME; }
+HRESULT jsdisp_get_idx_id(jsdisp_t *jsdisp, DWORD idx, DISPID *id) +{ + WCHAR name[11]; + + swprintf(name, ARRAY_SIZE(name), L"%u", idx); + return jsdisp_get_id(jsdisp, name, 0, id); +} + HRESULT jsdisp_call_value(jsdisp_t *jsfunc, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { HRESULT hres; diff --git a/dlls/jscript/engine.c b/dlls/jscript/engine.c index 8ce2efb85d4..7c949a3d2bc 100644 --- a/dlls/jscript/engine.c +++ b/dlls/jscript/engine.c @@ -162,6 +162,25 @@ 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); + return local_ref ? jsdisp_get_idx_id(&scope->dispex, local_ref - scope->detached_locals->local, id) : DISP_E_UNKNOWNNAME; +} + /* Steals input reference even on failure. */ static HRESULT stack_push_exprval(script_ctx_t *ctx, exprval_t *val) { @@ -228,6 +247,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 +454,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 +481,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)) @@ -466,9 +553,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 };
@@ -622,10 +709,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;
@@ -635,6 +754,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) { if (FAILED(hres = create_object(ctx, NULL, &jsobj))) @@ -649,16 +774,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; } @@ -781,6 +923,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) @@ -939,7 +1085,6 @@ static HRESULT scope_init_locals(script_ctx_t *ctx) unsigned int i, off, index; scope_chain_t *scope; BOOL detached_vars; - jsdisp_t *jsobj; HRESULT hres;
scope = frame->scope; @@ -951,14 +1096,13 @@ static HRESULT scope_init_locals(script_ctx_t *ctx) assert(frame->base_scope->frame == frame); frame->scope->frame = ctx->call_ctx; } - else if (!scope->obj) + else { - if (FAILED(hres = create_object(ctx, NULL, &jsobj))) + assert(!scope->detached_locals); + hres = alloc_detached_locals(ctx, frame, scope); + if (FAILED(hres)) return hres; - scope->obj = to_disp(jsobj); } - else - jsobj = as_jsdisp(scope->obj);
for(i = 0; i < frame->function->local_scopes[index].locals_cnt; i++) { @@ -975,10 +1119,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 @@ -987,12 +1141,7 @@ static HRESULT scope_init_locals(script_ctx_t *ctx) }
if (detached_vars) - { - hres = jsdisp_propput_name(jsobj, name, val); - jsval_release(val); - if (FAILED(hres)) - return hres; - } + scope->detached_locals->local[scope->detached_locals->var_offs + frame->function->variables[ref].scope_offs] = val; else { off = local_off(frame, ref); @@ -3297,6 +3446,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 +3493,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)) @@ -3365,6 +3516,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) { @@ -3376,8 +3528,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; }
@@ -3388,9 +3547,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 9b29373a0da..3918ec36989 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; /* FIXME: don't wrap it in a jsdisp (it holds ref and traverse for the garbage collector) */ + jsdisp_t dispex; IDispatch *obj; 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 { diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index e91aa6dfabd..bd80528c020 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -254,6 +254,7 @@ HRESULT jsdisp_propput_idx(jsdisp_t*,DWORD,jsval_t) DECLSPEC_HIDDEN; HRESULT jsdisp_propget_name(jsdisp_t*,LPCWSTR,jsval_t*) DECLSPEC_HIDDEN; HRESULT jsdisp_get_idx(jsdisp_t*,DWORD,jsval_t*) DECLSPEC_HIDDEN; HRESULT jsdisp_get_id(jsdisp_t*,const WCHAR*,DWORD,DISPID*) DECLSPEC_HIDDEN; +HRESULT jsdisp_get_idx_id(jsdisp_t*,DWORD,DISPID*) DECLSPEC_HIDDEN; HRESULT disp_delete(IDispatch*,DISPID,BOOL*) DECLSPEC_HIDDEN; HRESULT disp_delete_name(script_ctx_t*,IDispatch*,jsstr_t*,BOOL*) DECLSPEC_HIDDEN; HRESULT jsdisp_delete_idx(jsdisp_t*,DWORD) DECLSPEC_HIDDEN;