diff --git a/internal/engine.cm b/internal/engine.cm index f491e0fa..f8971208 100644 --- a/internal/engine.cm +++ b/internal/engine.cm @@ -158,7 +158,8 @@ function disrupt(err) } if (underlings) { - for (var id of underlings) { + var unders = array(underlings) + for (var id of unders) { log.console(`calling on ${id} to disrupt too`) $_.stop(create_actor({id})) } @@ -319,7 +320,7 @@ $_.clock = function(fn) { }) } -var underlings = new Set() // this is more like "all actors that are notified when we die" +var underlings = {} // this is more like "all actors that are notified when we die" var overling = null var root = null @@ -454,7 +455,7 @@ $_.stop = function stop(actor) { } if (!is_actor(actor)) throw Error('Can only call stop on an actor.') - if (!underlings.has(actor[ACTORDATA].id)) + if (is_null(underlings[actor[ACTORDATA].id])) throw Error('Can only call stop on an underling or self.') sys_msg(actor, {kind:"stop"}) @@ -483,10 +484,10 @@ $_.delay = function delay(fn, seconds = 0) { var enet = use_core('enet') // causes this actor to stop when another actor stops. -var couplings = new Set() +var couplings = {} $_.couple = function couple(actor) { if (actor == $_.self) return // can't couple to self - couplings.add(actor[ACTORDATA].id) + couplings[actor[ACTORDATA].id] = true sys_msg(actor, {kind:'couple', from: $_.self}) log.system(`coupled to ${actor}`) } @@ -676,7 +677,7 @@ function handle_actor_disconnect(id) { delete greeters[id] } log.system(`actor ${id} disconnected`) - if (couplings.has(id)) disrupt("coupled actor died") // couplings now disrupts instead of stop + if (!is_null(couplings[id])) disrupt("coupled actor died") // couplings now disrupts instead of stop } function handle_sysym(msg) @@ -691,7 +692,7 @@ function handle_sysym(msg) var greeter = greeters[from[ACTORDATA].id] if (greeter) greeter(msg.message) if (msg.message.type == 'disrupt') - underlings.delete(from[ACTORDATA].id) + delete underlings[from[ACTORDATA].id] break case 'contact': if (portal_fn) { @@ -703,7 +704,7 @@ function handle_sysym(msg) break case 'couple': // from must be notified when we die from = msg.from - underlings.add(from[ACTORDATA].id) + underlings[from[ACTORDATA].id] = true log.system(`actor ${from} is coupled to me`) break } diff --git a/source/cell.c b/source/cell.c index 261f9c1b..d41db028 100644 --- a/source/cell.c +++ b/source/cell.c @@ -128,7 +128,6 @@ void script_startup(cell_rt *prt) JS_AddIntrinsicEval(js); JS_AddIntrinsicRegExp(js); JS_AddIntrinsicJSON(js); - JS_AddIntrinsicMapSet(js); JS_SetContextOpaque(js, prt); prt->context = js; diff --git a/source/quickjs.c b/source/quickjs.c index 5eca973b..4b04405a 100644 --- a/source/quickjs.c +++ b/source/quickjs.c @@ -170,10 +170,6 @@ enum { JS_CLASS_C_FUNCTION_DATA, /* u.c_function_data_record */ JS_CLASS_FOR_IN_ITERATOR, /* u.for_in_iterator */ JS_CLASS_REGEXP, /* u.regexp */ - JS_CLASS_MAP, /* u.map_state */ - JS_CLASS_SET, /* u.map_state */ - JS_CLASS_MAP_ITERATOR, /* u.map_iterator_data */ - JS_CLASS_SET_ITERATOR, /* u.map_iterator_data */ JS_CLASS_ARRAY_ITERATOR, /* u.array_iterator_data */ JS_CLASS_STRING_ITERATOR, /* u.array_iterator_data */ JS_CLASS_REGEXP_STRING_ITERATOR, /* u.regexp_string_iterator_data */ @@ -831,8 +827,6 @@ struct JSObject { struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */ struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */ struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */ - struct JSMapState *map_state; /* JS_CLASS_MAP..JS_CLASS_WEAKSET */ - struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */ struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */ struct JSRegExpStringIteratorData *regexp_string_iterator_data; /* JS_CLASS_REGEXP_STRING_ITERATOR */ struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */ @@ -861,25 +855,6 @@ struct JSObject { } u; }; -typedef struct JSMapRecord { - int ref_count; /* used during enumeration to avoid freeing the record */ - BOOL empty : 8; /* TRUE if the record is deleted */ - struct list_head link; - struct JSMapRecord *hash_next; - JSValue key; - JSValue value; -} JSMapRecord; - -typedef struct JSMapState { - struct list_head records; /* list of JSMapRecord.link */ - uint32_t record_count; - JSMapRecord **hash_table; - int hash_bits; - uint32_t hash_size; /* = 2 ^ hash_bits */ - uint32_t record_count_threshold; /* count at which a hash table - resize is needed */ -} JSMapState; - enum { __JS_ATOM_NULL = JS_ATOM_NULL, #define DEF(name, str) JS_ATOM_ ## name, @@ -983,12 +958,7 @@ static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val); static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); static void js_regexp_finalizer(JSRuntime *rt, JSValue val); -static void js_map_finalizer(JSRuntime *rt, JSValue val); -static void js_map_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val); -static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); + static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val); static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); @@ -1080,8 +1050,6 @@ static void remove_gc_object(JSGCObjectHeader *h); static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); -static JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int is_map); static void JS_RunGCInternal(JSRuntime *rt); static int js_string_find_invalid_codepoint(JSString *p); static JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val, @@ -1286,10 +1254,6 @@ static JSClassShortDef const js_std_class_def[] = { { JS_ATOM_Function, js_c_function_data_finalizer, js_c_function_data_mark }, /* JS_CLASS_C_FUNCTION_DATA */ { JS_ATOM_ForInIterator, js_for_in_iterator_finalizer, js_for_in_iterator_mark }, /* JS_CLASS_FOR_IN_ITERATOR */ { JS_ATOM_RegExp, js_regexp_finalizer, NULL }, /* JS_CLASS_REGEXP */ - { JS_ATOM_Map, js_map_finalizer, js_map_mark }, /* JS_CLASS_MAP */ - { JS_ATOM_Set, js_map_finalizer, js_map_mark }, /* JS_CLASS_SET */ - { JS_ATOM_Map_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_MAP_ITERATOR */ - { JS_ATOM_Set_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_SET_ITERATOR */ { JS_ATOM_Array_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_ARRAY_ITERATOR */ { JS_ATOM_String_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_STRING_ITERATOR */ { JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */ @@ -1939,7 +1903,6 @@ JSContext *JS_NewContext(JSRuntime *rt) JS_AddIntrinsicEval(ctx); JS_AddIntrinsicStringNormalize(ctx); JS_AddIntrinsicRegExp(ctx); - JS_AddIntrinsicMapSet(ctx); return ctx; } @@ -6025,10 +5988,6 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) } } break; - case JS_CLASS_MAP: /* u.map_state */ - case JS_CLASS_SET: /* u.map_state */ - case JS_CLASS_MAP_ITERATOR: /* u.map_iterator_data */ - case JS_CLASS_SET_ITERATOR: /* u.map_iterator_data */ case JS_CLASS_ARRAY_ITERATOR: /* u.array_iterator_data */ case JS_CLASS_STRING_ITERATOR: /* u.array_iterator_data */ /* TODO */ @@ -10619,31 +10578,6 @@ static void js_print_object(JSPrintValueState *s, JSObject *p) } js_printf(s, "]"); comma_state = 2; - } else if (p->class_id == JS_CLASS_MAP || p->class_id == JS_CLASS_SET) { - JSMapState *ms = p->u.opaque; - struct list_head *el; - - if (!ms) - goto default_obj; - js_print_atom(s, rt->class_array[p->class_id].class_name); - js_printf(s, "(%u) { ", ms->record_count); - i = 0; - list_for_each(el, &ms->records) { - JSMapRecord *mr = list_entry(el, JSMapRecord, link); - js_print_comma(s, &comma_state); - if (mr->empty) - continue; - js_print_value(s, mr->key); - if (p->class_id == JS_CLASS_MAP) { - js_printf(s, " => "); - js_print_value(s, mr->value); - } - i++; - if (i >= s->options.max_item_count) - break; - } - if (i < ms->record_count) - js_print_more_items(s, &comma_state, ms->record_count - i); } else if (p->class_id == JS_CLASS_REGEXP && s->ctx && !s->options.raw_dump) { JSValue str = js_regexp_toString(s->ctx, JS_MKPTR(JS_TAG_OBJECT, p), 0, NULL); if (JS_IsException(str)) @@ -29141,7 +29075,6 @@ static const JSCFunctionListEntry js_object_funcs[] = { JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 0 ), JS_CFUNC_DEF("defineProperties", 2, js_object_defineProperties ), JS_CFUNC_DEF("getOwnPropertyNames", 1, js_object_getOwnPropertyNames ), - JS_CFUNC_MAGIC_DEF("groupBy", 2, js_object_groupBy, 0 ), JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 0 ), JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 0 ), JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 0 ), @@ -35482,847 +35415,6 @@ static const JSCFunctionListEntry js_symbol_proto_funcs[] = { JS_CGETSET_DEF("description", js_symbol_get_description, NULL ), }; -/* Set/Map/WeakSet/WeakMap */ - -#define MAGIC_SET (1 << 0) - -static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target, - int argc, JSValueConst *argv, int magic) -{ - - JSMapState *s; - JSValue obj, adder = JS_NULL, iter = JS_NULL, next_method = JS_NULL; - JSValueConst arr; - BOOL is_set; - - is_set = magic & MAGIC_SET; - 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); - JS_SetOpaque(obj, s); - s->hash_bits = 1; - s->hash_size = 1U << s->hash_bits; - s->hash_table = js_mallocz(ctx, sizeof(s->hash_table[0]) * s->hash_size); - if (!s->hash_table) - goto fail; - s->record_count_threshold = 4; - - arr = JS_NULL; - if (argc > 0) - arr = argv[0]; - if (!JS_IsNull(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); - 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) - break; - if (is_set) { - ret = JS_Call(ctx, adder, obj, 1, (JSValueConst *)&item); - if (JS_IsException(ret)) { - JS_FreeValue(ctx, item); - goto fail_close; - } - } else { - JSValue key, value; - JSValueConst args[2]; - key = JS_NULL; - value = JS_NULL; - 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_close; - } - 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_close: - /* close the iterator object, preserving pending exception */ - JS_IteratorClose(ctx, iter, TRUE); - fail: - - 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; -} - -/* hash multipliers, same as the Linux kernel (see Knuth vol 3, - section 6.4, exercise 9) */ -#define HASH_MUL32 0x61C88647 -#define HASH_MUL64 UINT64_C(0x61C8864680B583EB) - -static uint32_t map_hash32(uint32_t a, int hash_bits) -{ - return (a * HASH_MUL32) >> (32 - hash_bits); -} - -static uint32_t map_hash64(uint64_t a, int hash_bits) -{ - return (a * HASH_MUL64) >> (64 - hash_bits); -} - -static uint32_t map_hash_pointer(uintptr_t a, int hash_bits) -{ -#ifdef JS_PTR64 - return map_hash64(a, hash_bits); -#else - return map_hash32(a, hash_bits); -#endif -} - -/* XXX: better hash ? */ -/* precondition: 1 <= hash_bits <= 32 */ -static uint32_t map_hash_key(JSValueConst key, int hash_bits) -{ - uint32_t tag = JS_VALUE_GET_NORM_TAG(key); - uint32_t h; - double d; - - switch(tag) { - case JS_TAG_BOOL: - h = map_hash32(JS_VALUE_GET_INT(key) ^ JS_TAG_BOOL, hash_bits); - break; - case JS_TAG_STRING: - h = map_hash32(hash_string(JS_VALUE_GET_STRING(key), 0) ^ JS_TAG_STRING, hash_bits); - break; - case JS_TAG_STRING_ROPE: - h = map_hash32(hash_string_rope(key, 0) ^ JS_TAG_STRING, hash_bits); - break; - case JS_TAG_OBJECT: - case JS_TAG_SYMBOL: - h = map_hash_pointer((uintptr_t)JS_VALUE_GET_PTR(key) ^ tag, hash_bits); - break; - case JS_TAG_INT: - d = JS_VALUE_GET_INT(key); - 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: - h = map_hash64(float64_as_uint64(d) ^ JS_TAG_FLOAT64, hash_bits); - break; - default: - h = 0; - break; - } - return h; -} - -static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s, - JSValueConst key) -{ - JSMapRecord *mr; - uint32_t h; - h = map_hash_key(key, s->hash_bits); - for(mr = s->hash_table[h]; mr != NULL; mr = mr->hash_next) { - if (mr->empty) { - /* cannot match */ - } else { - 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, h; - int new_hash_bits; - struct list_head *el; - JSMapRecord *mr, **new_hash_table; - - /* XXX: no reporting of memory allocation failure */ - new_hash_bits = min_int(s->hash_bits + 1, 31); - new_hash_size = 1U << new_hash_bits; - new_hash_table = js_realloc(ctx, s->hash_table, - sizeof(new_hash_table[0]) * new_hash_size); - if (!new_hash_table) - return; - - memset(new_hash_table, 0, sizeof(new_hash_table[0]) * new_hash_size); - - list_for_each(el, &s->records) { - mr = list_entry(el, JSMapRecord, link); - if (mr->empty) { - } else { - h = map_hash_key(mr->key, new_hash_bits); - mr->hash_next = new_hash_table[h]; - new_hash_table[h] = mr; - } - } - s->hash_table = new_hash_table; - s->hash_bits = new_hash_bits; - 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->empty = FALSE; - mr->key = JS_DupValue(ctx, key); - h = map_hash_key(key, s->hash_bits); - mr->hash_next = s->hash_table[h]; - s->hash_table[h] = mr; - 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; -} - -/* warning: the record must be removed from the hash table before */ -static void map_delete_record(JSRuntime *rt, JSMapState *s, JSMapRecord *mr) -{ - if (mr->empty) - return; - - 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_NULL; - mr->value = JS_NULL; - } - 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); - } -} - -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 (magic & MAGIC_SET) - value = JS_NULL; - 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_NULL; - 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, **pmr; - JSValueConst key; - uint32_t h; - - if (!s) - return JS_EXCEPTION; - key = map_normalize_key(ctx, argv[0]); - - h = map_hash_key(key, s->hash_bits); - pmr = &s->hash_table[h]; - for(;;) { - mr = *pmr; - if (mr == NULL) - return JS_FALSE; - if (mr->empty) { - /* not valid */ - } else { - if (js_same_value_zero(ctx, mr->key, key)) - break; - } - pmr = &mr->hash_next; - } - - /* remove from the hash table */ - *pmr = mr->hash_next; - - 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; - - /* remove from the hash table */ - memset(s->hash_table, 0, sizeof(s->hash_table[0]) * s->hash_size); - - list_for_each_safe(el, el1, &s->records) { - mr = list_entry(el, JSMapRecord, link); - map_delete_record(ctx->rt, s, mr); - } - return JS_NULL; -} - -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_NULL; - 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_NULL; -} - -static JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int is_map) -{ - JSValueConst cb, args[2]; - JSValue res, iter, next, groups, key, v, prop; - JSAtom key_atom = JS_ATOM_NULL; - int64_t idx; - BOOL done; - - // "is function?" check must be observed before argv[0] is accessed - cb = argv[1]; - if (check_function(ctx, cb)) - return JS_EXCEPTION; - - iter = JS_GetIterator(ctx, argv[0]); - if (JS_IsException(iter)) - return JS_EXCEPTION; - - key = JS_NULL; - key_atom = JS_ATOM_NULL; - v = JS_NULL; - prop = JS_NULL; - groups = JS_NULL; - - next = JS_GetProperty(ctx, iter, JS_ATOM_next); - if (JS_IsException(next)) - goto exception; - - if (is_map) { - groups = js_map_constructor(ctx, JS_NULL, 0, NULL, 0); - } else { - groups = JS_NewObjectProto(ctx, JS_NULL); - } - if (JS_IsException(groups)) - goto exception; - - for (idx = 0; ; idx++) { - if (idx >= MAX_SAFE_INTEGER) { - JS_ThrowTypeError(ctx, "too many elements"); - goto iterator_close_exception; - } - v = JS_IteratorNext(ctx, iter, next, 0, NULL, &done); - if (JS_IsException(v)) - goto exception; - if (done) - break; // v is JS_NULL - - args[0] = v; - args[1] = JS_NewInt64(ctx, idx); - key = JS_Call(ctx, cb, ctx->global_obj, 2, args); - if (JS_IsException(key)) - goto iterator_close_exception; - - if (is_map) { - prop = js_map_get(ctx, groups, 1, (JSValueConst *)&key, 0); - } else { - key_atom = JS_ValueToAtom(ctx, key); - JS_FreeValue(ctx, key); - key = JS_NULL; - if (key_atom == JS_ATOM_NULL) - goto iterator_close_exception; - prop = JS_GetProperty(ctx, groups, key_atom); - } - if (JS_IsException(prop)) - goto exception; - - if (JS_IsNull(prop)) { - prop = JS_NewArray(ctx); - if (JS_IsException(prop)) - goto exception; - if (is_map) { - args[0] = key; - args[1] = prop; - res = js_map_set(ctx, groups, 2, args, 0); - if (JS_IsException(res)) - goto exception; - JS_FreeValue(ctx, res); - } else { - prop = JS_DupValue(ctx, prop); - if (JS_DefinePropertyValue(ctx, groups, key_atom, prop, - JS_PROP_C_W_E) < 0) { - goto exception; - } - } - } - res = js_array_push(ctx, prop, 1, (JSValueConst *)&v, /*unshift*/0); - if (JS_IsException(res)) - goto exception; - // res is an int64 - - JS_FreeValue(ctx, prop); - JS_FreeValue(ctx, key); - JS_FreeAtom(ctx, key_atom); - JS_FreeValue(ctx, v); - prop = JS_NULL; - key = JS_NULL; - key_atom = JS_ATOM_NULL; - v = JS_NULL; - } - - JS_FreeValue(ctx, iter); - JS_FreeValue(ctx, next); - return groups; - - iterator_close_exception: - JS_IteratorClose(ctx, iter, TRUE); - exception: - JS_FreeAtom(ctx, key_atom); - JS_FreeValue(ctx, prop); - JS_FreeValue(ctx, key); - JS_FreeValue(ctx, v); - JS_FreeValue(ctx, groups); - JS_FreeValue(ctx, iter); - JS_FreeValue(ctx, next); - return JS_EXCEPTION; -} - -static 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) { - 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); - } -} - -static 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); - JS_MarkValue(rt, mr->key, mark_func); - JS_MarkValue(rt, mr->value, mark_func); - } - } -} - -/* Map Iterator */ - -typedef struct JSMapIteratorData { - JSValue obj; - JSIteratorKindEnum kind; - JSMapRecord *cur_record; -} JSMapIteratorData; - -static 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); - } -} - -static 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_IsNull(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_NULL; - done: - /* end of enumeration */ - *pdone = TRUE; - return JS_NULL; - } - 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_CFUNC_MAGIC_DEF("groupBy", 2, js_object_groupBy, 1 ), - 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 * const js_map_proto_funcs_ptr[4] = { - js_map_proto_funcs, - js_set_proto_funcs, - js_map_iterator_proto_funcs, - js_set_iterator_proto_funcs, -}; - -static const uint8_t js_map_proto_funcs_count[4] = { - countof(js_map_proto_funcs), - countof(js_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]; - - /* --- Map and Set --- */ - for(i = 0; i < 2; 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); - JS_SetPropertyFunctionList(ctx, obj1, - js_map_funcs, countof(js_map_funcs)); - JS_NewGlobalCConstructor2(ctx, obj1, name, - ctx->class_proto[JS_CLASS_MAP + i]); - } - - /* --- MapIterator and SetIterator --- */ - 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], - /* now offset by +2, not +4 */ - js_map_proto_funcs_ptr[i + 2], - js_map_proto_funcs_count[i + 2]); - } -} - /* global object */ /* ============================================================================ @@ -36336,7 +35428,6 @@ void JS_AddIntrinsicMapSet(JSContext *ctx) * - fn: function utilities * ============================================================================ */ - /* ---------------------------------------------------------------------------- * number function and sub-functions * ---------------------------------------------------------------------------- */ @@ -37769,45 +36860,6 @@ static JSValue js_cell_array(JSContext *ctx, JSValueConst this_val, /* array(object) - keys */ if (JS_IsObject(arg) && !JS_IsArray(ctx, arg)) { - /* Check if it's a Set */ - JSObject *p = JS_VALUE_GET_OBJ(arg); - if (p->class_id == JS_CLASS_SET) { - /* Convert Set to array */ - JSValue iter = JS_GetProperty(ctx, arg, JS_ATOM_Symbol_iterator); - if (JS_IsException(iter)) return iter; - JSValue iter_obj = JS_Call(ctx, iter, arg, 0, NULL); - JS_FreeValue(ctx, iter); - if (JS_IsException(iter_obj)) return iter_obj; - - JSValue result = JS_NewArray(ctx); - if (JS_IsException(result)) { - JS_FreeValue(ctx, iter_obj); - return result; - } - - int64_t idx = 0; - while (1) { - JSValue next = JS_Call(ctx, JS_GetProperty(ctx, iter_obj, JS_ATOM_next), iter_obj, 0, NULL); - if (JS_IsException(next)) { - JS_FreeValue(ctx, iter_obj); - JS_FreeValue(ctx, result); - return JS_EXCEPTION; - } - JSValue done = JS_GetProperty(ctx, next, JS_ATOM_done); - if (JS_ToBool(ctx, done)) { - JS_FreeValue(ctx, done); - JS_FreeValue(ctx, next); - break; - } - JS_FreeValue(ctx, done); - JSValue value = JS_GetProperty(ctx, next, JS_ATOM_value); - JS_FreeValue(ctx, next); - JS_SetPropertyInt64(ctx, result, idx++, value); - } - JS_FreeValue(ctx, iter_obj); - return result; - } - /* Return object keys */ return JS_GetOwnPropertyNames2(ctx, arg, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK,