From 364e093ab7f8b473a40d31e03d533090c54f3649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= Date: Mon, 21 Mar 2022 17:58:30 +0200 Subject: [PATCH] jscript: Make Object.prototype.__proto__ an actual accessor. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have to define it after the constructors are initiated. Signed-off-by: Gabriel Ivăncescu Signed-off-by: Jacek Caban Signed-off-by: Alexandre Julliard --- dlls/jscript/dispex.c | 3 +- dlls/jscript/global.c | 29 ++++++++++++ dlls/jscript/jscript.h | 2 + dlls/jscript/object.c | 75 +++++++++++++++++++++++-------- dlls/mshtml/tests/documentmode.js | 60 +++++++++++++++++++++++++ 5 files changed, 148 insertions(+), 21 deletions(-) diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c index 03062eebb63..a74840ef701 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 b9e3d89f928..c0ed954230b 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 41b6c01268a..4a4d303f563 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 c01fbcce085..f8f1407a136 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)); -static HRESULT Object_set_proto_(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t value) -{ - jsdisp_t *proto; + hres = to_object(ctx, vthis, &disp); + if(FAILED(hres)) + return hres; - TRACE("%p\n", jsthis); + if(!r) + goto done; - 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(!(jsthis = to_jsdisp(disp))) { + FIXME("Host object this\n"); + hres = E_FAIL; + goto done; } - return jsdisp_change_prototype(jsthis, proto); + *r = jsthis->prototype + ? jsval_obj(jsdisp_addref(jsthis->prototype)) + : jsval_null(); +done: + IDispatch_Release(disp); + return hres; +} + +HRESULT Object_set_proto_(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) +{ + jsdisp_t *jsthis, *proto; + HRESULT hres; + + TRACE("%s\n", debugstr_jsval(vthis)); + + 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; + + 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 243a8faf263..43ebd56eb7e 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() {