cosmopolitan/third_party/quickjs/map.c
Justine Tunney 0d748ad58e
Fix warnings
This change fixes Cosmopolitan so it has fewer opinions about compiler
warnings. The whole repository had to be cleaned up to be buildable in
-Werror -Wall mode. This lets us benefit from things like strict const
checking. Some actual bugs might have been caught too.
2023-09-01 20:50:18 -07:00

749 lines
24 KiB
C

/*
* QuickJS Javascript Engine
*
* Copyright (c) 2017-2021 Fabrice Bellard
* Copyright (c) 2017-2021 Charlie Gordon
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "libc/assert.h"
#include "third_party/quickjs/internal.h"
asm(".ident\t\"\\n\\n\
QuickJS (MIT License)\\n\
Copyright (c) 2017-2021 Fabrice Bellard\\n\
Copyright (c) 2017-2021 Charlie Gordon\"");
asm(".include \"libc/disclaimer.inc\"");
/* clang-format off */
static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target,
int argc, JSValueConst *argv, int magic)
{
JSMapState *s;
JSValue obj, adder = JS_UNDEFINED, iter = JS_UNDEFINED, next_method = JS_UNDEFINED;
JSValueConst arr;
BOOL is_set, is_weak;
is_set = magic & MAGIC_SET;
is_weak = ((magic & MAGIC_WEAK) != 0);
obj = js_create_from_ctor(ctx, new_target, JS_CLASS_MAP + magic);
if (JS_IsException(obj))
return JS_EXCEPTION;
s = js_mallocz(ctx, sizeof(*s));
if (!s)
goto fail;
init_list_head(&s->records);
s->is_weak = is_weak;
JS_SetOpaque(obj, s);
s->hash_size = 1;
s->hash_table = js_malloc(ctx, sizeof(s->hash_table[0]) * s->hash_size);
if (!s->hash_table)
goto fail;
init_list_head(&s->hash_table[0]);
s->record_count_threshold = 4;
arr = JS_UNDEFINED;
if (argc > 0)
arr = argv[0];
if (!JS_IsUndefined(arr) && !JS_IsNull(arr)) {
JSValue item, ret;
BOOL done;
adder = JS_GetProperty(ctx, obj, is_set ? JS_ATOM_add : JS_ATOM_set);
if (JS_IsException(adder))
goto fail;
if (!JS_IsFunction(ctx, adder)) {
JS_ThrowTypeError(ctx, "set/add is not a function");
goto fail;
}
iter = JS_GetIterator(ctx, arr, FALSE);
if (JS_IsException(iter))
goto fail;
next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
if (JS_IsException(next_method))
goto fail;
for(;;) {
item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
if (JS_IsException(item))
goto fail;
if (done) {
JS_FreeValue(ctx, item);
break;
}
if (is_set) {
ret = JS_Call(ctx, adder, obj, 1, (JSValueConst *)&item);
if (JS_IsException(ret)) {
JS_FreeValue(ctx, item);
goto fail;
}
} else {
JSValue key, value;
JSValueConst args[2];
key = JS_UNDEFINED;
value = JS_UNDEFINED;
if (!JS_IsObject(item)) {
JS_ThrowTypeErrorNotAnObject(ctx);
goto fail1;
}
key = JS_GetPropertyUint32(ctx, item, 0);
if (JS_IsException(key))
goto fail1;
value = JS_GetPropertyUint32(ctx, item, 1);
if (JS_IsException(value))
goto fail1;
args[0] = key;
args[1] = value;
ret = JS_Call(ctx, adder, obj, 2, args);
if (JS_IsException(ret)) {
fail1:
JS_FreeValue(ctx, item);
JS_FreeValue(ctx, key);
JS_FreeValue(ctx, value);
goto fail;
}
JS_FreeValue(ctx, key);
JS_FreeValue(ctx, value);
}
JS_FreeValue(ctx, ret);
JS_FreeValue(ctx, item);
}
JS_FreeValue(ctx, next_method);
JS_FreeValue(ctx, iter);
JS_FreeValue(ctx, adder);
}
return obj;
fail:
if (JS_IsObject(iter)) {
/* close the iterator object, preserving pending exception */
JS_IteratorClose(ctx, iter, TRUE);
}
JS_FreeValue(ctx, next_method);
JS_FreeValue(ctx, iter);
JS_FreeValue(ctx, adder);
JS_FreeValue(ctx, obj);
return JS_EXCEPTION;
}
/* XXX: could normalize strings to speed up comparison */
static JSValueConst map_normalize_key(JSContext *ctx, JSValueConst key)
{
uint32_t tag = JS_VALUE_GET_TAG(key);
/* convert -0.0 to +0.0 */
if (JS_TAG_IS_FLOAT64(tag) && JS_VALUE_GET_FLOAT64(key) == 0.0) {
key = JS_NewInt32(ctx, 0);
}
return key;
}
/* XXX: better hash ? */
static uint32_t map_hash_key(JSContext *ctx, JSValueConst key)
{
uint32_t tag = JS_VALUE_GET_NORM_TAG(key);
uint32_t h;
double d;
JSFloat64Union u;
switch(tag) {
case JS_TAG_BOOL:
h = JS_VALUE_GET_INT(key);
break;
case JS_TAG_STRING:
h = hash_string(JS_VALUE_GET_STRING(key), 0);
break;
case JS_TAG_OBJECT:
case JS_TAG_SYMBOL:
h = (uintptr_t)JS_VALUE_GET_PTR(key) * 3163;
break;
case JS_TAG_INT:
d = JS_VALUE_GET_INT(key) * 3163;
goto hash_float64;
case JS_TAG_FLOAT64:
d = JS_VALUE_GET_FLOAT64(key);
/* normalize the NaN */
if (isnan(d))
d = JS_FLOAT64_NAN;
hash_float64:
u.d = d;
h = (u.u32[0] ^ u.u32[1]) * 3163;
break;
default:
h = 0; /* XXX: bignum support */
break;
}
h ^= tag;
return h;
}
static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s,
JSValueConst key)
{
struct list_head *el;
JSMapRecord *mr;
uint32_t h;
h = map_hash_key(ctx, key) & (s->hash_size - 1);
list_for_each(el, &s->hash_table[h]) {
mr = list_entry(el, JSMapRecord, hash_link);
if (js_same_value_zero(ctx, mr->key, key))
return mr;
}
return NULL;
}
static void map_hash_resize(JSContext *ctx, JSMapState *s)
{
uint32_t new_hash_size, i, h;
size_t slack;
struct list_head *new_hash_table, *el;
JSMapRecord *mr;
/* XXX: no reporting of memory allocation failure */
if (s->hash_size == 1)
new_hash_size = 4;
else
new_hash_size = s->hash_size * 2;
new_hash_table = js_realloc2(ctx, s->hash_table,
sizeof(new_hash_table[0]) * new_hash_size, &slack);
if (!new_hash_table)
return;
new_hash_size += slack / sizeof(*new_hash_table);
for(i = 0; i < new_hash_size; i++)
init_list_head(&new_hash_table[i]);
list_for_each(el, &s->records) {
mr = list_entry(el, JSMapRecord, link);
if (!mr->empty) {
h = map_hash_key(ctx, mr->key) & (new_hash_size - 1);
list_add_tail(&mr->hash_link, &new_hash_table[h]);
}
}
s->hash_table = new_hash_table;
s->hash_size = new_hash_size;
s->record_count_threshold = new_hash_size * 2;
}
static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s,
JSValueConst key)
{
uint32_t h;
JSMapRecord *mr;
mr = js_malloc(ctx, sizeof(*mr));
if (!mr)
return NULL;
mr->ref_count = 1;
mr->map = s;
mr->empty = FALSE;
if (s->is_weak) {
JSObject *p = JS_VALUE_GET_OBJ(key);
/* Add the weak reference */
mr->next_weak_ref = p->first_weak_ref;
p->first_weak_ref = mr;
} else {
JS_DupValue(ctx, key);
}
mr->key = (JSValue)key;
h = map_hash_key(ctx, key) & (s->hash_size - 1);
list_add_tail(&mr->hash_link, &s->hash_table[h]);
list_add_tail(&mr->link, &s->records);
s->record_count++;
if (s->record_count >= s->record_count_threshold) {
map_hash_resize(ctx, s);
}
return mr;
}
/* Remove the weak reference from the object weak
reference list. we don't use a doubly linked list to
save space, assuming a given object has few weak
references to it */
static void delete_weak_ref(JSRuntime *rt, JSMapRecord *mr)
{
JSMapRecord **pmr, *mr1;
JSObject *p;
p = JS_VALUE_GET_OBJ(mr->key);
pmr = &p->first_weak_ref;
for(;;) {
mr1 = *pmr;
assert(mr1 != NULL);
if (mr1 == mr)
break;
pmr = &mr1->next_weak_ref;
}
*pmr = mr1->next_weak_ref;
}
static void map_delete_record(JSRuntime *rt, JSMapState *s, JSMapRecord *mr)
{
if (mr->empty)
return;
list_del(&mr->hash_link);
if (s->is_weak) {
delete_weak_ref(rt, mr);
} else {
JS_FreeValueRT(rt, mr->key);
}
JS_FreeValueRT(rt, mr->value);
if (--mr->ref_count == 0) {
list_del(&mr->link);
js_free_rt(rt, mr);
} else {
/* keep a zombie record for iterators */
mr->empty = TRUE;
mr->key = JS_UNDEFINED;
mr->value = JS_UNDEFINED;
}
s->record_count--;
}
static void map_decref_record(JSRuntime *rt, JSMapRecord *mr)
{
if (--mr->ref_count == 0) {
/* the record can be safely removed */
assert(mr->empty);
list_del(&mr->link);
js_free_rt(rt, mr);
}
}
void reset_weak_ref(JSRuntime *rt, JSObject *p)
{
JSMapRecord *mr, *mr_next;
JSMapState *s;
(void)s;
/* first pass to remove the records from the WeakMap/WeakSet
lists */
for(mr = p->first_weak_ref; mr != NULL; mr = mr->next_weak_ref) {
s = mr->map;
assert(s->is_weak);
assert(!mr->empty); /* no iterator on WeakMap/WeakSet */
list_del(&mr->hash_link);
list_del(&mr->link);
}
/* second pass to free the values to avoid modifying the weak
reference list while traversing it. */
for(mr = p->first_weak_ref; mr != NULL; mr = mr_next) {
mr_next = mr->next_weak_ref;
JS_FreeValueRT(rt, mr->value);
js_free_rt(rt, mr);
}
p->first_weak_ref = NULL; /* fail safe */
}
static JSValue js_map_set(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic)
{
JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
JSMapRecord *mr;
JSValueConst key, value;
if (!s)
return JS_EXCEPTION;
key = map_normalize_key(ctx, argv[0]);
if (s->is_weak && !JS_IsObject(key))
return JS_ThrowTypeErrorNotAnObject(ctx);
if (magic & MAGIC_SET)
value = JS_UNDEFINED;
else
value = argv[1];
mr = map_find_record(ctx, s, key);
if (mr) {
JS_FreeValue(ctx, mr->value);
} else {
mr = map_add_record(ctx, s, key);
if (!mr)
return JS_EXCEPTION;
}
mr->value = JS_DupValue(ctx, value);
return JS_DupValue(ctx, this_val);
}
static JSValue js_map_get(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic)
{
JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
JSMapRecord *mr;
JSValueConst key;
if (!s)
return JS_EXCEPTION;
key = map_normalize_key(ctx, argv[0]);
mr = map_find_record(ctx, s, key);
if (!mr)
return JS_UNDEFINED;
else
return JS_DupValue(ctx, mr->value);
}
static JSValue js_map_has(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic)
{
JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
JSMapRecord *mr;
JSValueConst key;
if (!s)
return JS_EXCEPTION;
key = map_normalize_key(ctx, argv[0]);
mr = map_find_record(ctx, s, key);
return JS_NewBool(ctx, (mr != NULL));
}
static JSValue js_map_delete(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic)
{
JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
JSMapRecord *mr;
JSValueConst key;
if (!s)
return JS_EXCEPTION;
key = map_normalize_key(ctx, argv[0]);
mr = map_find_record(ctx, s, key);
if (!mr)
return JS_FALSE;
map_delete_record(ctx->rt, s, mr);
return JS_TRUE;
}
static JSValue js_map_clear(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic)
{
JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
struct list_head *el, *el1;
JSMapRecord *mr;
if (!s)
return JS_EXCEPTION;
list_for_each_safe(el, el1, &s->records) {
mr = list_entry(el, JSMapRecord, link);
map_delete_record(ctx->rt, s, mr);
}
return JS_UNDEFINED;
}
static JSValue js_map_get_size(JSContext *ctx, JSValueConst this_val, int magic)
{
JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
if (!s)
return JS_EXCEPTION;
return JS_NewUint32(ctx, s->record_count);
}
static JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic)
{
JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
JSValueConst func, this_arg;
JSValue ret, args[3];
struct list_head *el;
JSMapRecord *mr;
if (!s)
return JS_EXCEPTION;
func = argv[0];
if (argc > 1)
this_arg = argv[1];
else
this_arg = JS_UNDEFINED;
if (check_function(ctx, func))
return JS_EXCEPTION;
/* Note: the list can be modified while traversing it, but the
current element is locked */
el = s->records.next;
while (el != &s->records) {
mr = list_entry(el, JSMapRecord, link);
if (!mr->empty) {
mr->ref_count++;
/* must duplicate in case the record is deleted */
args[1] = JS_DupValue(ctx, mr->key);
if (magic)
args[0] = args[1];
else
args[0] = JS_DupValue(ctx, mr->value);
args[2] = (JSValue)this_val;
ret = JS_Call(ctx, func, this_arg, 3, (JSValueConst *)args);
JS_FreeValue(ctx, args[0]);
if (!magic)
JS_FreeValue(ctx, args[1]);
el = el->next;
map_decref_record(ctx->rt, mr);
if (JS_IsException(ret))
return ret;
JS_FreeValue(ctx, ret);
} else {
el = el->next;
}
}
return JS_UNDEFINED;
}
void js_map_finalizer(JSRuntime *rt, JSValue val)
{
JSObject *p;
JSMapState *s;
struct list_head *el, *el1;
JSMapRecord *mr;
p = JS_VALUE_GET_OBJ(val);
s = p->u.map_state;
if (s) {
/* if the object is deleted we are sure that no iterator is
using it */
list_for_each_safe(el, el1, &s->records) {
mr = list_entry(el, JSMapRecord, link);
if (!mr->empty) {
if (s->is_weak)
delete_weak_ref(rt, mr);
else
JS_FreeValueRT(rt, mr->key);
JS_FreeValueRT(rt, mr->value);
}
js_free_rt(rt, mr);
}
js_free_rt(rt, s->hash_table);
js_free_rt(rt, s);
}
}
void js_map_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
{
JSObject *p = JS_VALUE_GET_OBJ(val);
JSMapState *s;
struct list_head *el;
JSMapRecord *mr;
s = p->u.map_state;
if (s) {
list_for_each(el, &s->records) {
mr = list_entry(el, JSMapRecord, link);
if (!s->is_weak)
JS_MarkValue(rt, mr->key, mark_func);
JS_MarkValue(rt, mr->value, mark_func);
}
}
}
void js_map_iterator_finalizer(JSRuntime *rt, JSValue val)
{
JSObject *p;
JSMapIteratorData *it;
p = JS_VALUE_GET_OBJ(val);
it = p->u.map_iterator_data;
if (it) {
/* During the GC sweep phase the Map finalizer may be
called before the Map iterator finalizer */
if (JS_IsLiveObject(rt, it->obj) && it->cur_record) {
map_decref_record(rt, it->cur_record);
}
JS_FreeValueRT(rt, it->obj);
js_free_rt(rt, it);
}
}
void js_map_iterator_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
{
JSObject *p = JS_VALUE_GET_OBJ(val);
JSMapIteratorData *it;
it = p->u.map_iterator_data;
if (it) {
/* the record is already marked by the object */
JS_MarkValue(rt, it->obj, mark_func);
}
}
static JSValue js_create_map_iterator(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic)
{
JSIteratorKindEnum kind;
JSMapState *s;
JSMapIteratorData *it;
JSValue enum_obj;
kind = magic >> 2;
magic &= 3;
s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
if (!s)
return JS_EXCEPTION;
enum_obj = JS_NewObjectClass(ctx, JS_CLASS_MAP_ITERATOR + magic);
if (JS_IsException(enum_obj))
goto fail;
it = js_malloc(ctx, sizeof(*it));
if (!it) {
JS_FreeValue(ctx, enum_obj);
goto fail;
}
it->obj = JS_DupValue(ctx, this_val);
it->kind = kind;
it->cur_record = NULL;
JS_SetOpaque(enum_obj, it);
return enum_obj;
fail:
return JS_EXCEPTION;
}
static JSValue js_map_iterator_next(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv,
BOOL *pdone, int magic)
{
JSMapIteratorData *it;
JSMapState *s;
JSMapRecord *mr;
struct list_head *el;
it = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP_ITERATOR + magic);
if (!it) {
*pdone = FALSE;
return JS_EXCEPTION;
}
if (JS_IsUndefined(it->obj))
goto done;
s = JS_GetOpaque(it->obj, JS_CLASS_MAP + magic);
assert(s != NULL);
if (!it->cur_record) {
el = s->records.next;
} else {
mr = it->cur_record;
el = mr->link.next;
map_decref_record(ctx->rt, mr); /* the record can be freed here */
}
for(;;) {
if (el == &s->records) {
/* no more record */
it->cur_record = NULL;
JS_FreeValue(ctx, it->obj);
it->obj = JS_UNDEFINED;
done:
/* end of enumeration */
*pdone = TRUE;
return JS_UNDEFINED;
}
mr = list_entry(el, JSMapRecord, link);
if (!mr->empty)
break;
/* get the next record */
el = mr->link.next;
}
/* lock the record so that it won't be freed */
mr->ref_count++;
it->cur_record = mr;
*pdone = FALSE;
if (it->kind == JS_ITERATOR_KIND_KEY) {
return JS_DupValue(ctx, mr->key);
} else {
JSValueConst args[2];
args[0] = mr->key;
if (magic)
args[1] = mr->key;
else
args[1] = mr->value;
if (it->kind == JS_ITERATOR_KIND_VALUE) {
return JS_DupValue(ctx, args[1]);
} else {
return js_create_array(ctx, 2, args);
}
}
}
static const JSCFunctionListEntry js_map_funcs[] = {
JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
};
static const JSCFunctionListEntry js_map_proto_funcs[] = {
JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, 0 ),
JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, 0 ),
JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, 0 ),
JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, 0 ),
JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, 0 ),
JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, 0),
JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, 0 ),
JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_VALUE << 2) | 0 ),
JS_CFUNC_MAGIC_DEF("keys", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | 0 ),
JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | 0 ),
JS_ALIAS_DEF("[Symbol.iterator]", "entries" ),
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map", JS_PROP_CONFIGURABLE ),
};
static const JSCFunctionListEntry js_map_iterator_proto_funcs[] = {
JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, 0 ),
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map Iterator", JS_PROP_CONFIGURABLE ),
};
static const JSCFunctionListEntry js_set_proto_funcs[] = {
JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET ),
JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET ),
JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET ),
JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, MAGIC_SET ),
JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, MAGIC_SET ),
JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, MAGIC_SET ),
JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | MAGIC_SET ),
JS_ALIAS_DEF("keys", "values" ),
JS_ALIAS_DEF("[Symbol.iterator]", "values" ),
JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | MAGIC_SET ),
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set", JS_PROP_CONFIGURABLE ),
};
static const JSCFunctionListEntry js_set_iterator_proto_funcs[] = {
JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, MAGIC_SET ),
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set Iterator", JS_PROP_CONFIGURABLE ),
};
static const JSCFunctionListEntry js_weak_map_proto_funcs[] = {
JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, MAGIC_WEAK ),
JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, MAGIC_WEAK ),
JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_WEAK ),
JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_WEAK ),
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakMap", JS_PROP_CONFIGURABLE ),
};
static const JSCFunctionListEntry js_weak_set_proto_funcs[] = {
JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET | MAGIC_WEAK ),
JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET | MAGIC_WEAK ),
JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET | MAGIC_WEAK ),
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakSet", JS_PROP_CONFIGURABLE ),
};
static const JSCFunctionListEntry * const js_map_proto_funcs_ptr[6] = {
js_map_proto_funcs,
js_set_proto_funcs,
js_weak_map_proto_funcs,
js_weak_set_proto_funcs,
js_map_iterator_proto_funcs,
js_set_iterator_proto_funcs,
};
static const uint8_t js_map_proto_funcs_count[6] = {
countof(js_map_proto_funcs),
countof(js_set_proto_funcs),
countof(js_weak_map_proto_funcs),
countof(js_weak_set_proto_funcs),
countof(js_map_iterator_proto_funcs),
countof(js_set_iterator_proto_funcs),
};
void JS_AddIntrinsicMapSet(JSContext *ctx)
{
int i;
JSValue obj1;
char buf[ATOM_GET_STR_BUF_SIZE];
for(i = 0; i < 4; i++) {
const char *name = JS_AtomGetStr(ctx, buf, sizeof(buf),
JS_ATOM_Map + i);
ctx->class_proto[JS_CLASS_MAP + i] = JS_NewObject(ctx);
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP + i],
js_map_proto_funcs_ptr[i],
js_map_proto_funcs_count[i]);
obj1 = JS_NewCFunctionMagic(ctx, js_map_constructor, name, 0,
JS_CFUNC_constructor_magic, i);
if (i < 2) {
JS_SetPropertyFunctionList(ctx, obj1, js_map_funcs,
countof(js_map_funcs));
}
JS_NewGlobalCConstructor2(ctx, obj1, name, ctx->class_proto[JS_CLASS_MAP + i]);
}
for(i = 0; i < 2; i++) {
ctx->class_proto[JS_CLASS_MAP_ITERATOR + i] =
JS_NewObjectProto(ctx, ctx->iterator_proto);
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP_ITERATOR + i],
js_map_proto_funcs_ptr[i + 4],
js_map_proto_funcs_count[i + 4]);
}
}