mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-22 21:32:31 +00:00
Reduce build latency and fix old cpu bugs
This commit is contained in:
parent
df8ab0aa0c
commit
533f3d1ef1
69 changed files with 43069 additions and 43683 deletions
748
third_party/quickjs/map.c
vendored
Normal file
748
third_party/quickjs/map.c
vendored
Normal file
|
@ -0,0 +1,748 @@
|
|||
/*
|
||||
* 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;
|
||||
/* 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]);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue