[PATCH 04/10] jscript: Defer lookup to the actual locals for function statements.

Gabriel Ivăncescu gabrielopcode at gmail.com
Tue Apr 12 09:47:33 CDT 2022


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 at 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() {
-- 
2.34.1




More information about the wine-devel mailing list