[PATCH v2 1/2] jscript: Handle detached scope objects 'this' in all calls.

Gabriel Ivăncescu gabrielopcode at gmail.com
Fri May 6 08:49:17 CDT 2022


Not just for interpreted functions. And set to undefined in ES5+ modes.

Signed-off-by: Gabriel Ivăncescu <gabrielopcode at gmail.com>
---

Supersedes 234852-234853.

 dlls/jscript/engine.c      | 12 ++++--------
 dlls/jscript/function.c    | 30 ++++++++++++++++++++++++------
 dlls/jscript/tests/api.js  |  2 ++
 dlls/jscript/tests/lang.js |  3 +++
 dlls/mshtml/tests/es5.js   | 15 +++++++++++++++
 5 files changed, 48 insertions(+), 14 deletions(-)

diff --git a/dlls/jscript/engine.c b/dlls/jscript/engine.c
index 73b9d86..83005eb 100644
--- a/dlls/jscript/engine.c
+++ b/dlls/jscript/engine.c
@@ -3300,16 +3300,12 @@ HRESULT exec_source(script_ctx_t *ctx, DWORD flags, bytecode_t *bytecode, functi
         }
     }
 
-    /* ECMA-262 3rd Edition    11.2.3.7 */
+    /* FIXME: Workaround for ES5 mode because we don't have the JS global == window yet */
     if(this_obj) {
-        jsdisp_t *jsthis;
+        jsdisp_t *jsthis = to_jsdisp(this_obj);
 
-        jsthis = iface_to_jsdisp(this_obj);
-        if(jsthis) {
-            if(jsthis->builtin_info->class == JSCLASS_GLOBAL || jsthis->builtin_info->class == JSCLASS_NONE)
-                this_obj = NULL;
-            jsdisp_release(jsthis);
-        }
+        if(jsthis && jsthis->builtin_info->class == JSCLASS_GLOBAL)
+            this_obj = NULL;
     }
 
     if(ctx->call_ctx && (flags & EXEC_EVAL)) {
diff --git a/dlls/jscript/function.c b/dlls/jscript/function.c
index 12511bb..942ec9d 100644
--- a/dlls/jscript/function.c
+++ b/dlls/jscript/function.c
@@ -247,6 +247,24 @@ void detach_arguments_object(jsdisp_t *args_disp)
     jsdisp_release(frame->arguments_obj);
 }
 
