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
);
35 struct wine_rb_tree map
;
40 static HRESULT
Set_add(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
43 FIXME("%p\n", jsthis
);
47 static HRESULT
Set_clear(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
50 FIXME("%p\n", jsthis
);
54 static HRESULT
Set_delete(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
57 FIXME("%p\n", jsthis
);
61 static HRESULT
Set_forEach(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
64 FIXME("%p\n", jsthis
);
68 static HRESULT
Set_has(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
71 FIXME("%p\n", jsthis
);
75 static HRESULT
Set_value(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
82 static const builtin_prop_t Set_props
[] = {
83 {L
"add", Set_add
, PROPF_METHOD
|1},
84 {L
"clear", Set_clear
, PROPF_METHOD
},
85 {L
"delete" , Set_delete
, PROPF_METHOD
|1},
86 {L
"forEach", Set_forEach
, PROPF_METHOD
|1},
87 {L
"has", Set_has
, PROPF_METHOD
|1},
90 static const builtin_info_t Set_prototype_info
= {
93 ARRAY_SIZE(Set_props
),
99 static const builtin_info_t Set_info
= {
101 {NULL
, Set_value
, 0},
107 static HRESULT
Set_constructor(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
114 case DISPATCH_CONSTRUCT
:
117 if(!(set
= heap_alloc_zero(sizeof(*set
))))
118 return E_OUTOFMEMORY
;
120 hres
= init_dispex(&set
->dispex
, ctx
, &Set_info
, ctx
->set_prototype
);
124 *r
= jsval_obj(&set
->dispex
);
128 FIXME("unimplemented flags %x\n", flags
);
133 struct jsval_map_entry
{
134 struct wine_rb_entry entry
;
139 * We need to maintain a list as well to support traversal in forEach.
140 * If the entry is removed while being processed by forEach, it's
141 * still kept in the list and released later, when it's safe.
143 struct list list_entry
;
148 static int jsval_map_compare(const void *k
, const struct wine_rb_entry
*e
)
150 const struct jsval_map_entry
*entry
= WINE_RB_ENTRY_VALUE(e
, const struct jsval_map_entry
, entry
);
151 const jsval_t
*key
= k
;
153 if(jsval_type(entry
->key
) != jsval_type(*key
))
154 return (int)jsval_type(entry
->key
) - (int)jsval_type(*key
);
156 switch(jsval_type(*key
)) {
161 if(get_object(*key
) == get_object(entry
->key
)) return 0;
162 return get_object(*key
) < get_object(entry
->key
) ? -1 : 1;
164 return jsstr_cmp(get_string(*key
), get_string(entry
->key
));
166 if(get_number(*key
) == get_number(entry
->key
)) return 0;
167 if(isnan(get_number(*key
))) return isnan(get_number(entry
->key
)) ? 0 : -1;
168 if(isnan(get_number(entry
->key
))) return 1;
169 return get_number(*key
) < get_number(entry
->key
) ? -1 : 1;
171 if(get_bool(*key
) == get_bool(entry
->key
)) return 0;
172 return get_bool(*key
) ? 1 : -1;
179 static MapInstance
*get_map_this(vdisp_t
*jsthis
)
181 if(!(jsthis
->flags
& VDISP_JSDISP
) || !is_class(jsthis
->u
.jsdisp
, JSCLASS_MAP
)) {
182 WARN("not a Map object passed as 'this'\n");
186 return CONTAINING_RECORD(jsthis
->u
.jsdisp
, MapInstance
, dispex
);
189 static struct jsval_map_entry
*get_map_entry(MapInstance
*map
, jsval_t key
)
191 struct wine_rb_entry
*entry
;
192 if(!(entry
= wine_rb_get(&map
->map
, &key
))) return NULL
;
193 return CONTAINING_RECORD(entry
, struct jsval_map_entry
, entry
);
196 static void grab_map_entry(struct jsval_map_entry
*entry
)
201 static void release_map_entry(struct jsval_map_entry
*entry
)
203 if(--entry
->ref
) return;
204 jsval_release(entry
->key
);
205 jsval_release(entry
->value
);
206 list_remove(&entry
->list_entry
);
210 static void delete_map_entry(MapInstance
*map
, struct jsval_map_entry
*entry
)
213 wine_rb_remove(&map
->map
, &entry
->entry
);
214 entry
->deleted
= TRUE
;
215 release_map_entry(entry
);
218 static HRESULT
Map_clear(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
223 if(!(map
= get_map_this(jsthis
))) return JS_E_MAP_EXPECTED
;
227 while(!list_empty(&map
->entries
)) {
228 struct jsval_map_entry
*entry
= LIST_ENTRY(list_head(&map
->entries
), struct jsval_map_entry
, list_entry
);
229 delete_map_entry(map
, entry
);
232 if(r
) *r
= jsval_undefined();
236 static HRESULT
Map_delete(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
239 jsval_t key
= argc
>= 1 ? argv
[0] : jsval_undefined();
240 struct jsval_map_entry
*entry
;
243 if(!(map
= get_map_this(jsthis
))) return JS_E_MAP_EXPECTED
;
245 TRACE("%p (%s)\n", map
, debugstr_jsval(key
));
247 if((entry
= get_map_entry(map
, key
))) delete_map_entry(map
, entry
);
248 if(r
) *r
= jsval_bool(!!entry
);
252 static HRESULT
Map_forEach(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
255 jsval_t callback
= argc
? argv
[0] : jsval_undefined();
256 struct jsval_map_entry
*entry
;
260 if(!(map
= get_map_this(jsthis
))) return JS_E_MAP_EXPECTED
;
262 TRACE("%p (%s)\n", map
, debugstr_jsval(argc
>= 1 ? argv
[0] : jsval_undefined()));
264 if(!is_object_instance(callback
) || !get_object(callback
)) {
265 FIXME("invalid callback %s\n", debugstr_jsval(callback
));
270 FIXME("Unsupported argument\n");
274 LIST_FOR_EACH_ENTRY(entry
, &map
->entries
, struct jsval_map_entry
, list_entry
) {
278 args
[0] = entry
->value
;
279 args
[1] = entry
->key
;
280 grab_map_entry(entry
);
281 hres
= disp_call_value(ctx
, get_object(argv
[0]), NULL
, DISPATCH_METHOD
,
282 ARRAY_SIZE(args
), args
, &v
);
283 release_map_entry(entry
);
289 if(r
) *r
= jsval_undefined();
293 static HRESULT
Map_get(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
296 jsval_t key
= argc
>= 1 ? argv
[0] : jsval_undefined();
297 struct jsval_map_entry
*entry
;
300 if(!(map
= get_map_this(jsthis
))) return JS_E_MAP_EXPECTED
;
302 TRACE("%p (%s)\n", map
, debugstr_jsval(key
));
304 if(!(entry
= get_map_entry(map
, key
))) {
305 if(r
) *r
= jsval_undefined();
309 return r
? jsval_copy(entry
->value
, r
) : S_OK
;
312 static HRESULT
Map_set(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
315 jsval_t key
= argc
>= 1 ? argv
[0] : jsval_undefined();
316 jsval_t value
= argc
>= 2 ? argv
[1] : jsval_undefined();
317 struct jsval_map_entry
*entry
;
321 if(!(map
= get_map_this(jsthis
))) return JS_E_MAP_EXPECTED
;
323 TRACE("%p (%s %s)\n", map
, debugstr_jsval(key
), debugstr_jsval(value
));
325 if((entry
= get_map_entry(map
, key
))) {
327 hres
= jsval_copy(value
, &val
);
331 jsval_release(entry
->value
);
334 if(!(entry
= heap_alloc_zero(sizeof(*entry
)))) return E_OUTOFMEMORY
;
336 hres
= jsval_copy(key
, &entry
->key
);
337 if(SUCCEEDED(hres
)) {
338 hres
= jsval_copy(value
, &entry
->value
);
340 jsval_release(entry
->key
);
346 grab_map_entry(entry
);
347 wine_rb_put(&map
->map
, &entry
->key
, &entry
->entry
);
348 list_add_tail(&map
->entries
, &entry
->list_entry
);
352 if(r
) *r
= jsval_undefined();
356 static HRESULT
Map_has(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
359 jsval_t key
= argc
>= 1 ? argv
[0] : jsval_undefined();
360 struct jsval_map_entry
*entry
;
363 if(!(map
= get_map_this(jsthis
))) return JS_E_MAP_EXPECTED
;
365 TRACE("%p (%s)\n", map
, debugstr_jsval(key
));
367 entry
= get_map_entry(map
, key
);
368 if(r
) *r
= jsval_bool(!!entry
);
372 static HRESULT
Map_get_size(script_ctx_t
*ctx
, jsdisp_t
*jsthis
, jsval_t
*r
)
374 MapInstance
*map
= (MapInstance
*)jsthis
;
378 *r
= jsval_number(map
->size
);
382 static HRESULT
Map_value(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
389 static void Map_destructor(jsdisp_t
*dispex
)
391 MapInstance
*map
= (MapInstance
*)dispex
;
393 while(!list_empty(&map
->entries
)) {
394 struct jsval_map_entry
*entry
= LIST_ENTRY(list_head(&map
->entries
),
395 struct jsval_map_entry
, list_entry
);
396 assert(!entry
->deleted
);
397 release_map_entry(entry
);
402 static const builtin_prop_t Map_prototype_props
[] = {
403 {L
"clear", Map_clear
, PROPF_METHOD
},
404 {L
"delete" , Map_delete
, PROPF_METHOD
|1},
405 {L
"forEach", Map_forEach
, PROPF_METHOD
|1},
406 {L
"get", Map_get
, PROPF_METHOD
|1},
407 {L
"has", Map_has
, PROPF_METHOD
|1},
408 {L
"set", Map_set
, PROPF_METHOD
|2},
411 static const builtin_prop_t Map_props
[] = {
412 {L
"size", NULL
,0, Map_get_size
, builtin_set_const
},
415 static const builtin_info_t Map_prototype_info
= {
417 {NULL
, Map_value
, 0},
418 ARRAY_SIZE(Map_prototype_props
),
424 static const builtin_info_t Map_info
= {
426 {NULL
, Map_value
, 0},
427 ARRAY_SIZE(Map_props
),
433 static HRESULT
Map_constructor(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
440 case DISPATCH_CONSTRUCT
:
443 if(!(map
= heap_alloc_zero(sizeof(*map
))))
444 return E_OUTOFMEMORY
;
446 hres
= init_dispex(&map
->dispex
, ctx
, &Map_info
, ctx
->map_prototype
);
450 wine_rb_init(&map
->map
, jsval_map_compare
);
451 list_init(&map
->entries
);
452 *r
= jsval_obj(&map
->dispex
);
456 FIXME("unimplemented flags %x\n", flags
);
461 HRESULT
init_set_constructor(script_ctx_t
*ctx
)
463 jsdisp_t
*constructor
;
466 if(ctx
->version
< SCRIPTLANGUAGEVERSION_ES6
)
469 hres
= create_dispex(ctx
, &Set_prototype_info
, ctx
->object_prototype
, &ctx
->set_prototype
);
473 hres
= create_builtin_constructor(ctx
, Set_constructor
, L
"Set", NULL
,
474 PROPF_CONSTR
, ctx
->set_prototype
, &constructor
);
478 hres
= jsdisp_define_data_property(ctx
->global
, L
"Set", PROPF_WRITABLE
,
479 jsval_obj(constructor
));
480 jsdisp_release(constructor
);
484 hres
= create_dispex(ctx
, &Map_prototype_info
, ctx
->object_prototype
, &ctx
->map_prototype
);
488 hres
= create_builtin_constructor(ctx
, Map_constructor
, L
"Map", NULL
,
489 PROPF_CONSTR
, ctx
->map_prototype
, &constructor
);
493 hres
= jsdisp_define_data_property(ctx
->global
, L
"Map", PROPF_WRITABLE
,
494 jsval_obj(constructor
));
495 jsdisp_release(constructor
);