From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/compile.c | 2 +- dlls/jscript/engine.c | 65 ++++++++++++++++------ dlls/jscript/engine.h | 2 + dlls/jscript/function.c | 109 +++++++++++++++++++++++-------------- dlls/jscript/tests/lang.js | 32 +++++++++++ 5 files changed, 151 insertions(+), 59 deletions(-)
diff --git a/dlls/jscript/compile.c b/dlls/jscript/compile.c index e2690935212..2d2c2c9f40a 100644 --- a/dlls/jscript/compile.c +++ b/dlls/jscript/compile.c @@ -2567,7 +2567,7 @@ static HRESULT compile_function(compiler_ctx_t *ctx, statement_t *source, functi } }
- for(i = 0; i < func->param_cnt; i++) { + for(i = func->param_cnt; i--;) { if(!find_local(ctx, func->params[i], 0) && !alloc_local(ctx, func->params[i], -i-1, 0)) return E_OUTOFMEMORY; } 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..8675fc53529 100644 --- a/dlls/jscript/function.c +++ b/dlls/jscript/function.c @@ -127,50 +127,36 @@ static unsigned Arguments_idx_length(jsdisp_t *jsdisp)
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 && arguments->frame->base_scope->frame) return arguments->jsdisp.ctx->stack + arguments->frame->arguments_off + idx; - return NULL; + return arguments->buf + 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) @@ -206,13 +192,21 @@ HRESULT setup_arguments_object(script_ctx_t *ctx, call_frame_t *frame) { ArgumentsInstance *args; HRESULT hres; + unsigned i;
args = calloc(1, sizeof(*args)); if(!args) return E_OUTOFMEMORY;
+ args->buf = malloc(frame->argc * sizeof(*args->buf)); + if(!args->buf) { + free(args); + return E_OUTOFMEMORY; + } + hres = init_dispex_from_constr(&args->jsdisp, ctx, &Arguments_info, ctx->object_constr); if(FAILED(hres)) { + free(args->buf); free(args); return hres; } @@ -220,6 +214,8 @@ HRESULT setup_arguments_object(script_ctx_t *ctx, call_frame_t *frame) args->function = (InterpretedFunction*)function_from_jsdisp(jsdisp_addref(frame->function_instance)); args->argc = frame->argc; args->frame = frame; + for(i = 0; i < args->argc; i++) + args->buf[i] = jsval_undefined();
hres = jsdisp_define_data_property(&args->jsdisp, L"length", PROPF_WRITABLE | PROPF_CONFIGURABLE, jsval_number(args->argc)); @@ -237,13 +233,29 @@ 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; + + for(i = 0; i < arguments->argc; i++) { + assert(is_undefined(arguments->buf[i])); + hres = jsval_copy(args[i], &arguments->buf[i]); + if(FAILED(hres)) + 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 +267,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..30d8a338fd3 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; } + ok(get_a() === 2, "function(a, a, b, c) get_a() = " + get_a()); + 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; + ok(arguments[0] === 1, "function(a, a, b, c) arguments[0] = " + arguments[0]); + 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"); + ok(args[0] === 1, "function(a, a, b, c) detached args[0] = " + args[0]); + 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); + ok(arguments[0] === 5, "function(a, a) arguments[0] = " + arguments[0]); + 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);