[PATCH 8/9] jscript: Implement Set on top of Map.

Gabriel Ivăncescu gabrielopcode at gmail.com
Thu Apr 14 11:24:45 CDT 2022


Because a Set is just a Map where key == value.

Signed-off-by: Gabriel Ivăncescu <gabrielopcode at gmail.com>
---
 dlls/jscript/set.c                | 111 ++++++++++++++++++++++++------
 dlls/mshtml/tests/documentmode.js |  65 +++++++++++++++++
 dlls/mshtml/tests/es5.js          |   1 +
 3 files changed, 156 insertions(+), 21 deletions(-)

diff --git a/dlls/jscript/set.c b/dlls/jscript/set.c
index bdfe7b7..f3e8fe6 100644
--- a/dlls/jscript/set.c
+++ b/dlls/jscript/set.c
@@ -26,10 +26,6 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
 
-typedef struct {
-    jsdisp_t dispex;
-} SetInstance;
-
 typedef struct {
     jsdisp_t dispex;
     struct wine_rb_tree map;
@@ -105,6 +101,21 @@ static HRESULT get_map_this(jsval_t vthis, MapInstance **ret)
     return S_OK;
 }
 
+static HRESULT get_set_this(jsval_t vthis, MapInstance **ret)
+{
+    jsdisp_t *jsdisp;
+
+    if(!is_object_instance(vthis))
+        return JS_E_OBJECT_EXPECTED;
+    if(!(jsdisp = to_jsdisp(get_object(vthis))) || !is_class(jsdisp, JSCLASS_SET)) {
+        WARN("not a Set object passed as 'this'\n");
+        return JS_E_MAP_EXPECTED;
+    }
+
+    *ret = CONTAINING_RECORD(jsdisp, MapInstance, dispex);
+    return S_OK;
+}
+
 static struct jsval_map_entry *get_map_entry(MapInstance *map, jsval_t key)
 {
     struct wine_rb_entry *entry;
@@ -416,36 +427,91 @@ static HRESULT Map_constructor(script_ctx_t *ctx, jsval_t vthis, WORD flags, uns
 static HRESULT Set_add(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
         jsval_t *r)
 {
-    FIXME("%p\n", debugstr_jsval(vthis));
-    return E_NOTIMPL;
+    jsval_t key = argc ? argv[0] : jsval_undefined();
+    MapInstance *set;
+    HRESULT hres;
+
+    hres = get_set_this(vthis, &set);
+    if(FAILED(hres))
+        return hres;
+
+    TRACE("%p (%s)\n", set, debugstr_jsval(key));
+
+    return set_map_entry(set, key, key, r);
 }
 
 static HRESULT Set_clear(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
         jsval_t *r)
 {
-    FIXME("%p\n", debugstr_jsval(vthis));
-    return E_NOTIMPL;
+    MapInstance *set;
+    HRESULT hres;
+
+    hres = get_set_this(vthis, &set);
+    if(FAILED(hres))
+        return hres;
+
+    TRACE("%p\n", set);
+
+    while(!list_empty(&set->entries)) {
+        struct jsval_map_entry *entry = LIST_ENTRY(list_head(&set->entries), struct jsval_map_entry, list_entry);
+        delete_map_entry(set, entry);
+    }
+
+    if(r) *r = jsval_undefined();
+    return S_OK;
 }
 
 static HRESULT Set_delete(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
         jsval_t *r)
 {
-    FIXME("%p\n", debugstr_jsval(vthis));
-    return E_NOTIMPL;
+    jsval_t key = argc ? argv[0] : jsval_undefined();
+    struct jsval_map_entry *entry;
+    MapInstance *set;
+    HRESULT hres;
+
+    hres = get_set_this(vthis, &set);
+    if(FAILED(hres))
+        return hres;
+
+    TRACE("%p (%s)\n", set, debugstr_jsval(key));
+
+    if((entry = get_map_entry(set, key))) delete_map_entry(set, entry);
+    if(r) *r = jsval_bool(!!entry);
+    return S_OK;
 }
 
 static HRESULT Set_forEach(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
         jsval_t *r)
 {
-    FIXME("%p\n", debugstr_jsval(vthis));
-    return E_NOTIMPL;
+    MapInstance *set;
+    HRESULT hres;
+
+    hres = get_set_this(vthis, &set);
+    if(FAILED(hres))
+        return hres;
+
+    TRACE("%p (%s)\n", set, debugstr_jsval(argc ? argv[0] : jsval_undefined()));
+
+    return iterate_map(set, ctx, argc, argv, r);
 }
 
 static HRESULT Set_has(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
         jsval_t *r)
 {
-    FIXME("%p\n", debugstr_jsval(vthis));
-    return E_NOTIMPL;
+    jsval_t key = argc ? argv[0] : jsval_undefined();
+    struct jsval_map_entry *entry;
+    MapInstance *set;
+    HRESULT hres;
+
+    hres = get_set_this(vthis, &set);
+    if(FAILED(hres))
+        return hres;
+
+    TRACE("%p (%s)\n", set, debugstr_jsval(key));
+
+    entry = get_map_entry(set, key);
+    if(r) *r = jsval_bool(!!entry);
+    return S_OK;
 }
 
 static HRESULT Set_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
@@ -455,7 +521,7 @@ static HRESULT Set_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned
     return E_NOTIMPL;
 }
 
-static const builtin_prop_t Set_props[] = {
+static const builtin_prop_t Set_prototype_props[] = {
     {L"add",        Set_add,       PROPF_METHOD|1},
     {L"clear",      Set_clear,     PROPF_METHOD},
     {L"delete" ,    Set_delete,    PROPF_METHOD|1},
@@ -464,10 +530,10 @@ static const builtin_prop_t Set_props[] = {
 };
 
 static const builtin_info_t Set_prototype_info = {
-    JSCLASS_SET,
+    JSCLASS_OBJECT,
     Set_value,
-    ARRAY_SIZE(Set_props),
-    Set_props,
+    ARRAY_SIZE(Set_prototype_props),
+    Set_prototype_props,
     NULL,
     NULL
 };
@@ -475,15 +541,16 @@ static const builtin_info_t Set_prototype_info = {
 static const builtin_info_t Set_info = {
     JSCLASS_SET,
     Set_value,
-    0, NULL,
-    NULL,
+    ARRAY_SIZE(Map_props),
+    Map_props,
+    Map_destructor,
     NULL
 };
 
 static HRESULT Set_constructor(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
         jsval_t *r)
 {
-    SetInstance *set;
+    MapInstance *set;
     HRESULT hres;
 
     switch(flags) {
@@ -499,6 +566,8 @@ static HRESULT Set_constructor(script_ctx_t *ctx, jsval_t vthis, WORD flags, uns
         if(FAILED(hres))
             return hres;
 
+        wine_rb_init(&set->map, jsval_map_compare);
+        list_init(&set->entries);
         *r = jsval_obj(&set->dispex);
         return S_OK;
 
diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js
index 6d06dad..74e871e 100644
--- a/dlls/mshtml/tests/documentmode.js
+++ b/dlls/mshtml/tests/documentmode.js
@@ -912,6 +912,12 @@ sync_test("set_obj", function() {
 
     function test_length(name, len) {
         ok(Set.prototype[name].length === len, "Set.prototype." + name + " = " + Set.prototype[name].length);
+        try {
+            Set.prototype[name].call({}, 0);
+            ok(false, "expected exception calling Set.prototype." + name + "(object)");
+        }catch(e) {
+            ok(e.number === 0xa13fc - 0x80000000, "Set.prototype." + name + "(object) threw " + e.number);
+        }
     }
     test_length("add", 1);
     test_length("clear", 0);
@@ -924,6 +930,65 @@ sync_test("set_obj", function() {
 
     r = Object.prototype.toString.call(s);
     ok(r === "[object Object]", "toString returned " + r);
+
+    r = s.has(-0);
+    ok(r === false, "has(-0) returned " + r);
+    ok(s.size === 0, "size = " + s.size);
+
+    r = s.add(42);
+    ok(r === undefined, "add(42) returned " + r);
+    r = s.add(42);
+    ok(r === undefined, "add(42) returned " + r);
+    r = s.add(0);
+    ok(r === undefined, "add(0) returned " + r);
+    r = s.has(-0);
+    ok(r === false, "has(-0) returned " + r);
+    r = s.add(-0);
+    ok(r === undefined, "add(-0) returned " + r);
+    r = s.has(-0);
+    ok(r === true, "has(-0) after add returned " + r);
+    r = s.add("test");
+    ok(r === undefined, "add(test) returned " + r);
+    r = s.add(13);
+    ok(r === undefined, "add(13) returned " + r);
+    r = s.add(s);
+    ok(r === undefined, "add(s) returned " + r);
+
+    r = s["delete"]("test"); /* using s.delete() would break parsing in quirks mode */
+    ok(r === true, "delete(test) returned " + r);
+    r = s["delete"]("test");
+    ok(r === false, "delete(test) returned " + r);
+
+    ok(s.size === 5, "size = " + s.size);
+    s.size = 100;
+    ok(s.size === 5, "size (after set) = " + s.size);
+
+    var a = [];
+    r = s.forEach(function(value, key, obj) {
+        var t = s["delete"](key);
+        ok(t === true, "delete(" + key + ") returned " + r);
+        ok(value === key, "value = " + value + ", key = " + key);
+        ok(obj === s, "set = " + obj);
+        ok(this === a, "this = " + this);
+        a.push(value);
+    }, a);
+    ok(r === undefined, "forEach returned " + r);
+    ok(a.length === 5, "a.length = " + a.length);
+    for(var i = 0; i < a.length; i++)
+        ok(a[i] === [42, 0, -0, 13, s][i], "a[" + i + "] = " + a[i]);
+    ok(s.size === 0, "size = " + s.size);
+
+    s = new Set();
+    ok(s.size === 0, "size = " + s.size);
+    s.add(1);
+    s.add(2);
+    ok(s.size === 2, "size = " + s.size);
+    r = s.clear();
+    ok(r === undefined, "clear returned " + r);
+    ok(s.size === 0, "size = " + s.size);
+
+    s = new Set([1, 2, 3]);
+    ok(s.size === 0, "size = " + s.size);
 });
 
 sync_test("map_obj", function() {
diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js
index 43a6ce7..4892207 100644
--- a/dlls/mshtml/tests/es5.js
+++ b/dlls/mshtml/tests/es5.js
@@ -1376,6 +1376,7 @@ sync_test("builtin_context", function() {
         [ "Number.toFixed",         JS_E_NUMBER_EXPECTED,       function(ctx) { Number.prototype.toFixed.call(ctx); } ],
         [ "Object.isPrototypeOf",   JS_E_OBJECT_EXPECTED,       function(ctx) { Object.prototype.isPrototypeOf.call(ctx, Object); } ],
         [ "RegExp.exec",            JS_E_REGEXP_EXPECTED,       function(ctx) { RegExp.prototype.exec.call(ctx, "foobar"); } ],
+        [ "Set.add",                JS_E_OBJECT_EXPECTED,       function(ctx) { Set.prototype.add.call(ctx, 5); } ],
         [ "String.search",          JS_E_OBJECT_EXPECTED,       function(ctx) { String.prototype.search.call(ctx, /foobar/g); } ],
         [ "String.trim",            JS_E_OBJECT_EXPECTED,       function(ctx) { String.prototype.trim.call(ctx); } ],
         [ "VBArray.dimensions",     JS_E_VBARRAY_EXPECTED,      function(ctx) { VBArray.prototype.dimensions.call(ctx); } ]
-- 
2.34.1




More information about the wine-devel mailing list