windows.applicationmodel/tests: Use PathRemoveFileSpecW() instead of PathCchRemoveFil...
[wine.git] / dlls / jscript / set.c
blobac9efbb4da0573aafb17dadc6afa9f4790b5f25a
1 /*
2 * Copyright 2021 Jacek Caban for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include <assert.h>
20 #include <math.h>
22 #include "jscript.h"
24 #include "wine/rbtree.h"
25 #include "wine/debug.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
29 typedef struct {
30 jsdisp_t dispex;
31 struct wine_rb_tree map;
32 struct list entries;
33 size_t size;
34 } MapInstance;
36 struct jsval_map_entry {
37 struct wine_rb_entry entry;
38 jsval_t key;
39 jsval_t value;
42 * We need to maintain a list as well to support traversal in forEach.
43 * If the entry is removed while being processed by forEach, it's
44 * still kept in the list and released later, when it's safe.
46 struct list list_entry;
47 unsigned int ref;
48 BOOL deleted;
51 static int jsval_map_compare(const void *k, const struct wine_rb_entry *e)
53 const struct jsval_map_entry *entry = WINE_RB_ENTRY_VALUE(e, const struct jsval_map_entry, entry);
54 const jsval_t *key = k;
55 union {
56 double d;
57 INT64 n;
58 } bits1, bits2;
60 if(jsval_type(entry->key) != jsval_type(*key))
61 return (int)jsval_type(entry->key) - (int)jsval_type(*key);
63 switch(jsval_type(*key)) {
64 case JSV_UNDEFINED:
65 case JSV_NULL:
66 return 0;
67 case JSV_OBJECT:
68 if(get_object(*key) == get_object(entry->key)) return 0;
69 return get_object(*key) < get_object(entry->key) ? -1 : 1;
70 case JSV_STRING:
71 return jsstr_cmp(get_string(*key), get_string(entry->key));
72 case JSV_NUMBER:
73 if(isnan(get_number(*key))) return isnan(get_number(entry->key)) ? 0 : -1;
74 if(isnan(get_number(entry->key))) return 1;
76 /* native treats -0 differently than 0, so need to compare bitwise */
77 bits1.d = get_number(*key);
78 bits2.d = get_number(entry->key);
79 return (bits1.n == bits2.n) ? 0 : (bits1.n < bits2.n ? -1 : 1);
80 case JSV_BOOL:
81 if(get_bool(*key) == get_bool(entry->key)) return 0;
82 return get_bool(*key) ? 1 : -1;
83 default:
84 assert(0);
85 return 0;
89 static HRESULT get_map_this(script_ctx_t *ctx, jsval_t vthis, MapInstance **ret)
91 jsdisp_t *jsdisp;
93 if(!is_object_instance(vthis))
94 return JS_E_OBJECT_EXPECTED;
95 if(!(jsdisp = to_jsdisp(get_object(vthis))) || !is_class(jsdisp, JSCLASS_MAP)) {
96 WARN("not a Map object passed as 'this'\n");
97 return throw_error(ctx, JS_E_WRONG_THIS, L"Map");
100 *ret = CONTAINING_RECORD(jsdisp, MapInstance, dispex);
101 return S_OK;
104 static HRESULT get_set_this(script_ctx_t *ctx, jsval_t vthis, MapInstance **ret)
106 jsdisp_t *jsdisp;
108 if(!is_object_instance(vthis))
109 return JS_E_OBJECT_EXPECTED;
110 if(!(jsdisp = to_jsdisp(get_object(vthis))) || !is_class(jsdisp, JSCLASS_SET)) {
111 WARN("not a Set object passed as 'this'\n");
112 return throw_error(ctx, JS_E_WRONG_THIS, L"Set");
115 *ret = CONTAINING_RECORD(jsdisp, MapInstance, dispex);
116 return S_OK;
119 static struct jsval_map_entry *get_map_entry(MapInstance *map, jsval_t key)
121 struct wine_rb_entry *entry;
122 if(!(entry = wine_rb_get(&map->map, &key))) return NULL;
123 return CONTAINING_RECORD(entry, struct jsval_map_entry, entry);
126 static void grab_map_entry(struct jsval_map_entry *entry)
128 entry->ref++;
131 static void release_map_entry(struct jsval_map_entry *entry)
133 if(--entry->ref) return;
134 jsval_release(entry->key);
135 jsval_release(entry->value);
136 list_remove(&entry->list_entry);
137 free(entry);
140 static void delete_map_entry(MapInstance *map, struct jsval_map_entry *entry)
142 map->size--;
143 wine_rb_remove(&map->map, &entry->entry);
144 entry->deleted = TRUE;
145 release_map_entry(entry);
148 static HRESULT set_map_entry(MapInstance *map, jsval_t key, jsval_t value, jsval_t *r)
150 struct jsval_map_entry *entry;
151 HRESULT hres;
153 if((entry = get_map_entry(map, key))) {
154 jsval_t val;
155 hres = jsval_copy(value, &val);
156 if(FAILED(hres))
157 return hres;
159 jsval_release(entry->value);
160 entry->value = val;
161 }else {
162 if(!(entry = calloc(1, sizeof(*entry)))) return E_OUTOFMEMORY;
164 hres = jsval_copy(key, &entry->key);
165 if(SUCCEEDED(hres)) {
166 hres = jsval_copy(value, &entry->value);
167 if(FAILED(hres))
168 jsval_release(entry->key);
170 if(FAILED(hres)) {
171 free(entry);
172 return hres;
174 grab_map_entry(entry);
175 wine_rb_put(&map->map, &entry->key, &entry->entry);
176 list_add_tail(&map->entries, &entry->list_entry);
177 map->size++;
180 if(r) *r = jsval_undefined();
181 return S_OK;
184 static HRESULT iterate_map(MapInstance *map, script_ctx_t *ctx, unsigned argc, jsval_t *argv, jsval_t *r)
186 struct list *iter = list_head(&map->entries);
187 jsval_t context_this = jsval_undefined();
188 HRESULT hres;
190 if(!argc || !is_object_instance(argv[0])) {
191 FIXME("invalid callback %s\n", debugstr_jsval(argc ? argv[0] : jsval_undefined()));
192 return E_FAIL;
195 if(argc > 1)
196 context_this = argv[1];
198 while(iter) {
199 struct jsval_map_entry *entry = LIST_ENTRY(iter, struct jsval_map_entry, list_entry);
200 jsval_t args[3], v;
202 if(entry->deleted) {
203 iter = list_next(&map->entries, iter);
204 continue;
207 args[0] = entry->value;
208 args[1] = entry->key;
209 args[2] = jsval_obj(&map->dispex);
210 grab_map_entry(entry);
211 hres = disp_call_value(ctx, get_object(argv[0]), context_this, DISPATCH_METHOD, ARRAY_SIZE(args), args, &v);
212 iter = list_next(&map->entries, iter);
213 release_map_entry(entry);
214 if(FAILED(hres))
215 return hres;
216 jsval_release(v);
219 if(r) *r = jsval_undefined();
220 return S_OK;
223 static HRESULT Map_clear(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
224 jsval_t *r)
226 struct jsval_map_entry *entry, *entry2;
227 MapInstance *map;
228 HRESULT hres;
230 hres = get_map_this(ctx, vthis, &map);
231 if(FAILED(hres))
232 return hres;
234 TRACE("%p\n", map);
236 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &map->entries, struct jsval_map_entry, list_entry)
237 delete_map_entry(map, entry);
239 if(r) *r = jsval_undefined();
240 return S_OK;
243 static HRESULT Map_delete(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
244 jsval_t *r)
246 jsval_t key = argc >= 1 ? argv[0] : jsval_undefined();
247 struct jsval_map_entry *entry;
248 MapInstance *map;
249 HRESULT hres;
251 hres = get_map_this(ctx, vthis, &map);
252 if(FAILED(hres))
253 return hres;
255 TRACE("%p (%s)\n", map, debugstr_jsval(key));
257 if((entry = get_map_entry(map, key))) delete_map_entry(map, entry);
258 if(r) *r = jsval_bool(!!entry);
259 return S_OK;
262 static HRESULT Map_forEach(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
263 jsval_t *r)
265 MapInstance *map;
266 HRESULT hres;
268 hres = get_map_this(ctx, vthis, &map);
269 if(FAILED(hres))
270 return hres;
272 TRACE("%p (%s)\n", map, debugstr_jsval(argc >= 1 ? argv[0] : jsval_undefined()));
274 return iterate_map(map, ctx, argc, argv, r);
277 static HRESULT Map_get(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
278 jsval_t *r)
280 jsval_t key = argc >= 1 ? argv[0] : jsval_undefined();
281 struct jsval_map_entry *entry;
282 MapInstance *map;
283 HRESULT hres;
285 hres = get_map_this(ctx, vthis, &map);
286 if(FAILED(hres))
287 return hres;
289 TRACE("%p (%s)\n", map, debugstr_jsval(key));
291 if(!(entry = get_map_entry(map, key))) {
292 if(r) *r = jsval_undefined();
293 return S_OK;
296 return r ? jsval_copy(entry->value, r) : S_OK;
299 static HRESULT Map_set(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
300 jsval_t *r)
302 jsval_t key = argc >= 1 ? argv[0] : jsval_undefined();
303 jsval_t value = argc >= 2 ? argv[1] : jsval_undefined();
304 MapInstance *map;
305 HRESULT hres;
307 hres = get_map_this(ctx, vthis, &map);
308 if(FAILED(hres))
309 return hres;
311 TRACE("%p (%s %s)\n", map, debugstr_jsval(key), debugstr_jsval(value));
313 return set_map_entry(map, key, value, r);
316 static HRESULT Map_has(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
317 jsval_t *r)
319 jsval_t key = argc >= 1 ? argv[0] : jsval_undefined();
320 struct jsval_map_entry *entry;
321 MapInstance *map;
322 HRESULT hres;
324 hres = get_map_this(ctx, vthis, &map);
325 if(FAILED(hres))
326 return hres;
328 TRACE("%p (%s)\n", map, debugstr_jsval(key));
330 entry = get_map_entry(map, key);
331 if(r) *r = jsval_bool(!!entry);
332 return S_OK;
335 static HRESULT Map_get_size(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
337 MapInstance *map = (MapInstance*)jsthis;
339 TRACE("%p\n", map);
341 *r = jsval_number(map->size);
342 return S_OK;
345 static HRESULT Map_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
346 jsval_t *r)
348 FIXME("\n");
349 return E_NOTIMPL;
352 static void Map_destructor(jsdisp_t *dispex)
354 MapInstance *map = (MapInstance*)dispex;
356 while(!list_empty(&map->entries)) {
357 struct jsval_map_entry *entry = LIST_ENTRY(list_head(&map->entries),
358 struct jsval_map_entry, list_entry);
359 assert(!entry->deleted);
360 release_map_entry(entry);
363 free(map);
366 static HRESULT Map_gc_traverse(struct gc_ctx *gc_ctx, enum gc_traverse_op op, jsdisp_t *dispex)
368 MapInstance *map = (MapInstance*)dispex;
369 struct jsval_map_entry *entry, *entry2;
370 HRESULT hres;
372 if(op == GC_TRAVERSE_UNLINK) {
373 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &map->entries, struct jsval_map_entry, list_entry)
374 release_map_entry(entry);
375 wine_rb_destroy(&map->map, NULL, NULL);
376 return S_OK;
379 LIST_FOR_EACH_ENTRY(entry, &map->entries, struct jsval_map_entry, list_entry) {
380 hres = gc_process_linked_val(gc_ctx, op, dispex, &entry->key);
381 if(FAILED(hres))
382 return hres;
383 hres = gc_process_linked_val(gc_ctx, op, dispex, &entry->value);
384 if(FAILED(hres))
385 return hres;
387 return S_OK;
390 static const builtin_prop_t Map_prototype_props[] = {
391 {L"clear", Map_clear, PROPF_METHOD},
392 {L"delete" , Map_delete, PROPF_METHOD|1},
393 {L"forEach", Map_forEach, PROPF_METHOD|1},
394 {L"get", Map_get, PROPF_METHOD|1},
395 {L"has", Map_has, PROPF_METHOD|1},
396 {L"set", Map_set, PROPF_METHOD|2},
399 static const builtin_prop_t Map_props[] = {
400 {L"size", NULL,0, Map_get_size, builtin_set_const},
403 static const builtin_info_t Map_prototype_info = {
404 JSCLASS_OBJECT,
405 Map_value,
406 ARRAY_SIZE(Map_prototype_props),
407 Map_prototype_props,
408 NULL,
409 NULL
412 static const builtin_info_t Map_info = {
413 JSCLASS_MAP,
414 Map_value,
415 ARRAY_SIZE(Map_props),
416 Map_props,
417 Map_destructor,
418 NULL,
419 NULL,
420 NULL,
421 NULL,
422 Map_gc_traverse
425 static HRESULT Map_constructor(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
426 jsval_t *r)
428 MapInstance *map;
429 HRESULT hres;
431 switch(flags) {
432 case DISPATCH_CONSTRUCT:
433 TRACE("\n");
435 if(!r)
436 return S_OK;
437 if(!(map = calloc(1, sizeof(*map))))
438 return E_OUTOFMEMORY;
440 hres = init_dispex(&map->dispex, ctx, &Map_info, ctx->map_prototype);
441 if(FAILED(hres))
442 return hres;
444 wine_rb_init(&map->map, jsval_map_compare);
445 list_init(&map->entries);
446 *r = jsval_obj(&map->dispex);
447 return S_OK;
449 case DISPATCH_METHOD:
450 return throw_error(ctx, JS_E_WRONG_THIS, L"Map");
452 default:
453 FIXME("unimplemented flags %x\n", flags);
454 return E_NOTIMPL;
458 static HRESULT Set_add(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
459 jsval_t *r)
461 jsval_t key = argc ? argv[0] : jsval_undefined();
462 MapInstance *set;
463 HRESULT hres;
465 hres = get_set_this(ctx, vthis, &set);
466 if(FAILED(hres))
467 return hres;
469 TRACE("%p (%s)\n", set, debugstr_jsval(key));
471 return set_map_entry(set, key, key, r);
474 static HRESULT Set_clear(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
475 jsval_t *r)
477 struct jsval_map_entry *entry, *entry2;
478 MapInstance *set;
479 HRESULT hres;
481 hres = get_set_this(ctx, vthis, &set);
482 if(FAILED(hres))
483 return hres;
485 TRACE("%p\n", set);
487 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &set->entries, struct jsval_map_entry, list_entry)
488 delete_map_entry(set, entry);
490 if(r) *r = jsval_undefined();
491 return S_OK;
494 static HRESULT Set_delete(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
495 jsval_t *r)
497 jsval_t key = argc ? argv[0] : jsval_undefined();
498 struct jsval_map_entry *entry;
499 MapInstance *set;
500 HRESULT hres;
502 hres = get_set_this(ctx, vthis, &set);
503 if(FAILED(hres))
504 return hres;
506 TRACE("%p (%s)\n", set, debugstr_jsval(key));
508 if((entry = get_map_entry(set, key))) delete_map_entry(set, entry);
509 if(r) *r = jsval_bool(!!entry);
510 return S_OK;
513 static HRESULT Set_forEach(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
514 jsval_t *r)
516 MapInstance *set;
517 HRESULT hres;
519 hres = get_set_this(ctx, vthis, &set);
520 if(FAILED(hres))
521 return hres;
523 TRACE("%p (%s)\n", set, debugstr_jsval(argc ? argv[0] : jsval_undefined()));
525 return iterate_map(set, ctx, argc, argv, r);
528 static HRESULT Set_has(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
529 jsval_t *r)
531 jsval_t key = argc ? argv[0] : jsval_undefined();
532 struct jsval_map_entry *entry;
533 MapInstance *set;
534 HRESULT hres;
536 hres = get_set_this(ctx, vthis, &set);
537 if(FAILED(hres))
538 return hres;
540 TRACE("%p (%s)\n", set, debugstr_jsval(key));
542 entry = get_map_entry(set, key);
543 if(r) *r = jsval_bool(!!entry);
544 return S_OK;
547 static HRESULT Set_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
548 jsval_t *r)
550 FIXME("\n");
551 return E_NOTIMPL;
554 static const builtin_prop_t Set_prototype_props[] = {
555 {L"add", Set_add, PROPF_METHOD|1},
556 {L"clear", Set_clear, PROPF_METHOD},
557 {L"delete" , Set_delete, PROPF_METHOD|1},
558 {L"forEach", Set_forEach, PROPF_METHOD|1},
559 {L"has", Set_has, PROPF_METHOD|1},
562 static const builtin_info_t Set_prototype_info = {
563 JSCLASS_OBJECT,
564 Set_value,
565 ARRAY_SIZE(Set_prototype_props),
566 Set_prototype_props,
567 NULL,
568 NULL
571 static const builtin_info_t Set_info = {
572 JSCLASS_SET,
573 Set_value,
574 ARRAY_SIZE(Map_props),
575 Map_props,
576 Map_destructor,
577 NULL,
578 NULL,
579 NULL,
580 NULL,
581 Map_gc_traverse
584 static HRESULT Set_constructor(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
585 jsval_t *r)
587 MapInstance *set;
588 HRESULT hres;
590 switch(flags) {
591 case DISPATCH_CONSTRUCT:
592 TRACE("\n");
594 if(!r)
595 return S_OK;
596 if(!(set = calloc(1, sizeof(*set))))
597 return E_OUTOFMEMORY;
599 hres = init_dispex(&set->dispex, ctx, &Set_info, ctx->set_prototype);
600 if(FAILED(hres))
601 return hres;
603 wine_rb_init(&set->map, jsval_map_compare);
604 list_init(&set->entries);
605 *r = jsval_obj(&set->dispex);
606 return S_OK;
608 case DISPATCH_METHOD:
609 return throw_error(ctx, JS_E_WRONG_THIS, L"Set");
611 default:
612 FIXME("unimplemented flags %x\n", flags);
613 return E_NOTIMPL;
617 typedef struct {
618 jsdisp_t dispex;
619 struct rb_tree map;
620 } WeakMapInstance;
622 static int weakmap_compare(const void *k, const struct rb_entry *e)
624 ULONG_PTR a = (ULONG_PTR)k, b = (ULONG_PTR)RB_ENTRY_VALUE(e, const struct weakmap_entry, entry)->key;
625 return (a > b) - (a < b);
628 static HRESULT get_weakmap_this(script_ctx_t *ctx, jsval_t vthis, WeakMapInstance **ret)
630 jsdisp_t *jsdisp;
632 if(!is_object_instance(vthis))
633 return JS_E_OBJECT_EXPECTED;
634 if(!(jsdisp = to_jsdisp(get_object(vthis))) || !is_class(jsdisp, JSCLASS_WEAKMAP)) {
635 WARN("not a WeakMap object passed as 'this'\n");
636 throw_error(ctx, JS_E_WRONG_THIS, L"WeakMap");
637 return DISP_E_EXCEPTION;
640 *ret = CONTAINING_RECORD(jsdisp, WeakMapInstance, dispex);
641 return S_OK;
644 static struct weakmap_entry *get_weakmap_entry(WeakMapInstance *weakmap, jsdisp_t *key)
646 struct rb_entry *entry;
647 if(!(entry = rb_get(&weakmap->map, key))) return NULL;
648 return CONTAINING_RECORD(entry, struct weakmap_entry, entry);
651 void remove_weakmap_entry(struct weakmap_entry *entry)
653 WeakMapInstance *weakmap = (WeakMapInstance*)entry->weakmap;
654 struct list *next = entry->weak_refs_entry.next;
656 if(next->next != &entry->weak_refs_entry)
657 list_remove(&entry->weak_refs_entry);
658 else {
659 struct weak_refs_entry *weak_refs_entry = LIST_ENTRY(next, struct weak_refs_entry, list);
660 entry->key->has_weak_refs = FALSE;
661 rb_remove(&entry->key->ctx->weak_refs, &weak_refs_entry->entry);
662 free(weak_refs_entry);
664 rb_remove(&weakmap->map, &entry->entry);
665 jsval_release(entry->value);
666 free(entry);
669 static HRESULT WeakMap_clear(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
670 jsval_t *r)
672 WeakMapInstance *weakmap;
673 HRESULT hres;
675 hres = get_weakmap_this(ctx, vthis, &weakmap);
676 if(FAILED(hres))
677 return hres;
679 TRACE("%p\n", weakmap);
681 while(weakmap->map.root)
682 remove_weakmap_entry(RB_ENTRY_VALUE(weakmap->map.root, struct weakmap_entry, entry));
684 if(r) *r = jsval_undefined();
685 return S_OK;
688 static HRESULT WeakMap_delete(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
689 jsval_t *r)
691 jsdisp_t *key = (argc >= 1 && is_object_instance(argv[0])) ? to_jsdisp(get_object(argv[0])) : NULL;
692 struct weakmap_entry *entry;
693 WeakMapInstance *weakmap;
694 HRESULT hres;
696 hres = get_weakmap_this(ctx, vthis, &weakmap);
697 if(FAILED(hres))
698 return hres;
700 TRACE("%p (%p)\n", weakmap, key);
702 if((entry = get_weakmap_entry(weakmap, key)))
703 remove_weakmap_entry(entry);
704 if(r) *r = jsval_bool(!!entry);
705 return S_OK;
708 static HRESULT WeakMap_get(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
709 jsval_t *r)
711 jsdisp_t *key = (argc >= 1 && is_object_instance(argv[0])) ? to_jsdisp(get_object(argv[0])) : NULL;
712 struct weakmap_entry *entry;
713 WeakMapInstance *weakmap;
714 HRESULT hres;
716 hres = get_weakmap_this(ctx, vthis, &weakmap);
717 if(FAILED(hres))
718 return hres;
720 TRACE("%p (%p)\n", weakmap, key);
722 if(!(entry = get_weakmap_entry(weakmap, key))) {
723 if(r) *r = jsval_undefined();
724 return S_OK;
727 return r ? jsval_copy(entry->value, r) : S_OK;
730 static HRESULT WeakMap_set(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
731 jsval_t *r)
733 jsdisp_t *key = (argc >= 1 && is_object_instance(argv[0])) ? to_jsdisp(get_object(argv[0])) : NULL;
734 jsval_t value = argc >= 2 ? argv[1] : jsval_undefined();
735 struct weakmap_entry *entry;
736 WeakMapInstance *weakmap;
737 HRESULT hres;
739 hres = get_weakmap_this(ctx, vthis, &weakmap);
740 if(FAILED(hres))
741 return hres;
743 TRACE("%p (%p %s)\n", weakmap, key, debugstr_jsval(value));
745 if(!key)
746 return JS_E_KEY_NOT_OBJECT;
748 if(key->ctx != ctx) {
749 FIXME("different ctx not supported\n");
750 return JS_E_KEY_NOT_OBJECT;
753 if((entry = get_weakmap_entry(weakmap, key))) {
754 jsval_t val;
755 hres = jsval_copy(value, &val);
756 if(FAILED(hres))
757 return hres;
759 jsval_release(entry->value);
760 entry->value = val;
761 }else {
762 struct weak_refs_entry *weak_refs_entry;
764 if(!(entry = malloc(sizeof(*entry))))
765 return E_OUTOFMEMORY;
767 hres = jsval_copy(value, &entry->value);
768 if(FAILED(hres)) {
769 free(entry);
770 return hres;
773 if(key->has_weak_refs)
774 weak_refs_entry = RB_ENTRY_VALUE(rb_get(&ctx->weak_refs, key), struct weak_refs_entry, entry);
775 else {
776 if(!(weak_refs_entry = malloc(sizeof(*weak_refs_entry)))) {
777 jsval_release(entry->value);
778 free(entry);
779 return E_OUTOFMEMORY;
781 rb_put(&ctx->weak_refs, key, &weak_refs_entry->entry);
782 list_init(&weak_refs_entry->list);
783 key->has_weak_refs = TRUE;
785 list_add_tail(&weak_refs_entry->list, &entry->weak_refs_entry);
787 entry->key = key;
788 entry->weakmap = &weakmap->dispex;
789 rb_put(&weakmap->map, key, &entry->entry);
792 if(r) *r = jsval_undefined();
793 return S_OK;
796 static HRESULT WeakMap_has(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
797 jsval_t *r)
799 jsdisp_t *key = (argc >= 1 && is_object_instance(argv[0])) ? to_jsdisp(get_object(argv[0])) : NULL;
800 WeakMapInstance *weakmap;
801 HRESULT hres;
803 hres = get_weakmap_this(ctx, vthis, &weakmap);
804 if(FAILED(hres))
805 return hres;
807 TRACE("%p (%p)\n", weakmap, key);
809 if(r) *r = jsval_bool(!!get_weakmap_entry(weakmap, key));
810 return S_OK;
813 static HRESULT WeakMap_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
814 jsval_t *r)
816 FIXME("\n");
817 return E_NOTIMPL;
820 static void WeakMap_destructor(jsdisp_t *dispex)
822 WeakMapInstance *weakmap = (WeakMapInstance*)dispex;
824 while(weakmap->map.root)
825 remove_weakmap_entry(RB_ENTRY_VALUE(weakmap->map.root, struct weakmap_entry, entry));
827 free(weakmap);
830 static HRESULT WeakMap_gc_traverse(struct gc_ctx *gc_ctx, enum gc_traverse_op op, jsdisp_t *dispex)
832 WeakMapInstance *weakmap = (WeakMapInstance*)dispex;
833 struct weakmap_entry *entry;
834 HRESULT hres;
836 if(op == GC_TRAVERSE_UNLINK) {
837 while(weakmap->map.root)
838 remove_weakmap_entry(RB_ENTRY_VALUE(weakmap->map.root, struct weakmap_entry, entry));
839 return S_OK;
842 RB_FOR_EACH_ENTRY(entry, &weakmap->map, struct weakmap_entry, entry) {
843 /* Only traverse the values if the key turned out to be alive, which means it might not have traversed
844 the associated values with it from this WeakMap yet (because it wasn't considered alive back then).
845 We need both the key and the WeakMap for the entry to actually be accessible (and thus traversed). */
846 if(op == GC_TRAVERSE && entry->key->gc_marked)
847 continue;
849 hres = gc_process_linked_val(gc_ctx, op, dispex, &entry->value);
850 if(FAILED(hres))
851 return hres;
853 return S_OK;
856 static const builtin_prop_t WeakMap_prototype_props[] = {
857 {L"clear", WeakMap_clear, PROPF_METHOD},
858 {L"delete", WeakMap_delete, PROPF_METHOD|1},
859 {L"get", WeakMap_get, PROPF_METHOD|1},
860 {L"has", WeakMap_has, PROPF_METHOD|1},
861 {L"set", WeakMap_set, PROPF_METHOD|2},
864 static const builtin_info_t WeakMap_prototype_info = {
865 JSCLASS_OBJECT,
866 WeakMap_value,
867 ARRAY_SIZE(WeakMap_prototype_props),
868 WeakMap_prototype_props,
869 NULL,
870 NULL
873 static const builtin_info_t WeakMap_info = {
874 JSCLASS_WEAKMAP,
875 WeakMap_value,
877 NULL,
878 WeakMap_destructor,
879 NULL,
880 NULL,
881 NULL,
882 NULL,
883 WeakMap_gc_traverse
886 static HRESULT WeakMap_constructor(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
887 jsval_t *r)
889 WeakMapInstance *weakmap;
890 HRESULT hres;
892 switch(flags) {
893 case DISPATCH_CONSTRUCT:
894 TRACE("\n");
896 if(!r)
897 return S_OK;
898 if(!(weakmap = calloc(1, sizeof(*weakmap))))
899 return E_OUTOFMEMORY;
901 hres = init_dispex(&weakmap->dispex, ctx, &WeakMap_info, ctx->weakmap_prototype);
902 if(FAILED(hres))
903 return hres;
905 rb_init(&weakmap->map, weakmap_compare);
906 *r = jsval_obj(&weakmap->dispex);
907 return S_OK;
909 case DISPATCH_METHOD:
910 return throw_error(ctx, JS_E_WRONG_THIS, L"WeakMap");
912 default:
913 FIXME("unimplemented flags %x\n", flags);
914 return E_NOTIMPL;
918 HRESULT init_set_constructor(script_ctx_t *ctx)
920 jsdisp_t *constructor;
921 HRESULT hres;
923 if(ctx->version < SCRIPTLANGUAGEVERSION_ES6)
924 return S_OK;
926 hres = create_dispex(ctx, &Set_prototype_info, ctx->object_prototype, &ctx->set_prototype);
927 if(FAILED(hres))
928 return hres;
930 hres = create_builtin_constructor(ctx, Set_constructor, L"Set", NULL,
931 PROPF_CONSTR, ctx->set_prototype, &constructor);
932 if(FAILED(hres))
933 return hres;
935 hres = jsdisp_define_data_property(ctx->global, L"Set", PROPF_WRITABLE,
936 jsval_obj(constructor));
937 jsdisp_release(constructor);
938 if(FAILED(hres))
939 return hres;
941 hres = create_dispex(ctx, &Map_prototype_info, ctx->object_prototype, &ctx->map_prototype);
942 if(FAILED(hres))
943 return hres;
945 hres = create_builtin_constructor(ctx, Map_constructor, L"Map", NULL,
946 PROPF_CONSTR, ctx->map_prototype, &constructor);
947 if(FAILED(hres))
948 return hres;
950 hres = jsdisp_define_data_property(ctx->global, L"Map", PROPF_WRITABLE,
951 jsval_obj(constructor));
952 jsdisp_release(constructor);
953 if(FAILED(hres))
954 return hres;
956 hres = create_dispex(ctx, &WeakMap_prototype_info, ctx->object_prototype, &ctx->weakmap_prototype);
957 if(FAILED(hres))
958 return hres;
960 hres = create_builtin_constructor(ctx, WeakMap_constructor, L"WeakMap", NULL,
961 PROPF_CONSTR, ctx->weakmap_prototype, &constructor);
962 if(FAILED(hres))
963 return hres;
965 hres = jsdisp_define_data_property(ctx->global, L"WeakMap", PROPF_WRITABLE,
966 jsval_obj(constructor));
967 jsdisp_release(constructor);
968 return hres;