[PATCH 3/5] jscript: Make Object.prototype.__proto__ an actual accessor.

Gabriel Ivăncescu gabrielopcode at gmail.com
Mon Mar 21 10:58:30 CDT 2022


We have to define it after the constructors are initiated.

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

__proto__ seems to be specially treated as an actual accessor builtin on
native; the other builtin accessor props are already tested to be values
when retrieved via getOwnPropertyDescriptor, despite what the spec says
(see "getOwnPropertyDescriptor" test in mshtml/tests/es5.js).

This also allows us to get rid of the workaround treating builtins with
setters as accessors on prop_put (which was just for __proto__ anyway)
which prevented tests from failing.

 dlls/jscript/dispex.c             |  3 +-
 dlls/jscript/global.c             | 29 +++++++++++++
 dlls/jscript/jscript.h            |  2 +
 dlls/jscript/object.c             | 71 +++++++++++++++++++++++--------
 dlls/mshtml/tests/documentmode.js | 60 ++++++++++++++++++++++++++
 5 files changed, 146 insertions(+), 19 deletions(-)

diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c
index 03062ee..a74840e 100644
--- a/dlls/jscript/dispex.c
+++ b/dlls/jscript/dispex.c
@@ -488,8 +488,7 @@ static HRESULT prop_put(jsdisp_t *This, dispex_prop_t *prop, jsval_t val)
             prop_iter = prototype_iter->props + prop_iter->u.ref;
         } while(prop_iter->type == PROP_PROTREF);
 
-        if(prop_iter->type == PROP_ACCESSOR ||
-           (prop_iter->type == PROP_BUILTIN && prop_iter->u.p->setter))
+        if(prop_iter->type == PROP_ACCESSOR)
             prop = prop_iter;
     }
 
diff --git a/dlls/jscript/global.c b/dlls/jscript/global.c
index b9e3d89..c0ed954 100644
--- a/dlls/jscript/global.c
+++ b/dlls/jscript/global.c
@@ -911,6 +911,31 @@ static const builtin_info_t JSGlobal_info = {
     NULL
 };
 
