From 46367d1ecc8a016ab7c7af9ec107e33c6d46fc26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= Date: Tue, 2 Nov 2021 20:07:11 +0200 Subject: [PATCH] jscript: Implement Object.prototype.__proto__ property. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gabriel Ivăncescu Signed-off-by: Jacek Caban Signed-off-by: Alexandre Julliard --- dlls/jscript/dispex.c | 23 ++++++++++++- dlls/jscript/jscript.h | 2 ++ dlls/jscript/object.c | 28 ++++++++++++++++ dlls/mshtml/tests/documentmode.js | 55 +++++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+), 1 deletion(-) diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c index 4c57a690f5f..e64a5203564 100644 --- a/dlls/jscript/dispex.c +++ b/dlls/jscript/dispex.c @@ -461,7 +461,8 @@ 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) + if(prop_iter->type == PROP_ACCESSOR || + (prop_iter->type == PROP_BUILTIN && prop_iter->u.p->setter)) prop = prop_iter; } @@ -2654,6 +2655,26 @@ HRESULT jsdisp_define_data_property(jsdisp_t *obj, const WCHAR *name, unsigned f return jsdisp_define_property(obj, name, &prop_desc); } +HRESULT jsdisp_change_prototype(jsdisp_t *obj, jsdisp_t *proto) +{ + DWORD i; + + if(obj->prototype == proto) + return S_OK; + + if(obj->prototype) { + for(i = 0; i < obj->prop_cnt; i++) + if(obj->props[i].type == PROP_PROTREF) + obj->props[i].type = PROP_DELETED; + jsdisp_release(obj->prototype); + } + + obj->prototype = proto; + if(proto) + jsdisp_addref(proto); + return S_OK; +} + void jsdisp_freeze(jsdisp_t *obj, BOOL seal) { unsigned int i; diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 90a5e150e62..69897cd20c0 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -104,6 +104,7 @@ HRESULT get_dispatch_typeinfo(ITypeInfo**) DECLSPEC_HIDDEN; #define PROPF_VERSION_SHIFT 16 #define PROPF_HTML (SCRIPTLANGUAGEVERSION_HTML << PROPF_VERSION_SHIFT) #define PROPF_ES5 ((SCRIPTLANGUAGEVERSION_HTML|SCRIPTLANGUAGEVERSION_ES5) << PROPF_VERSION_SHIFT) +#define PROPF_ES6 ((SCRIPTLANGUAGEVERSION_HTML|SCRIPTLANGUAGEVERSION_ES6) << PROPF_VERSION_SHIFT) /* * This is our internal dispatch flag informing calee that it's called directly from interpreter. @@ -326,6 +327,7 @@ HRESULT jsdisp_define_property(jsdisp_t*,const WCHAR*,property_desc_t*) DECLSPEC HRESULT jsdisp_define_data_property(jsdisp_t*,const WCHAR*,unsigned,jsval_t) DECLSPEC_HIDDEN; HRESULT jsdisp_next_prop(jsdisp_t*,DISPID,enum jsdisp_enum_type,DISPID*) DECLSPEC_HIDDEN; HRESULT jsdisp_get_prop_name(jsdisp_t*,DISPID,jsstr_t**); +HRESULT jsdisp_change_prototype(jsdisp_t*,jsdisp_t*) DECLSPEC_HIDDEN; void jsdisp_freeze(jsdisp_t*,BOOL) DECLSPEC_HIDDEN; BOOL jsdisp_is_frozen(jsdisp_t*,BOOL) DECLSPEC_HIDDEN; diff --git a/dlls/jscript/object.c b/dlls/jscript/object.c index fd5ab52a8ae..89684f5fb6f 100644 --- a/dlls/jscript/object.c +++ b/dlls/jscript/object.c @@ -216,6 +216,33 @@ static HRESULT Object_isPrototypeOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD fla return S_OK; } +static HRESULT Object_get_proto_(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) +{ + TRACE("%p\n", jsthis); + + if(r) + *r = jsthis->prototype + ? jsval_obj(jsdisp_addref(jsthis->prototype)) + : jsval_null(); + return S_OK; +} + +static HRESULT Object_set_proto_(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t value) +{ + jsdisp_t *proto; + + TRACE("%p\n", jsthis); + + 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; + } + + return jsdisp_change_prototype(jsthis, proto); +} + static HRESULT Object_get_value(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) { jsstr_t *ret; @@ -236,6 +263,7 @@ 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 3307f2c1848..02a5f5f3f75 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -1025,3 +1025,58 @@ sync_test("elem_attr", function() { r = elem.getAttribute("className"); ok(r === "cls3", "className attr = " + r); }); + +sync_test("__proto__", function() { + var v = document.documentMode; + var r, x = 42; + + if(v < 11) { + ok(x.__proto__ === undefined, "x.__proto__ = " + x.__proto__); + ok(!("__proto__" in Object), "Object.__proto__ = " + Object.__proto__); + return; + } + + ok(x.__proto__ === Number.prototype, "x.__proto__ = " + x.__proto__); + ok(Object.__proto__ === Function.prototype, "Object.__proto__ = " + Object.__proto__); + ok(Object.prototype.__proto__ === null, "Object.prototype.__proto__ = " + Object.prototype.__proto__); + ok(Object.prototype.hasOwnProperty("__proto__"), "__proto__ is not a property of Object.prototype"); + ok(!Object.prototype.hasOwnProperty.call(x, "__proto__"), "__proto__ is a property of x"); + + x.__proto__ = Object.prototype; + ok(x.__proto__ === Number.prototype, "x.__proto__ set to Object.prototype = " + x.__proto__); + ok(!Object.prototype.hasOwnProperty.call(x, "__proto__"), "__proto__ is a property of x after set to Object.prototype"); + x = {}; + x.__proto__ = null; + r = Object.getPrototypeOf(x); + ok(x.__proto__ === undefined, "x.__proto__ after set to null = " + x.__proto__); + ok(r === null, "getPrototypeOf(x) after set to null = " + r); + + function check(expect, msg) { + var r = Object.getPrototypeOf(x); + ok(x.__proto__ === expect, "x.__proto__ " + msg + " = " + x.__proto__); + ok(r === expect, "getPrototypeOf(x) " + msg + " = " + r); + ok(!Object.prototype.hasOwnProperty.call(x, "__proto__"), "__proto__ is a property of x " + msg); + } + + x = {}; + check(Object.prototype, "after x set to {}"); + x.__proto__ = Number.prototype; + check(Number.prototype, "after set to Number.prototype"); + x.__proto__ = Object.prototype; + check(Object.prototype, "after re-set to Object.prototype"); + + function ctor() { } + var obj = new ctor(); + x.__proto__ = obj; + check(obj, "after set to obj"); + x.__proto__ = ctor.prototype; + check(obj.__proto__, "after set to ctor.prototype"); + ok(obj.__proto__ === ctor.prototype, "obj.__proto__ !== ctor.prototype"); + + r = (delete x.__proto__); + todo_wine. + ok(r, "delete x.__proto__ returned " + r); + 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); +});