Functions declared as function statements have an associated local_ref and can be changed from within themselves by using their name (by literally changing the local variable), while function expressions can not.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/engine.c | 6 ++-- dlls/mshtml/tests/es5.js | 59 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-)
diff --git a/dlls/jscript/engine.c b/dlls/jscript/engine.c index c483f2a..73b9d86 100644 --- a/dlls/jscript/engine.c +++ b/dlls/jscript/engine.c @@ -587,7 +587,8 @@ static HRESULT detach_scope(script_ctx_t *ctx, call_frame_t *frame, scope_chain_ scope->obj = to_disp(scope->jsobj); }
- if (scope == frame->base_scope && func->name && ctx->version >= SCRIPTLANGUAGEVERSION_ES5) + if (scope == frame->base_scope && func->name && func->local_ref == INVALID_LOCAL_REF && + ctx->version >= SCRIPTLANGUAGEVERSION_ES5) jsdisp_propput_name(scope->jsobj, func->name, jsval_obj(jsdisp_addref(frame->function_instance)));
index = scope->scope_index; @@ -716,7 +717,8 @@ static HRESULT identifier_eval(script_ctx_t *ctx, BSTR identifier, exprval_t *re }
/* ECMA-262 5.1 Edition 13 */ - if(func->name && ctx->version >= SCRIPTLANGUAGEVERSION_ES5 && !wcscmp(identifier, func->name)) { + if(func->name && ctx->version >= SCRIPTLANGUAGEVERSION_ES5 && + func->local_ref == INVALID_LOCAL_REF && !wcscmp(identifier, func->name)) { TRACE("returning a function from scope chain\n"); ret->type = EXPRVAL_JSVAL; ret->u.val = jsval_obj(jsdisp_addref(scope->frame->function_instance)); diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index e8ee713..2c08080 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -1691,6 +1691,65 @@ sync_test("functions scope", function() { func_outer(o); func(); ok(ret === o, "ret != o"); + + func_outer = function g(i) + { + ok(ret === (i ? 42 : o), "ret during g(" + i + ") = " + ret); + ok(typeof g == "function", "typeof g == " + typeof g); + + g = function() { ok(false, "redefined g was executed"); } + ret = 42; + if(!i) g(1); + } + func_outer(0); + + function h() + { + ok(typeof h == "function", "typeof h == " + typeof h); + var f = function func_inner(i) + { + if(i === 101) { + ok(h === "string", "h during old h(101) = " + h); + ret = -2; + return; + } + if(i === 100) { + ok(h.toString() === "function foo() {}", "h.toString() during old h(100) = " + h.toString()); + h = "string"; + ok(h === "string", "h during old h(100) after set to string = " + h); + ret = -1; + return; + } + if(i === 1) { + ok(h !== func_inner, "h during h(1) === func_inner"); + return; + } + ok(h === func_inner, "h during h() !== func_inner"); + if(i) { + ok(ret === 42, "ret during h(2) = " + ret); + return; + } + ret = 13; + } + f(1); + h = f; + h(2); + } + func_outer = h; + h(); + ok(ret === 42, "ret after calling h() first time = " + ret); + ok(func_outer !== h, "func_outer after calling h() first time === h"); + func_outer = h; + h(); + ok(ret === 13, "ret after calling h() second time = " + ret); + ok(func_outer === h, "func_outer after calling h() second time === h"); + h = function foo() {} + ok(func_outer !== h, "func_outer after setting h to empty function === h"); + func_outer(100); + ok(ret === -1, "ret after calling old h(100) = " + ret); + ok(h === "string", "h after calling old h(100) = " + h); + func_outer(101); + ok(ret === -2, "ret after calling old h(101) = " + ret); });
sync_test("console", function() {