Gabriel Ivăncescu : jscript: Implement Object.prototype.__proto__ property.

Alexandre Julliard julliard at winehq.org
Wed Nov 3 16:42:02 CDT 2021


Module: wine
Branch: master
Commit: 46367d1ecc8a016ab7c7af9ec107e33c6d46fc26
URL:    https://source.winehq.org/git/wine.git/?a=commit;h=46367d1ecc8a016ab7c7af9ec107e33c6d46fc26

Author: Gabriel Ivăncescu <gabrielopcode at gmail.com>
Date:   Tue Nov  2 20:07:11 2021 +0200

jscript: Implement Object.prototype.__proto__ property.

Signed-off-by: Gabriel Ivăncescu <gabrielopcode at gmail.com>
Signed-off-by: Jacek Caban <jacek at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 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);
+});




More information about the wine-cvs mailing list