[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