From: Gabriel Ivăncescu gabrielopcode@gmail.com
Rather than using the variable obj for them. And look them up from the argument object, if available.
The variable obj will still contain extra arguments that were not passed, or even the passed args if the argument obj is detached from the call frame (at that point, both need to have their own distinct copies).
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/engine.c | 65 +++++++++++++++++------ dlls/jscript/engine.h | 2 + dlls/jscript/function.c | 102 ++++++++++++++++++++++--------------- dlls/jscript/tests/lang.js | 32 ++++++++++++ 4 files changed, 144 insertions(+), 57 deletions(-)
diff --git a/dlls/jscript/engine.c b/dlls/jscript/engine.c index d24b54ec800..fba0e5bddef 100644 --- a/dlls/jscript/engine.c +++ b/dlls/jscript/engine.c @@ -215,28 +215,44 @@ static BOOL stack_topn_exprval(script_ctx_t *ctx, unsigned n, exprval_t *r) /* Got stack reference in deoptimized code. Need to convert it back to variable object reference. */
assert(off < frame->variables_off + frame->function->var_cnt); - if (off >= frame->variables_off) + if (off < frame->variables_off && off - frame->arguments_off < frame->argc && frame->base_scope->arguments_obj) { - name = frame->function->variables[off - frame->variables_off].name; - scope = frame->scope; - } - else - { - name = frame->function->params[off - frame->arguments_off]; - scope = frame->base_scope; - } + WCHAR name_idx[11]; + jsobj = frame->base_scope->arguments_obj; + swprintf(name_idx, ARRAY_SIZE(name_idx), L"%u", off - frame->arguments_off);
- while (1) - { - if ((jsobj = to_jsdisp(scope->obj)) && SUCCEEDED(hres = jsdisp_get_id(jsobj, name, 0, &id))) - break; - if (scope == frame->base_scope) + if (FAILED(hres = jsdisp_get_id(jsobj, name_idx, 0, &id))) { r->type = EXPRVAL_INVALID; r->u.hres = hres; return FALSE; } - scope = scope->next; + } + else + { + if (off >= frame->variables_off) + { + name = frame->function->variables[off - frame->variables_off].name; + scope = frame->scope; + } + else + { + name = frame->function->params[off - frame->arguments_off]; + scope = frame->base_scope; + } + + while (1) + { + if ((jsobj = to_jsdisp(scope->obj)) && SUCCEEDED(hres = jsdisp_get_id(jsobj, name, 0, &id))) + break; + if (scope == frame->base_scope) + { + r->type = EXPRVAL_INVALID; + r->u.hres = hres; + return FALSE; + } + scope = scope->next; + } }
*stack_top_ref(ctx, n+1) = jsval_obj(jsdisp_addref(jsobj)); @@ -632,7 +648,7 @@ HRESULT jsval_strict_equal(jsval_t lval, jsval_t rval, BOOL *ret) 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, args_len = 0; jsdisp_t *jsobj; HRESULT hres;
@@ -651,6 +667,13 @@ static HRESULT detach_scope(script_ctx_t *ctx, call_frame_t *frame, scope_chain_ else jsobj = as_jsdisp(scope->obj);
+ if (scope->arguments_obj) + { + args_len = frame->argc; + if (FAILED(hres = copy_arguments_from_stack(scope->arguments_obj, frame))) + return hres; + } + if (scope == frame->base_scope && func->name && func->local_ref == INVALID_LOCAL_REF && ctx->version >= SCRIPTLANGUAGEVERSION_ES5) jsdisp_propput_name(jsobj, func->name, jsval_obj(jsdisp_addref(frame->function_instance))); @@ -661,6 +684,8 @@ static HRESULT detach_scope(script_ctx_t *ctx, call_frame_t *frame, scope_chain_ WCHAR *name = frame->function->local_scopes[index].locals[i].name; int ref = frame->function->local_scopes[index].locals[i].ref;
+ if(ref < 0 && -ref - 1 < args_len) + continue; 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 @@ -788,6 +813,14 @@ 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(scope->arguments_obj) { + assert(scope->scope_index == 0); + hres = get_arguments_identifier_id(scope->arguments_obj, identifier, &id); + if(hres != DISP_E_MEMBERNOTFOUND) { + if(SUCCEEDED(hres)) + exprval_set_disp_ref(ret, to_disp(scope->arguments_obj), id); + return hres; + } }
if (!scope->obj) diff --git a/dlls/jscript/engine.h b/dlls/jscript/engine.h index 3688972f29f..13e5f7c4f12 100644 --- a/dlls/jscript/engine.h +++ b/dlls/jscript/engine.h @@ -303,4 +303,6 @@ HRESULT exec_source(script_ctx_t*,DWORD,bytecode_t*,function_code_t*,scope_chain
HRESULT create_source_function(script_ctx_t*,bytecode_t*,function_code_t*,scope_chain_t*,jsdisp_t**) DECLSPEC_HIDDEN; HRESULT setup_arguments_object(script_ctx_t*,call_frame_t*) DECLSPEC_HIDDEN; +HRESULT copy_arguments_from_stack(jsdisp_t*,call_frame_t*) DECLSPEC_HIDDEN; void detach_arguments_object(jsdisp_t*,call_frame_t*) DECLSPEC_HIDDEN; +HRESULT get_arguments_identifier_id(jsdisp_t*,const WCHAR*,DISPID*) DECLSPEC_HIDDEN; diff --git a/dlls/jscript/function.c b/dlls/jscript/function.c index b1d7e249923..d4314fa560f 100644 --- a/dlls/jscript/function.c +++ b/dlls/jscript/function.c @@ -129,48 +129,34 @@ 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) - return arguments->jsdisp.ctx->stack + arguments->frame->arguments_off + idx; - return NULL; + return arguments->jsdisp.ctx->stack + arguments->frame->arguments_off + 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) @@ -237,13 +223,34 @@ HRESULT setup_arguments_object(script_ctx_t *ctx, call_frame_t *frame) return S_OK; }
+HRESULT copy_arguments_from_stack(jsdisp_t *args_disp, call_frame_t *frame) +{ + ArgumentsInstance *arguments = arguments_from_jsdisp(args_disp); + const jsval_t *args = arguments->jsdisp.ctx->stack + frame->arguments_off; + HRESULT hres; + unsigned i; + + arguments->buf = malloc(arguments->argc * sizeof(*arguments->buf)); + if(!arguments->buf) + return E_OUTOFMEMORY; + + for(i = 0; i < arguments->argc; i++) { + hres = jsval_copy(args[i], &arguments->buf[i]); + if(FAILED(hres)) { + do arguments->buf[i++] = jsval_undefined(); while(i < arguments->argc); + return hres; + } + } + + return S_OK; +} + void detach_arguments_object(jsdisp_t *args_disp, call_frame_t *popped_frame) { ArgumentsInstance *arguments = arguments_from_jsdisp(args_disp); call_frame_t *frame = arguments->frame; const BOOL on_stack = frame->base_scope->frame == frame; jsdisp_t *jsobj; - HRESULT hres;
if(frame != popped_frame) return; @@ -255,29 +262,42 @@ void detach_arguments_object(jsdisp_t *args_disp, call_frame_t *popped_frame) frame->base_scope->arguments_obj = NULL; arguments->frame = NULL;
- /* Don't bother coppying arguments if call frame holds the last reference. */ - if(arguments->jsdisp.ref > 1) { - arguments->buf = malloc(arguments->argc * sizeof(*arguments->buf)); - if(arguments->buf) { - int i; + /* Don't bother copying arguments if scope holds the last reference */ + if(arguments->jsdisp.ref > 1 && on_stack) + copy_arguments_from_stack(&arguments->jsdisp, frame);
- 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)) - arguments->buf[i] = jsval_undefined(); - } - }else { - ERR("out of memory\n"); - arguments->argc = 0; + /* If scope stays alive, copy the args to the variable obj, as they'll be separate from the arguments obj now */ + if(frame->base_scope->dispex.ref > 1) { + const local_ref_scopes_t *local_scope = &frame->function->local_scopes[frame->base_scope->scope_index]; + const jsval_t *args = on_stack ? arguments->jsdisp.ctx->stack + frame->arguments_off : arguments->buf; + unsigned i; + + for(i = 0; i < local_scope->locals_cnt; i++) { + int ref = local_scope->locals[i].ref; + + if(ref < 0 && -ref - 1 < arguments->argc) + jsdisp_propput_name(jsobj, local_scope->locals[i].name, args[-ref - 1]); } }
jsdisp_release(&arguments->jsdisp); }
+HRESULT get_arguments_identifier_id(jsdisp_t *args_disp, const WCHAR *identifier, DISPID *id) +{ + ArgumentsInstance *arguments = arguments_from_jsdisp(args_disp); + local_ref_t *ref; + WCHAR buf[11]; + + ref = lookup_local(arguments->function->func_code, identifier, 0); + + if(!ref || ref->ref >= 0 || -ref->ref - 1 >= arguments->argc) + return DISP_E_MEMBERNOTFOUND; + + swprintf(buf, ARRAY_SIZE(buf), L"%u", -ref->ref - 1); + return jsdisp_get_id(&arguments->jsdisp, buf, fdexNameImplicit, id); +} + HRESULT Function_invoke(jsdisp_t *func_this, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { FunctionInstance *function; diff --git a/dlls/jscript/tests/lang.js b/dlls/jscript/tests/lang.js index 5c201d5bf26..36afcc82417 100644 --- a/dlls/jscript/tests/lang.js +++ b/dlls/jscript/tests/lang.js @@ -306,6 +306,38 @@ argumentsTest(); ok(arguments === 1, "arguments = " + arguments); })();
+// duplicated argument names are shadowed by the last argument with the same name +(function() { + var args, get_a, set_a; + + (function(a, a, b, c) { + get_a = function() { return a; } + set_a = function(v) { a = v; } + todo_wine_ok(get_a() === 2, "function(a, a, b, c) get_a() = " + get_a()); + todo_wine_ok(a === 2, "function(a, a, b, c) a = " + a); + ok(b === 3, "function(a, a, b, c) b = " + b); + ok(c === 4, "function(a, a, b, c) c = " + c); + a = 42; + todo_wine_ok(arguments[0] === 1, "function(a, a, b, c) arguments[0] = " + arguments[0]); + todo_wine_ok(arguments[1] === 42, "function(a, a, b, c) arguments[1] = " + arguments[1]); + ok(get_a() === 42, "function(a, a, b, c) get_a() = " + get_a() + " expected 42"); + args = arguments; + })(1, 2, 3, 4); + + ok(get_a() === 42, "function(a, a, b, c) get_a() after detach = " + get_a()); + set_a(100); + ok(get_a() === 100, "function(a, a, b, c) get_a() = " + get_a() + " expected 100"); + todo_wine_ok(args[0] === 1, "function(a, a, b, c) detached args[0] = " + args[0]); + todo_wine_ok(args[1] === 42, "function(a, a, b, c) detached args[1] = " + args[1]); + + (function(a, a) { + eval("var a = 7;"); + ok(a === 7, "function(a, a) a = " + a); + todo_wine_ok(arguments[0] === 5, "function(a, a) arguments[0] = " + arguments[0]); + todo_wine_ok(arguments[1] === 7, "function(a, a) arguments[1] = " + arguments[1]); + })(5, 6); +})(); + (function callAsExprTest() { ok(callAsExprTest.arguments === null, "callAsExprTest.arguments = " + callAsExprTest.arguments); })(1,2);