+/* ECMA-262 3rd Edition    11.2.3.7
+   ECMA-262 5.1 Edition    11.2.3.6 */
+static inline HRESULT call(script_ctx_t *ctx, FunctionInstance *function, jsval_t vthis, unsigned flags,
+        unsigned argc, jsval_t *argv, jsval_t *r)
+{
+    jsdisp_t *jsthis;
+
+    if(is_object_instance(vthis) && (jsthis = to_jsdisp(get_object(vthis)))) {
+        if(ctx->version < SCRIPTLANGUAGEVERSION_ES5) {
+            if(jsthis->builtin_info->class == JSCLASS_GLOBAL || jsthis->builtin_info->class == JSCLASS_NONE)
+                vthis = jsval_null();
+        }else if(jsthis->builtin_info->class == JSCLASS_NONE)
+            vthis = jsval_undefined();
+    }
+
+    return function->vtbl->call(ctx, function, vthis, flags, argc, argv, r);
+}
+
 HRESULT Function_invoke(jsdisp_t *func_this, IDispatch *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
 {
     FunctionInstance *function;
@@ -256,7 +274,7 @@ HRESULT Function_invoke(jsdisp_t *func_this, IDispatch *jsthis, WORD flags, unsi
     assert(is_class(func_this, JSCLASS_FUNCTION));
     function = function_from_jsdisp(func_this);
 
-    return function->vtbl->call(function->dispex.ctx, function, jsthis ? jsval_disp(jsthis) : jsval_null(), flags, argc, argv, r);
+    return call(function->dispex.ctx, function, jsthis ? jsval_disp(jsthis) : jsval_null(), flags, argc, argv, r);
 }
 
 static HRESULT Function_get_length(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
@@ -378,7 +396,7 @@ static HRESULT Function_apply(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsi
 
     if(SUCCEEDED(hres)) {
         if(function) {
-            hres = function->vtbl->call(ctx, function, this_val, flags, cnt, args, r);
+            hres = call(ctx, function, this_val, flags, cnt, args, r);
         }else {
             jsval_t res;
             hres = disp_call_value(ctx, get_object(vthis), is_object_instance(this_val) ? get_object(this_val) : NULL,
@@ -429,7 +447,7 @@ static HRESULT Function_call(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsig
         cnt = argc-1;
     }
 
-    hres = function->vtbl->call(ctx, function, this_val, flags, cnt, argv + 1, r);
+    hres = call(ctx, function, this_val, flags, cnt, argv + 1, r);
 
     jsval_release(this_val);
     return hres;
@@ -483,7 +501,7 @@ HRESULT Function_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned ar
         return E_FAIL;
     }
 
-    return function->vtbl->call(ctx, function, vthis, flags, argc, argv, r);
+    return call(ctx, function, vthis, flags, argc, argv, r);
 }
 
 HRESULT Function_get_value(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
@@ -839,8 +857,8 @@ static HRESULT BindFunction_call(script_ctx_t *ctx, FunctionInstance *func, jsva
             memcpy(call_args + function->argc, argv, argc * sizeof(*call_args));
     }
 
-    hres = function->target->vtbl->call(ctx, function->target, function->this ? jsval_disp(function->this) : jsval_null(),
-                                        flags, call_argc, call_args, r);
+    hres = call(ctx, function->target, function->this ? jsval_disp(function->this) : jsval_null(),
+                flags, call_argc, call_args, r);
 
     heap_free(call_args);
     return hres;
diff --git a/dlls/jscript/tests/api.js b/dlls/jscript/tests/api.js
index e81bbea..1368f39 100644
--- a/dlls/jscript/tests/api.js
+++ b/dlls/jscript/tests/api.js
@@ -2634,6 +2634,8 @@ testException(function() {null.toString();}, "E_OBJECT_EXPECTED");
 testException(function() {RegExp.prototype.toString.call(new Object());}, "E_REGEXP_EXPECTED");
 testException(function() {/a/.lastIndex();}, "E_NOT_FUNC");
 testException(function() {"a".length();}, "E_NOT_FUNC");
+testException(function() {((function() { var f = Number.prototype.toString; return (function() { return f(); }); })())();}, "E_NOT_NUM");
+testException(function() {((function() { var f = Object.prototype.hasOwnProperty; return (function() { return f("f"); }); })())();}, "E_OBJECT_EXPECTED");
 
 testException(function() { return arguments.callee(); }, "E_STACK_OVERFLOW");
 
diff --git a/dlls/jscript/tests/lang.js b/dlls/jscript/tests/lang.js
index cf08423..67e4576 100644
--- a/dlls/jscript/tests/lang.js
+++ b/dlls/jscript/tests/lang.js
@@ -310,6 +310,9 @@ argumentsTest();
     ok(callAsExprTest.arguments === null, "callAsExprTest.arguments = " + callAsExprTest.arguments);
 })(1,2);
 
+tmp = ((function() { var f = function() {return this}; return (function() { return f(); }); })())();
+ok(tmp === this, "detached scope function call this != global this");
+
 tmp = (function() {1;})();
 ok(tmp === undefined, "tmp = " + tmp);
 tmp = eval("1;");
diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js
index 8238e5d..fb756ac 100644
--- a/dlls/mshtml/tests/es5.js
+++ b/dlls/mshtml/tests/es5.js
@@ -1784,6 +1784,21 @@ sync_test("let scope instances", function() {
     ok(f() == 2, "f() = " + f());
 });
 
+sync_test("builtins detached scope this", function() {
+    try {
+        ((function() { var f = Number.prototype.toString; return (function() { return f(); }); })())();
+    }catch(ex) {
+        var n = ex.number >>> 0;
+        ok(n === JS_E_NUMBER_EXPECTED, "Number.toString threw " + n);
+    }
+
+    var r = ((function() { var f = Object.prototype.toString; return (function() { return f(); }); })())();
+    ok(r === "[object Undefined]", "Object.toString returned " + r);
+
+    var r = ((function() { return (function() { return this; }); })())();
+    ok(r === window, "detached scope this = " + r);
+});
+
 sync_test("functions scope", function() {
     function f(){ return 1; }
     function f(){ return 2; }
-- 
2.34.1




More information about the wine-devel mailing list