[PATCH 4/9] jscript: Implement Object.prototype.__defineSetter__.

Gabriel Ivăncescu gabrielopcode at gmail.com
Tue Mar 22 10:41:22 CDT 2022


Signed-off-by: Gabriel Ivăncescu <gabrielopcode at gmail.com>
---
 dlls/jscript/object.c             | 47 +++++++++++++++
 dlls/mshtml/tests/documentmode.js | 95 +++++++++++++++++++++++++++++++
 2 files changed, 142 insertions(+)

diff --git a/dlls/jscript/object.c b/dlls/jscript/object.c
index 5045db3..8b0ec6f 100644
--- a/dlls/jscript/object.c
+++ b/dlls/jscript/object.c
@@ -335,6 +335,52 @@ done:
     return S_OK;
 }
 
+static HRESULT Object_defineSetter(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
+{
+    property_desc_t desc;
+    const WCHAR *name;
+    jsstr_t *name_str;
+    jsdisp_t *jsthis;
+    HRESULT hres;
+
+    TRACE("\n");
+
+    if(!is_object_instance(vthis) || !get_object(vthis) || !(jsthis = to_jsdisp(get_object(vthis))))
+        goto done;
+
+    hres = to_flat_string(ctx, argc ? argv[0] : jsval_undefined(), &name_str, &name);
+    if(FAILED(hres))
+        return hres;
+
+    if(argc < 2 || !is_object_instance(argv[1]) || !get_object(argv[1]))
+        hres = JS_E_FUNCTION_EXPECTED;
+    else {
+        hres = S_OK;
+        desc.setter = to_jsdisp(get_object(argv[1]));
+        if(!desc.setter) {
+            FIXME("setter is not JS object\n");
+            hres = E_NOTIMPL;
+        }
+        /* FIXME: Check IsCallable */
+    }
+
+    if(SUCCEEDED(hres)) {
+        desc.flags = desc.mask = PROPF_CONFIGURABLE | PROPF_ENUMERABLE;
+        desc.explicit_getter = FALSE;
+        desc.explicit_setter = TRUE;
+        desc.explicit_value  = FALSE;
+        desc.getter = NULL;
+        hres = jsdisp_define_property(jsthis, name, &desc);
+    }
+    jsstr_release(name_str);
+    if(FAILED(hres))
+        return hres;
+done:
+    if(r)
+        *r = jsval_undefined();
+    return S_OK;
+}
+
 HRESULT Object_get_proto_(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
 {
     jsdisp_t *jsthis;
@@ -407,6 +453,7 @@ static void Object_destructor(jsdisp_t *dispex)
 
 static const builtin_prop_t Object_props[] = {
     {L"__defineGetter__",      Object_defineGetter,          PROPF_METHOD|PROPF_ES6|2},
+    {L"__defineSetter__",      Object_defineSetter,          PROPF_METHOD|PROPF_ES6|2},
     {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 18dd65e..4a776f8 100644
--- a/dlls/mshtml/tests/documentmode.js
+++ b/dlls/mshtml/tests/documentmode.js
@@ -1503,6 +1503,101 @@ sync_test("__defineGetter__", function() {
     ok(x.bar === "wine", "x.bar with getter = " + x.bar);
 });
 
+sync_test("__defineSetter__", function() {
+    var v = document.documentMode;
+    var r, x = 42;
+
+    if(v < 11) {
+        ok(x.__defineSetter__ === undefined, "x.__defineSetter__ = " + x.__defineSetter__);
+        ok(!("__defineSetter__" in Object), "Object.__defineSetter__ = " + Object.__defineSetter__);
+        return;
+    }
+    ok(Object.prototype.hasOwnProperty("__defineSetter__"), "__defineSetter__ is not a property of Object.prototype");
+    ok(Object.prototype.__defineSetter__.length === 2, "__defineSetter__.length = " + Object.prototype.__defineSetter__.length);
+
+    function getter() { return "wine"; }
+    function setter(val) { this.setterVal = val - 1; }
+
+    r = x.__defineSetter__("foo", setter);
+    ok(r === undefined, "__defineSetter__ on 42 returned " + r);
+    ok(x.foo === undefined, "42.foo = " + x.foo);
+
+    x = {};
+    r = x.__defineSetter__("foo", setter);
+    ok(r === undefined, "__defineSetter__ returned " + r);
+    ok(x.setterVal === undefined, "x.setterVal = " + x.setterVal);
+    x.foo = 13;
+    ok(x.setterVal === 12, "x.setterVal = " + x.setterVal);
+    r = Object.getOwnPropertyDescriptor(x, "foo");
+    ok(r.value === undefined, "x.foo value = " + r.value);
+    ok(r.get === undefined, "x.foo get = " + r.get);
+    ok(r.set === setter, "x.foo set = " + r.set);
+    ok(r.writable === undefined, "x.foo writable = " + r.writable);
+    ok(r.enumerable === true, "x.foo enumerable = " + r.enumerable);
+    ok(r.configurable === true, "x.foo configurable = " + r.configurable);
+
+    Object.defineProperty(x, "foo", { get: getter, set: undefined, configurable: false });
+    r = Object.getOwnPropertyDescriptor(x, "foo");
+    ok(r.value === undefined, "x.foo getter value = " + r.value);
+    ok(r.get === getter, "x.foo getter get = " + r.get);
+    ok(r.set === undefined, "x.foo getter set = " + r.set);
+    ok(r.writable === undefined, "x.foo getter writable = " + r.writable);
+    ok(r.enumerable === true, "x.foo getter enumerable = " + r.enumerable);
+    ok(r.configurable === false, "x.foo getter configurable = " + r.configurable);
+    try {
+        x.__defineSetter__("foo", setter);
+        ok(false, "expected exception calling __defineSetter__ on non-configurable property");
+    }catch(e) {
+        ok(e.number === 0xa13d6 - 0x80000000, "__defineSetter__ on non-configurable property threw exception " + e.number);
+    }
+
+    r = Object.prototype.__defineSetter__.call(undefined, "bar", setter);
+    ok(r === undefined, "__defineSetter__ on undefined returned " + r);
+    r = Object.prototype.__defineSetter__.call(null, "bar", setter);
+    ok(r === undefined, "__defineSetter__ on null returned " + r);
+    r = x.__defineSetter__(null, setter);
+    ok(r === undefined, "__defineSetter__ null prop returned " + r);
+    x["null"] = 100;
+    ok(x.setterVal === 99, "x.setterVal after setting x.null = " + x.setterVal);
+    r = x.__defineSetter__(50, setter);
+    ok(r === undefined, "__defineSetter__ 50 prop returned " + r);
+    x["50"] = 33;
+    ok(x.setterVal === 32, "x.setterVal after setting x.50 = " + x.setterVal);
+
+    try {
+        x.__defineSetter__("bar", true);
+        ok(false, "expected exception calling __defineSetter__ with bool");
+    }catch(e) {
+        ok(e.number === 0xa138a - 0x80000000, "__defineSetter__ with bool threw exception " + e.number);
+    }
+    try {
+        x.__defineSetter__("bar", undefined);
+        ok(false, "expected exception calling __defineSetter__ with undefined");
+    }catch(e) {
+        ok(e.number === 0xa138a - 0x80000000, "__defineSetter__ with undefined threw exception " + e.number);
+    }
+    try {
+        x.__defineSetter__("bar", null);
+        ok(false, "expected exception calling __defineSetter__ with null");
+    }catch(e) {
+        ok(e.number === 0xa138a - 0x80000000, "__defineSetter__ with null threw exception " + e.number);
+    }
+    try {
+        Object.prototype.__defineSetter__.call(x, "bar");
+        ok(false, "expected exception calling __defineSetter__ with only one arg");
+    }catch(e) {
+        ok(e.number === 0xa138a - 0x80000000, "__defineSetter__ with only one arg threw exception " + e.number);
+    }
+
+    x.bar = "test";
+    ok(x.bar === "test", "x.bar = " + x.bar);
+    x.__defineSetter__("bar", setter);
+    ok(x.bar === undefined, "x.bar with setter = " + x.bar);
+    x.bar = 10;
+    ok(x.bar === undefined, "x.bar with setter = " + x.bar);
+    ok(x.setterVal === 9, "x.setterVal after setting bar = " + x.setterVal);
+});
+
 async_test("postMessage", function() {
     var v = document.documentMode;
     var onmessage_called = false;
-- 
2.34.1




More information about the wine-devel mailing list