+static HRESULT init_object_prototype_accessors(script_ctx_t *ctx, jsdisp_t *object_prototype)
+{
+    property_desc_t desc;
+    HRESULT hres = S_OK;
+
+    /* __proto__ is an actual accessor on native, despite being a builtin */
+    if(ctx->version >= SCRIPTLANGUAGEVERSION_ES6) {
+        desc.flags = PROPF_CONFIGURABLE;
+        desc.mask  = PROPF_CONFIGURABLE | PROPF_ENUMERABLE;
+        desc.explicit_getter = desc.explicit_setter = TRUE;
+        desc.explicit_value = FALSE;
+
+        hres = create_builtin_function(ctx, Object_get_proto_, NULL, NULL, PROPF_METHOD, NULL, &desc.getter);
+        if(SUCCEEDED(hres)) {
+            hres = create_builtin_function(ctx, Object_set_proto_, NULL, NULL, PROPF_METHOD|1, NULL, &desc.setter);
+            if(SUCCEEDED(hres)) {
+                hres = jsdisp_define_property(object_prototype, L"__proto__", &desc);
+                jsdisp_release(desc.setter);
+            }
+            jsdisp_release(desc.getter);
+        }
+    }
+    return hres;
+}
+
 static HRESULT init_constructors(script_ctx_t *ctx, jsdisp_t *object_prototype)
 {
     HRESULT hres;
@@ -1073,6 +1098,10 @@ HRESULT init_global(script_ctx_t *ctx)
     if(FAILED(hres))
         return hres;
 
+    hres = init_object_prototype_accessors(ctx, ctx->object_prototype);
+    if(FAILED(hres))
+        return hres;
+
     hres = create_math(ctx, &math);
     if(FAILED(hres))
         return hres;
diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h
index 41b6c01..4a4d303 100644
--- a/dlls/jscript/jscript.h
+++ b/dlls/jscript/jscript.h
@@ -451,6 +451,8 @@ BOOL bool_obj_value(jsdisp_t*) DECLSPEC_HIDDEN;
 unsigned array_get_length(jsdisp_t*) DECLSPEC_HIDDEN;
 
 HRESULT JSGlobal_eval(script_ctx_t*,jsval_t,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN;
+HRESULT Object_get_proto_(script_ctx_t*,jsval_t,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN;
+HRESULT Object_set_proto_(script_ctx_t*,jsval_t,WORD,unsigned,jsval_t*,jsval_t*) DECLSPEC_HIDDEN;
 
 static inline BOOL is_class(jsdisp_t *jsdisp, jsclass_t class)
 {
diff --git a/dlls/jscript/object.c b/dlls/jscript/object.c
index c01fbcc..f8f1407 100644
--- a/dlls/jscript/object.c
+++ b/dlls/jscript/object.c
@@ -289,31 +289,69 @@ done:
     return hres;
 }
 
-static HRESULT Object_get_proto_(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
+HRESULT Object_get_proto_(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
 {
-    TRACE("%p\n", jsthis);
+    jsdisp_t *jsthis;
+    IDispatch *disp;
+    HRESULT hres;
 
-    if(r)
-        *r = jsthis->prototype
-            ? jsval_obj(jsdisp_addref(jsthis->prototype))
-            : jsval_null();
-    return S_OK;
+    TRACE("%s\n", debugstr_jsval(vthis));
+
+    hres = to_object(ctx, vthis, &disp);
+    if(FAILED(hres))
+        return hres;
+
+    if(!r)
+        goto done;
+
+    if(!(jsthis = to_jsdisp(disp))) {
+        FIXME("Host object this\n");
+        hres = E_FAIL;
+        goto done;
+    }
+
+    *r = jsthis->prototype
+        ? jsval_obj(jsdisp_addref(jsthis->prototype))
+        : jsval_null();
+done:
+    IDispatch_Release(disp);
+    return hres;
 }
 
-static HRESULT Object_set_proto_(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t value)
+HRESULT Object_set_proto_(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
 {
-    jsdisp_t *proto;
+    jsdisp_t *jsthis, *proto;
+    HRESULT hres;
 
-    TRACE("%p\n", jsthis);
+    TRACE("%s\n", debugstr_jsval(vthis));
 
-    if(is_undefined(value) || is_null(value))
-        proto = NULL;
-    else if(!is_object_instance(value) || !(proto = to_jsdisp(get_object(value)))) {
-        FIXME("not an object\n");
-        return E_FAIL;
+    if(is_undefined(vthis) || is_null(vthis))
+        return JS_E_OBJECT_EXPECTED;
+    if(!argc) {
+        if(r)
+            *r = jsval_undefined();
+        return S_OK;
     }
+    if(!is_object_instance(vthis) || !(jsthis = to_jsdisp(get_object(vthis))))
+        goto done;
 
-    return jsdisp_change_prototype(jsthis, proto);
+    if(is_null(argv[0])) {
+        proto = NULL;
+    }else if(is_object_instance(argv[0])) {
+        proto = to_jsdisp(get_object(argv[0]));
+        if(!proto) {
+            FIXME("Host object value\n");
+            return E_FAIL;
+        }
+    }else
+        goto done;
+
+    hres = jsdisp_change_prototype(jsthis, proto);
+    if(FAILED(hres))
+        return hres;
+
+done:
+    return r ? jsval_copy(argv[0], r) : S_OK;
 }
 
 static void Object_destructor(jsdisp_t *dispex)
@@ -322,7 +360,6 @@ static void Object_destructor(jsdisp_t *dispex)
 }
 
 static const builtin_prop_t Object_props[] = {
-    {L"__proto__",             NULL, PROPF_ES6,              Object_get_proto_, Object_set_proto_},
     {L"hasOwnProperty",        Object_hasOwnProperty,        PROPF_METHOD|1},
     {L"isPrototypeOf",         Object_isPrototypeOf,         PROPF_METHOD|1},
     {L"propertyIsEnumerable",  Object_propertyIsEnumerable,  PROPF_METHOD|1},
diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js
index 243a8fa..43ebd56 100644
--- a/dlls/mshtml/tests/documentmode.js
+++ b/dlls/mshtml/tests/documentmode.js
@@ -1330,6 +1330,66 @@ sync_test("__proto__", function() {
     ok(Object.prototype.hasOwnProperty("__proto__"), "__proto__ is not a property of Object.prototype after delete");
     r = Object.getPrototypeOf(x);
     ok(r === ctor.prototype, "x.__proto__ after delete = " + r);
+
+    var desc = Object.getOwnPropertyDescriptor(Object.prototype, "__proto__");
+    ok(desc.value === undefined, "__proto__ value = " + desc.value);
+    ok(Object.getPrototypeOf(desc.get) === Function.prototype, "__proto__ getter not a function");
+    ok(Object.getPrototypeOf(desc.set) === Function.prototype, "__proto__ setter not a function");
+    ok(desc.get.length === 0, "__proto__ getter length = " + desc.get.length);
+    ok(desc.set.length === 1, "__proto__ setter length = " + desc.set.length);
+
+    r = desc.get.call(x, 1, 2, 3, 4);
+    ok(r === x.__proto__, "calling __proto__ getter on x returned " + r);
+
+    r = desc.set.call(x, obj);
+    ok(r === obj, "calling __proto__ setter(obj) on x returned " + r);
+    check(obj, "after set to obj via calling setter");
+    r = desc.set.call(x, 42);
+    ok(r === 42, "calling __proto__ setter(42) on x returned " + r);
+    check(obj, "after set to obj via calling setter(42)");
+    r = desc.set.call(x, "foo");
+    ok(r === "foo", "calling __proto__ setter('foo') on x returned " + r);
+    check(obj, "after set to obj via calling setter('foo')");
+    r = desc.set.call(x);
+    ok(r === undefined, "calling __proto__ setter() on x returned " + r);
+    r = desc.set.call(true, obj);
+    ok(r === obj, "calling __proto__ setter(obj) on true value returned " + r);
+    x = true;
+    r = desc.set.call(x, obj);
+    ok(r === obj, "calling __proto__ setter(obj) on x set to true returned " + r);
+    ok(x.__proto__ === Boolean.prototype, "true value __proto__ after set to obj = " + x.__proto__);
+    x = new Boolean(true);
+    r = desc.set.call(x, obj);
+    ok(r === obj, "calling __proto__ setter(obj) on x set to Boolean(true) returned " + r);
+    ok(x.__proto__ === obj, "Boolean(true) __proto__ after set to obj = " + x.__proto__);
+
+    r = desc.get.call(13);
+    ok(r === Number.prototype, "calling __proto__ getter on 13 returned " + r);
+    try {
+        r = desc.get.call(undefined);
+        ok(false, "expected exception calling __proto__ getter on undefined");
+    }catch(e) {
+        ok(e.number === 0xa138f - 0x80000000, "calling __proto__ getter on undefined threw exception " + e.number);
+    }
+    try {
+        r = desc.get.call(null);
+        ok(false, "expected exception calling __proto__ getter on null");
+    }catch(e) {
+        ok(e.number === 0xa138f - 0x80000000, "calling __proto__ getter on null threw exception " + e.number);
+    }
+
+    try {
+        r = desc.set.call(undefined, obj);
+        ok(false, "expected exception calling __proto__ setter on undefined");
+    }catch(e) {
+        ok(e.number === 0xa138f - 0x80000000, "calling __proto__ setter on undefined threw exception " + e.number);
+    }
+    try {
+        r = desc.set.call(null, obj);
+        ok(false, "expected exception calling __proto__ setter on null");
+    }catch(e) {
+        ok(e.number === 0xa138f - 0x80000000, "calling __proto__ setter on null threw exception " + e.number);
+    }
 });
 
 async_test("postMessage", function() {
-- 
2.34.1




More information about the wine-devel mailing list