dbghelp: Properly fail on PDB files generated by MSVC compiler version 14.31.
[wine.git] / dlls / jscript / set.c
blobd1ea6637956429d72076239c71b11c7709014dd5
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 heap_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 = heap_alloc_zero(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 heap_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 jsval_map_entry *entry;
187 IDispatch *context_obj = NULL;
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 && !is_undefined(argv[1])) {
196 if(!is_object_instance(argv[1])) {
197 FIXME("Unsupported context this %s\n", debugstr_jsval(argv[1]));
198 return E_NOTIMPL;
200 context_obj = get_object(argv[1]);
203 LIST_FOR_EACH_ENTRY(entry, &map->entries, struct jsval_map_entry, list_entry) {
204 jsval_t args[3], v;
205 if(entry->deleted)
206 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_obj,
212 DISPATCH_METHOD, ARRAY_SIZE(args), args, &v);
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 MapInstance *map;
227 HRESULT hres;
229 hres = get_map_this(ctx, vthis, &map);
230 if(FAILED(hres))
231 return hres;
233 TRACE("%p\n", map);
235 while(!list_empty(&map->entries)) {
236 struct jsval_map_entry *entry = LIST_ENTRY(list_head(&map->entries), struct jsval_map_entry, list_entry);
237 delete_map_entry(map, entry);
240 if(r) *r = jsval_undefined();
241 return S_OK;
244 static HRESULT Map_delete(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
245 jsval_t *r)
247 jsval_t key = argc >= 1 ? argv[0] : jsval_undefined();
248 struct jsval_map_entry *entry;
249 MapInstance *map;
250 HRESULT hres;
252 hres = get_map_this(ctx, vthis, &map);
253 if(FAILED(hres))
254 return hres;
256 TRACE("%p (%s)\n", map, debugstr_jsval(key));
258 if((entry = get_map_entry(map, key))) delete_map_entry(map, entry);
259 if(r) *r = jsval_bool(!!entry);
260 return S_OK;
263 static HRESULT Map_forEach(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
264 jsval_t *r)
266 MapInstance *map;
267 HRESULT hres;
269 hres = get_map_this(ctx, vthis, &map);
270 if(FAILED(hres))
271 return hres;
273 TRACE("%p (%s)\n", map, debugstr_jsval(argc >= 1 ? argv[0] : jsval_undefined()));
275 return iterate_map(map, ctx, argc, argv, r);
278 static HRESULT Map_get(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
279 jsval_t *r)
281 jsval_t key = argc >= 1 ? argv[0] : jsval_undefined();
282 struct jsval_map_entry *entry;
283 MapInstance *map;
284 HRESULT hres;
286 hres = get_map_this(ctx, vthis, &map);
287 if(FAILED(hres))
288 return hres;
290 TRACE("%p (%s)\n", map, debugstr_jsval(key));
292 if(!(entry = get_map_entry(map, key))) {
293 if(r) *r = jsval_undefined();
294 return S_OK;
297 return r ? jsval_copy(entry->value, r) : S_OK;
300 static HRESULT Map_set(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
301 jsval_t *r)
303 jsval_t key = argc >= 1 ? argv[0] : jsval_undefined();
304 jsval_t value = argc >= 2 ? argv[1] : jsval_undefined();
305 MapInstance *map;
306 HRESULT hres;
308 hres = get_map_this(ctx, vthis, &map);
309 if(FAILED(hres))
310 return hres;
312 TRACE("%p (%s %s)\n", map, debugstr_jsval(key), debugstr_jsval(value));
314 return set_map_entry(map, key, value, r);
317 static HRESULT Map_has(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
318 jsval_t *r)
320 jsval_t key = argc >= 1 ? argv[0] : jsval_undefined();
321 struct jsval_map_entry *entry;
322 MapInstance *map;
323 HRESULT hres;
325 hres = get_map_this(ctx, vthis, &map);
326 if(FAILED(hres))
327 return hres;
329 TRACE("%p (%s)\n", map, debugstr_jsval(key));
331 entry = get_map_entry(map, key);
332 if(r) *r = jsval_bool(!!entry);
333 return S_OK;
336 static HRESULT Map_get_size(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
338 MapInstance *map = (MapInstance*)jsthis;
340 TRACE("%p\n", map);
342 *r = jsval_number(map->size);
343 return S_OK;
346 static HRESULT Map_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
347 jsval_t *r)
349 FIXME("\n");
350 return E_NOTIMPL;
353 static void Map_destructor(jsdisp_t *dispex)
355 MapInstance *map = (MapInstance*)dispex;
357 while(!list_empty(&map->entries)) {
358 struct jsval_map_entry *entry = LIST_ENTRY(list_head(&map->entries),
359 struct jsval_map_entry, list_entry);
360 assert(!entry->deleted);
361 release_map_entry(entry);
364 heap_free(map);
366 static const builtin_prop_t Map_prototype_props[] = {
367 {L"clear", Map_clear, PROPF_METHOD},
368 {L"delete" , Map_delete, PROPF_METHOD|1},
369 {L"forEach", Map_forEach, PROPF_METHOD|1},
370 {L"get", Map_get, PROPF_METHOD|1},
371 {L"has", Map_has, PROPF_METHOD|1},
372 {L"set", Map_set, PROPF_METHOD|2},
375 static const builtin_prop_t Map_props[] = {
376 {L"size", NULL,0, Map_get_size, builtin_set_const},
379 static const builtin_info_t Map_prototype_info = {
380 JSCLASS_OBJECT,
381 Map_value,
382 ARRAY_SIZE(Map_prototype_props),
383 Map_prototype_props,
384 NULL,
385 NULL
388 static const builtin_info_t Map_info = {
389 JSCLASS_MAP,
390 Map_value,
391 ARRAY_SIZE(Map_props),
392 Map_props,
393 Map_destructor,
394 NULL
397 static HRESULT Map_constructor(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
398 jsval_t *r)
400 MapInstance *map;
401 HRESULT hres;
403 switch(flags) {
404 case DISPATCH_CONSTRUCT:
405 TRACE("\n");
407 if(!r)
408 return S_OK;
409 if(!(map = heap_alloc_zero(sizeof(*map))))
410 return E_OUTOFMEMORY;
412 hres = init_dispex(&map->dispex, ctx, &Map_info, ctx->map_prototype);
413 if(FAILED(hres))
414 return hres;
416 wine_rb_init(&map->map, jsval_map_compare);
417 list_init(&map->entries);
418 *r = jsval_obj(&map->dispex);
419 return S_OK;
421 default:
422 FIXME("unimplemented flags %x\n", flags);
423 return E_NOTIMPL;
427 static HRESULT Set_add(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
428 jsval_t *r)
430 jsval_t key = argc ? argv[0] : jsval_undefined();
431 MapInstance *set;
432 HRESULT hres;
434 hres = get_set_this(ctx, vthis, &set);
435 if(FAILED(hres))
436 return hres;
438 TRACE("%p (%s)\n", set, debugstr_jsval(key));
440 return set_map_entry(set, key, key, r);
443 static HRESULT Set_clear(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
444 jsval_t *r)
446 MapInstance *set;
447 HRESULT hres;
449 hres = get_set_this(ctx, vthis, &set);
450 if(FAILED(hres))
451 return hres;
453 TRACE("%p\n", set);
455 while(!list_empty(&set->entries)) {
456 struct jsval_map_entry *entry = LIST_ENTRY(list_head(&set->entries), struct jsval_map_entry, list_entry);
457 delete_map_entry(set, entry);
460 if(r) *r = jsval_undefined();
461 return S_OK;
464 static HRESULT Set_delete(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
465 jsval_t *r)
467 jsval_t key = argc ? argv[0] : jsval_undefined();
468 struct jsval_map_entry *entry;
469 MapInstance *set;
470 HRESULT hres;
472 hres = get_set_this(ctx, vthis, &set);
473 if(FAILED(hres))
474 return hres;
476 TRACE("%p (%s)\n", set, debugstr_jsval(key));
478 if((entry = get_map_entry(set, key))) delete_map_entry(set, entry);
479 if(r) *r = jsval_bool(!!entry);
480 return S_OK;
483 static HRESULT Set_forEach(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
484 jsval_t *r)
486 MapInstance *set;
487 HRESULT hres;
489 hres = get_set_this(ctx, vthis, &set);
490 if(FAILED(hres))
491 return hres;
493 TRACE("%p (%s)\n", set, debugstr_jsval(argc ? argv[0] : jsval_undefined()));
495 return iterate_map(set, ctx, argc, argv, r);
498 static HRESULT Set_has(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
499 jsval_t *r)
501 jsval_t key = argc ? argv[0] : jsval_undefined();
502 struct jsval_map_entry *entry;
503 MapInstance *set;
504 HRESULT hres;
506 hres = get_set_this(ctx, vthis, &set);
507 if(FAILED(hres))
508 return hres;
510 TRACE("%p (%s)\n", set, debugstr_jsval(key));
512 entry = get_map_entry(set, key);
513 if(r) *r = jsval_bool(!!entry);
514 return S_OK;
517 static HRESULT Set_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
518 jsval_t *r)
520 FIXME("\n");
521 return E_NOTIMPL;
524 static const builtin_prop_t Set_prototype_props[] = {
525 {L"add", Set_add, PROPF_METHOD|1},
526 {L"clear", Set_clear, PROPF_METHOD},
527 {L"delete" , Set_delete, PROPF_METHOD|1},
528 {L"forEach", Set_forEach, PROPF_METHOD|1},
529 {L"has", Set_has, PROPF_METHOD|1},
532 static const builtin_info_t Set_prototype_info = {
533 JSCLASS_OBJECT,
534 Set_value,
535 ARRAY_SIZE(Set_prototype_props),
536 Set_prototype_props,
537 NULL,
538 NULL
541 static const builtin_info_t Set_info = {
542 JSCLASS_SET,
543 Set_value,
544 ARRAY_SIZE(Map_props),
545 Map_props,
546 Map_destructor,
547 NULL
550 static HRESULT Set_constructor(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
551 jsval_t *r)
553 MapInstance *set;
554 HRESULT hres;
556 switch(flags) {
557 case DISPATCH_CONSTRUCT:
558 TRACE("\n");
560 if(!r)
561 return S_OK;
562 if(!(set = heap_alloc_zero(sizeof(*set))))
563 return E_OUTOFMEMORY;
565 hres = init_dispex(&set->dispex, ctx, &Set_info, ctx->set_prototype);
566 if(FAILED(hres))
567 return hres;
569 wine_rb_init(&set->map, jsval_map_compare);
570 list_init(&set->entries);
571 *r = jsval_obj(&set->dispex);
572 return S_OK;
574 default:
575 FIXME("unimplemented flags %x\n", flags);
576 return E_NOTIMPL;
580 HRESULT init_set_constructor(script_ctx_t *ctx)
582 jsdisp_t *constructor;
583 HRESULT hres;
585 if(ctx->version < SCRIPTLANGUAGEVERSION_ES6)
586 return S_OK;
588 hres = create_dispex(ctx, &Set_prototype_info, ctx->object_prototype, &ctx->set_prototype);
589 if(FAILED(hres))
590 return hres;
592 hres = create_builtin_constructor(ctx, Set_constructor, L"Set", NULL,
593 PROPF_CONSTR, ctx->set_prototype, &constructor);
594 if(FAILED(hres))
595 return hres;
597 hres = jsdisp_define_data_property(ctx->global, L"Set", PROPF_WRITABLE,
598 jsval_obj(constructor));
599 jsdisp_release(constructor);
600 if(FAILED(hres))
601 return hres;
603 hres = create_dispex(ctx, &Map_prototype_info, ctx->object_prototype, &ctx->map_prototype);
604 if(FAILED(hres))
605 return hres;
607 hres = create_builtin_constructor(ctx, Map_constructor, L"Map", NULL,
608 PROPF_CONSTR, ctx->map_prototype, &constructor);
609 if(FAILED(hres))
610 return hres;
612 hres = jsdisp_define_data_property(ctx->global, L"Map", PROPF_WRITABLE,
613 jsval_obj(constructor));
614 jsdisp_release(constructor);
615 return hres;