diff --git a/dlls/jscript/set.c b/dlls/jscript/set.c index bdfe7b75e55..f3e8fe664c8 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 6d06dad9fc7..74e871e47f1 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 43a6ce77350..489220790b2 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); } ]