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
24 #include "wine/rbtree.h"
25 #include "wine/debug.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(jscript
);
31 struct wine_rb_tree map
;
36 struct jsval_map_entry
{
37 struct wine_rb_entry entry
;
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
;
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
;
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
)) {
68 if(get_object(*key
) == get_object(entry
->key
)) return 0;
69 return get_object(*key
) < get_object(entry
->key
) ? -1 : 1;
71 return jsstr_cmp(get_string(*key
), get_string(entry
->key
));
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);
81 if(get_bool(*key
) == get_bool(entry
->key
)) return 0;
82 return get_bool(*key
) ? 1 : -1;
89 static HRESULT
get_map_this(script_ctx_t
*ctx
, jsval_t vthis
, MapInstance
**ret
)
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
);
104 static HRESULT
get_set_this(script_ctx_t
*ctx
, jsval_t vthis
, MapInstance
**ret
)
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
);
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
)
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
);
140 static void delete_map_entry(MapInstance
*map
, struct jsval_map_entry
*entry
)
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
;
153 if((entry
= get_map_entry(map
, key
))) {
155 hres
= jsval_copy(value
, &val
);
159 jsval_release(entry
->value
);
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
);
168 jsval_release(entry
->key
);
174 grab_map_entry(entry
);
175 wine_rb_put(&map
->map
, &entry
->key
, &entry
->entry
);
176 list_add_tail(&map
->entries
, &entry
->list_entry
);
180 if(r
) *r
= jsval_undefined();
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();
190 if(!argc
|| !is_object_instance(argv
[0])) {
191 FIXME("invalid callback %s\n", debugstr_jsval(argc
? argv
[0] : jsval_undefined()));
196 context_this
= argv
[1];
199 struct jsval_map_entry
*entry
= LIST_ENTRY(iter
, struct jsval_map_entry
, list_entry
);
203 iter
= list_next(&map
->entries
, iter
);
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
);
219 if(r
) *r
= jsval_undefined();
223 static HRESULT
Map_clear(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
226 struct jsval_map_entry
*entry
, *entry2
;
230 hres
= get_map_this(ctx
, vthis
, &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();
243 static HRESULT
Map_delete(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
246 jsval_t key
= argc
>= 1 ? argv
[0] : jsval_undefined();
247 struct jsval_map_entry
*entry
;
251 hres
= get_map_this(ctx
, vthis
, &map
);
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
);
262 static HRESULT
Map_forEach(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
268 hres
= get_map_this(ctx
, vthis
, &map
);
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
,
280 jsval_t key
= argc
>= 1 ? argv
[0] : jsval_undefined();
281 struct jsval_map_entry
*entry
;
285 hres
= get_map_this(ctx
, vthis
, &map
);
289 TRACE("%p (%s)\n", map
, debugstr_jsval(key
));
291 if(!(entry
= get_map_entry(map
, key
))) {
292 if(r
) *r
= jsval_undefined();
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
,
302 jsval_t key
= argc
>= 1 ? argv
[0] : jsval_undefined();
303 jsval_t value
= argc
>= 2 ? argv
[1] : jsval_undefined();
307 hres
= get_map_this(ctx
, vthis
, &map
);
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
,
319 jsval_t key
= argc
>= 1 ? argv
[0] : jsval_undefined();
320 struct jsval_map_entry
*entry
;
324 hres
= get_map_this(ctx
, vthis
, &map
);
328 TRACE("%p (%s)\n", map
, debugstr_jsval(key
));
330 entry
= get_map_entry(map
, key
);
331 if(r
) *r
= jsval_bool(!!entry
);
335 static HRESULT
Map_get_size(script_ctx_t
*ctx
, jsdisp_t
*jsthis
, jsval_t
*r
)
337 MapInstance
*map
= (MapInstance
*)jsthis
;
341 *r
= jsval_number(map
->size
);
345 static HRESULT
Map_value(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
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
);
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
;
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
);
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
);
383 hres
= gc_process_linked_val(gc_ctx
, op
, dispex
, &entry
->value
);
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
= {
406 ARRAY_SIZE(Map_prototype_props
),
412 static const builtin_info_t Map_info
= {
415 ARRAY_SIZE(Map_props
),
425 static HRESULT
Map_constructor(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
432 case DISPATCH_CONSTRUCT
:
437 if(!(map
= calloc(1, sizeof(*map
))))
438 return E_OUTOFMEMORY
;
440 hres
= init_dispex(&map
->dispex
, ctx
, &Map_info
, ctx
->map_prototype
);
444 wine_rb_init(&map
->map
, jsval_map_compare
);
445 list_init(&map
->entries
);
446 *r
= jsval_obj(&map
->dispex
);
449 case DISPATCH_METHOD
:
450 return throw_error(ctx
, JS_E_WRONG_THIS
, L
"Map");
453 FIXME("unimplemented flags %x\n", flags
);
458 static HRESULT
Set_add(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
461 jsval_t key
= argc
? argv
[0] : jsval_undefined();
465 hres
= get_set_this(ctx
, vthis
, &set
);
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
,
477 struct jsval_map_entry
*entry
, *entry2
;
481 hres
= get_set_this(ctx
, vthis
, &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();
494 static HRESULT
Set_delete(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
497 jsval_t key
= argc
? argv
[0] : jsval_undefined();
498 struct jsval_map_entry
*entry
;
502 hres
= get_set_this(ctx
, vthis
, &set
);
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
);
513 static HRESULT
Set_forEach(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
519 hres
= get_set_this(ctx
, vthis
, &set
);
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
,
531 jsval_t key
= argc
? argv
[0] : jsval_undefined();
532 struct jsval_map_entry
*entry
;
536 hres
= get_set_this(ctx
, vthis
, &set
);
540 TRACE("%p (%s)\n", set
, debugstr_jsval(key
));
542 entry
= get_map_entry(set
, key
);
543 if(r
) *r
= jsval_bool(!!entry
);
547 static HRESULT
Set_value(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
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
= {
565 ARRAY_SIZE(Set_prototype_props
),
571 static const builtin_info_t Set_info
= {
574 ARRAY_SIZE(Map_props
),
584 static HRESULT
Set_constructor(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
591 case DISPATCH_CONSTRUCT
:
596 if(!(set
= calloc(1, sizeof(*set
))))
597 return E_OUTOFMEMORY
;
599 hres
= init_dispex(&set
->dispex
, ctx
, &Set_info
, ctx
->set_prototype
);
603 wine_rb_init(&set
->map
, jsval_map_compare
);
604 list_init(&set
->entries
);
605 *r
= jsval_obj(&set
->dispex
);
608 case DISPATCH_METHOD
:
609 return throw_error(ctx
, JS_E_WRONG_THIS
, L
"Set");
612 FIXME("unimplemented flags %x\n", flags
);
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
)
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
);
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
);
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
);
669 static HRESULT
WeakMap_clear(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
672 WeakMapInstance
*weakmap
;
675 hres
= get_weakmap_this(ctx
, vthis
, &weakmap
);
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();
688 static HRESULT
WeakMap_delete(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
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
;
696 hres
= get_weakmap_this(ctx
, vthis
, &weakmap
);
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
);
708 static HRESULT
WeakMap_get(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
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
;
716 hres
= get_weakmap_this(ctx
, vthis
, &weakmap
);
720 TRACE("%p (%p)\n", weakmap
, key
);
722 if(!(entry
= get_weakmap_entry(weakmap
, key
))) {
723 if(r
) *r
= jsval_undefined();
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
,
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
;
739 hres
= get_weakmap_this(ctx
, vthis
, &weakmap
);
743 TRACE("%p (%p %s)\n", weakmap
, key
, debugstr_jsval(value
));
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
))) {
755 hres
= jsval_copy(value
, &val
);
759 jsval_release(entry
->value
);
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
);
773 if(key
->has_weak_refs
)
774 weak_refs_entry
= RB_ENTRY_VALUE(rb_get(&ctx
->weak_refs
, key
), struct weak_refs_entry
, entry
);
776 if(!(weak_refs_entry
= malloc(sizeof(*weak_refs_entry
)))) {
777 jsval_release(entry
->value
);
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
);
788 entry
->weakmap
= &weakmap
->dispex
;
789 rb_put(&weakmap
->map
, key
, &entry
->entry
);
792 if(r
) *r
= jsval_undefined();
796 static HRESULT
WeakMap_has(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
799 jsdisp_t
*key
= (argc
>= 1 && is_object_instance(argv
[0])) ? to_jsdisp(get_object(argv
[0])) : NULL
;
800 WeakMapInstance
*weakmap
;
803 hres
= get_weakmap_this(ctx
, vthis
, &weakmap
);
807 TRACE("%p (%p)\n", weakmap
, key
);
809 if(r
) *r
= jsval_bool(!!get_weakmap_entry(weakmap
, key
));
813 static HRESULT
WeakMap_value(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
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
));
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
;
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
));
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
)
849 hres
= gc_process_linked_val(gc_ctx
, op
, dispex
, &entry
->value
);
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
= {
867 ARRAY_SIZE(WeakMap_prototype_props
),
868 WeakMap_prototype_props
,
873 static const builtin_info_t WeakMap_info
= {
886 static HRESULT
WeakMap_constructor(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
889 WeakMapInstance
*weakmap
;
893 case DISPATCH_CONSTRUCT
:
898 if(!(weakmap
= calloc(1, sizeof(*weakmap
))))
899 return E_OUTOFMEMORY
;
901 hres
= init_dispex(&weakmap
->dispex
, ctx
, &WeakMap_info
, ctx
->weakmap_prototype
);
905 rb_init(&weakmap
->map
, weakmap_compare
);
906 *r
= jsval_obj(&weakmap
->dispex
);
909 case DISPATCH_METHOD
:
910 return throw_error(ctx
, JS_E_WRONG_THIS
, L
"WeakMap");
913 FIXME("unimplemented flags %x\n", flags
);
918 HRESULT
init_set_constructor(script_ctx_t
*ctx
)
920 jsdisp_t
*constructor
;
923 if(ctx
->version
< SCRIPTLANGUAGEVERSION_ES6
)
926 hres
= create_dispex(ctx
, &Set_prototype_info
, ctx
->object_prototype
, &ctx
->set_prototype
);
930 hres
= create_builtin_constructor(ctx
, Set_constructor
, L
"Set", NULL
,
931 PROPF_CONSTR
, ctx
->set_prototype
, &constructor
);
935 hres
= jsdisp_define_data_property(ctx
->global
, L
"Set", PROPF_WRITABLE
,
936 jsval_obj(constructor
));
937 jsdisp_release(constructor
);
941 hres
= create_dispex(ctx
, &Map_prototype_info
, ctx
->object_prototype
, &ctx
->map_prototype
);
945 hres
= create_builtin_constructor(ctx
, Map_constructor
, L
"Map", NULL
,
946 PROPF_CONSTR
, ctx
->map_prototype
, &constructor
);
950 hres
= jsdisp_define_data_property(ctx
->global
, L
"Map", PROPF_WRITABLE
,
951 jsval_obj(constructor
));
952 jsdisp_release(constructor
);
956 hres
= create_dispex(ctx
, &WeakMap_prototype_info
, ctx
->object_prototype
, &ctx
->weakmap_prototype
);
960 hres
= create_builtin_constructor(ctx
, WeakMap_constructor
, L
"WeakMap", NULL
,
961 PROPF_CONSTR
, ctx
->weakmap_prototype
, &constructor
);
965 hres
= jsdisp_define_data_property(ctx
->global
, L
"WeakMap", PROPF_WRITABLE
,
966 jsval_obj(constructor
));
967 jsdisp_release(constructor
);