From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/compile.c | 16 ++++++++-- dlls/jscript/engine.c | 6 +++- dlls/jscript/engine.h | 4 ++- dlls/jscript/function.c | 61 ++++++++++++++++++++++++++++++++++++-- dlls/jscript/tests/lang.js | 10 +++++++ 5 files changed, 90 insertions(+), 7 deletions(-)
diff --git a/dlls/jscript/compile.c b/dlls/jscript/compile.c index f81757cd93c..9107d696f29 100644 --- a/dlls/jscript/compile.c +++ b/dlls/jscript/compile.c @@ -2525,6 +2525,7 @@ static HRESULT compile_function(compiler_ctx_t *ctx, statement_t *source, functi
func->bytecode = ctx->code; func->local_ref = INVALID_LOCAL_REF; + func->shadowed_param_cnt = 0; func->scope_index = 0; ctx->func_head = ctx->func_tail = NULL; ctx->from_eval = from_eval; @@ -2566,9 +2567,18 @@ static HRESULT compile_function(compiler_ctx_t *ctx, statement_t *source, functi } }
- for(i = 0; 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; + func->shadowed_params_idx = compiler_alloc(ctx->code, func->param_cnt * sizeof(*func->shadowed_params_idx)); + if(!func->shadowed_params_idx) + return E_OUTOFMEMORY; + + for(i = func->param_cnt; i--;) { + if(find_local(ctx, func->params[i], 0)) + func->shadowed_params_idx[i] = func->shadowed_param_cnt++; + else { + func->shadowed_params_idx[i] = ~0; + if(!alloc_local(ctx, func->params[i], -i-1, 0)) + return E_OUTOFMEMORY; + } }
hres = visit_block_statement(ctx, NULL, source); diff --git a/dlls/jscript/engine.c b/dlls/jscript/engine.c index 9d465f84388..846518d88c5 100644 --- a/dlls/jscript/engine.c +++ b/dlls/jscript/engine.c @@ -696,7 +696,11 @@ static HRESULT detach_variable_object(script_ctx_t *ctx, call_frame_t *frame, BO }
TRACE("detaching scope chain %p, frame %p.\n", ctx->call_ctx->scope, frame); - return detach_scope_chain(ctx, frame, ctx->call_ctx->scope); + hres = detach_scope_chain(ctx, frame, ctx->call_ctx->scope); + if(FAILED(hres)) + return hres; + + return fill_shadowed_params(ctx, frame); }
static BOOL lookup_global_members(script_ctx_t *ctx, BSTR identifier, exprval_t *ret) diff --git a/dlls/jscript/engine.h b/dlls/jscript/engine.h index 63faed2a8fc..34bd3e0e81f 100644 --- a/dlls/jscript/engine.h +++ b/dlls/jscript/engine.h @@ -172,8 +172,9 @@ typedef struct _function_code_t { int func_id; /* -1 if not a function */ } *variables;
- unsigned param_cnt; + unsigned param_cnt, shadowed_param_cnt; BSTR *params; + unsigned *shadowed_params_idx; /* index for each param into arguments object's shadowed_params, ~0 if not shadowed */
local_ref_scopes_t *local_scopes; unsigned local_scope_count; @@ -305,3 +306,4 @@ 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; void detach_arguments_object(jsdisp_t*) DECLSPEC_HIDDEN; +HRESULT fill_shadowed_params(script_ctx_t*,call_frame_t*) DECLSPEC_HIDDEN; diff --git a/dlls/jscript/function.c b/dlls/jscript/function.c index 018fd3955db..a4c2d2f09d5 100644 --- a/dlls/jscript/function.c +++ b/dlls/jscript/function.c @@ -67,6 +67,7 @@ typedef struct { jsdisp_t jsdisp; InterpretedFunction *function; jsval_t *buf; + jsval_t *shadowed_params; call_frame_t *frame; unsigned argc; } ArgumentsInstance; @@ -114,6 +115,13 @@ static void Arguments_destructor(jsdisp_t *jsdisp) free(arguments->buf); }
+ if(arguments->shadowed_params) { + unsigned i, cnt = arguments->frame->function->shadowed_param_cnt; + for(i = 0; i < cnt; i++) + jsval_release(arguments->shadowed_params[i]); + free(arguments->shadowed_params); + } + if(arguments->function) jsdisp_release(&arguments->function->function.dispex); free(arguments); @@ -131,6 +139,8 @@ static jsval_t *get_argument_ref(ArgumentsInstance *arguments, unsigned idx) 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; + if(arguments->shadowed_params && arguments->frame->function->shadowed_params_idx[idx] != ~0) + return arguments->shadowed_params + arguments->frame->function->shadowed_params_idx[idx]; return NULL; }
@@ -144,7 +154,6 @@ static HRESULT Arguments_idx_get(jsdisp_t *jsdisp, unsigned idx, jsval_t *r) 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(arguments->frame->base_scope->jsobj, arguments->function->func_code->params[idx], r); } @@ -168,7 +177,6 @@ static HRESULT Arguments_idx_put(jsdisp_t *jsdisp, unsigned idx, jsval_t val) return S_OK; }
- /* FIXME: Accessing by name won't work for duplicated argument names */ return jsdisp_propput_name(arguments->frame->base_scope->jsobj, arguments->function->func_code->params[idx], val); } @@ -187,6 +195,15 @@ static HRESULT Arguments_gc_traverse(struct gc_ctx *gc_ctx, enum gc_traverse_op } }
+ if(arguments->shadowed_params) { + unsigned cnt = arguments->frame->function->shadowed_param_cnt; + for(i = 0; i < cnt; i++) { + hres = gc_process_linked_val(gc_ctx, op, jsdisp, &arguments->shadowed_params[i]); + if(FAILED(hres)) + return hres; + } + } + return gc_process_linked_obj(gc_ctx, op, jsdisp, &arguments->function->function.dispex, (void**)&arguments->function); }
@@ -204,6 +221,7 @@ static const builtin_info_t Arguments_info = {
HRESULT setup_arguments_object(script_ctx_t *ctx, call_frame_t *frame) { + unsigned i, shadowed_param_cnt; ArgumentsInstance *args; HRESULT hres;
@@ -211,8 +229,15 @@ HRESULT setup_arguments_object(script_ctx_t *ctx, call_frame_t *frame) if(!args) return E_OUTOFMEMORY;
+ shadowed_param_cnt = frame->function->shadowed_param_cnt; + if(shadowed_param_cnt && !(args->shadowed_params = malloc(shadowed_param_cnt * sizeof(*args->shadowed_params)))) { + free(args); + return E_OUTOFMEMORY; + } + hres = init_dispex_from_constr(&args->jsdisp, ctx, &Arguments_info, ctx->object_constr); if(FAILED(hres)) { + free(args->shadowed_params); free(args); return hres; } @@ -220,6 +245,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 < shadowed_param_cnt; i++) + args->shadowed_params[i] = jsval_undefined();
hres = jsdisp_define_data_property(&args->jsdisp, L"length", PROPF_WRITABLE | PROPF_CONFIGURABLE, jsval_number(args->argc)); @@ -242,11 +269,13 @@ void detach_arguments_object(jsdisp_t *args_disp) ArgumentsInstance *arguments = arguments_from_jsdisp(args_disp); call_frame_t *frame = arguments->frame; const BOOL on_stack = frame->base_scope->frame == frame; + jsval_t *shadowed_params = arguments->shadowed_params; 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 */ jsdisp_propput_name(frame->base_scope->jsobj, L"arguments", jsval_undefined()); + arguments->shadowed_params = NULL; arguments->frame = NULL;
/* Don't bother coppying arguments if call frame holds the last reference. */ @@ -256,8 +285,11 @@ void detach_arguments_object(jsdisp_t *args_disp) int i;
for(i = 0; i < arguments->argc ; i++) { + hres = S_OK; if(on_stack || i >= frame->function->param_cnt) hres = jsval_copy(arguments->jsdisp.ctx->stack[frame->arguments_off + i], arguments->buf+i); + else if(frame->function->shadowed_params_idx[i] != ~0) + arguments->buf[i] = shadowed_params[frame->function->shadowed_params_idx[i]]; else hres = jsdisp_propget_name(frame->base_scope->jsobj, frame->function->params[i], arguments->buf+i); if(FAILED(hres)) @@ -267,11 +299,36 @@ void detach_arguments_object(jsdisp_t *args_disp) ERR("out of memory\n"); arguments->argc = 0; } + }else if(shadowed_params) { + unsigned i, cnt = frame->function->shadowed_param_cnt; + for(i = 0; i < cnt; i++) + jsval_release(shadowed_params[i]); } + free(shadowed_params);
jsdisp_release(frame->arguments_obj); }
+HRESULT fill_shadowed_params(script_ctx_t *ctx, call_frame_t *frame) +{ + ArgumentsInstance *arguments = arguments_from_jsdisp(frame->arguments_obj); + HRESULT hres; + unsigned i; + + if(!frame->function->shadowed_param_cnt || !frame->arguments_obj) + return S_OK; + + for(i = 0; i < frame->function->param_cnt; i++) { + if(frame->function->shadowed_params_idx[i] == ~0) + continue; + hres = jsval_copy(ctx->stack[frame->arguments_off + i], arguments->shadowed_params + arguments->frame->function->shadowed_params_idx[i]); + if(FAILED(hres)) + return hres; + } + + return S_OK; +} + 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..adb5be67c8e 100644 --- a/dlls/jscript/tests/lang.js +++ b/dlls/jscript/tests/lang.js @@ -306,6 +306,16 @@ argumentsTest(); ok(arguments === 1, "arguments = " + arguments); })();
+// duplicated argument names are shadowed by the last argument with the same name +(function(a, a, b, c) { + 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]); +})(1, 2, 3, 4); + (function callAsExprTest() { ok(callAsExprTest.arguments === null, "callAsExprTest.arguments = " + callAsExprTest.arguments); })(1,2);